diff --git a/CMakeLists.txt b/CMakeLists.txt index 54e51dc1a..e4bd8815f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ -project(Slic3r) cmake_minimum_required(VERSION 3.2) +project(Slic3r) include("version.inc") include(GNUInstallDirs) @@ -37,7 +37,7 @@ set(SLIC3R_GTK "2" CACHE STRING "GTK version to use with wxWidgets on Linux") # Proposal for C++ unit tests and sandboxes option(SLIC3R_BUILD_SANDBOXES "Build development sandboxes" OFF) -option(SLIC3R_BUILD_TESTS "Build unit tests" OFF) +option(SLIC3R_BUILD_TESTS "Build unit tests" ON) # Print out the SLIC3R_* cache options get_cmake_property(_cache_vars CACHE_VARIABLES) @@ -173,10 +173,11 @@ if (NOT MSVC AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMP # On GCC and Clang, no return from a non-void function is a warning only. Here, we make it an error. add_compile_options(-Werror=return-type) - #removes LOTS of extraneous Eigen warnings (GCC only supports it since 6.1) - #if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 6.1) - # add_compile_options(-Wno-ignored-attributes) # Tamas: Eigen include dirs are marked as SYSTEM - #endif() + # removes LOTS of extraneous Eigen warnings (GCC only supports it since 6.1) + # https://eigen.tuxfamily.org/bz/show_bug.cgi?id=1221 + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6.0) + add_compile_options(-Wno-ignored-attributes) # Tamas: Eigen include dirs are marked as SYSTEM + endif() #GCC generates loads of -Wunknown-pragmas when compiling igl. The fix is not easy due to a bug in gcc, see # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66943 or @@ -190,6 +191,7 @@ if (NOT MSVC AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMP add_compile_options(-fsanitize=address -fno-omit-frame-pointer) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=address") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -fsanitize=address") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lasan") @@ -254,7 +256,8 @@ if(NOT WIN32) # boost::process was introduced first in version 1.64.0 set(MINIMUM_BOOST_VERSION "1.64.0") endif() -find_package(Boost ${MINIMUM_BOOST_VERSION} REQUIRED COMPONENTS system filesystem thread log locale regex) +set(_boost_components "system;filesystem;thread;log;locale;regex;chrono;atomic;date_time") +find_package(Boost ${MINIMUM_BOOST_VERSION} REQUIRED COMPONENTS ${_boost_components}) # boost compile only in release & debug. We have to force the release version for RELWITHDEBINFO compilation if (MSVC) set_target_properties(${Boost_LIBRARIES} PROPERTIES MAP_IMPORTED_CONFIG_RELWITHDEBINFO RELEASE) @@ -272,37 +275,55 @@ if(NOT SLIC3R_STATIC) target_compile_definitions(boost_headeronly INTERFACE BOOST_LOG_DYN_LINK) endif() +function(slic3r_remap_configs targets from_Cfg to_Cfg) + if(MSVC) + string(TOUPPER ${from_Cfg} from_CFG) + + foreach(tgt ${targets}) + if(TARGET ${tgt}) + set_target_properties(${tgt} PROPERTIES MAP_IMPORTED_CONFIG_${from_CFG} ${to_Cfg}) + endif() + endforeach() + endif() +endfunction() + if(TARGET Boost::system) message(STATUS "Boost::boost exists") target_link_libraries(boost_headeronly INTERFACE Boost::boost) + + # Only from cmake 3.12 + # list(TRANSFORM _boost_components PREPEND Boost:: OUTPUT_VARIABLE _boost_targets) + set(_boost_targets "") + foreach(comp ${_boost_components}) + list(APPEND _boost_targets "Boost::${comp}") + endforeach() + target_link_libraries(boost_libs INTERFACE boost_headeronly # includes the custom compile definitions as well - Boost::system - Boost::filesystem - Boost::thread - Boost::log - Boost::locale - Boost::regex + ${_boost_targets} ) + slic3r_remap_configs("${_boost_targets}" RelWithDebInfo Release) else() target_include_directories(boost_headeronly INTERFACE ${Boost_INCLUDE_DIRS}) target_link_libraries(boost_libs INTERFACE boost_headeronly ${Boost_LIBRARIES}) endif() + + # Find and configure intel-tbb if(SLIC3R_STATIC) set(TBB_STATIC 1) endif() set(TBB_DEBUG 1) find_package(TBB REQUIRED) -include_directories(${TBB_INCLUDE_DIRS}) -add_definitions(${TBB_DEFINITIONS}) -if(MSVC) - # Suppress implicit linking of the TBB libraries by the Visual Studio compiler. - add_definitions(-D__TBB_NO_IMPLICIT_LINKAGE) -endif() +# include_directories(${TBB_INCLUDE_DIRS}) +# add_definitions(${TBB_DEFINITIONS}) +# if(MSVC) +# # Suppress implicit linking of the TBB libraries by the Visual Studio compiler. +# add_definitions(-D__TBB_NO_IMPLICIT_LINKAGE) +# endif() # The Intel TBB library will use the std::exception_ptr feature of C++11. -add_definitions(-DTBB_USE_CAPTURED_EXCEPTION=0) +# add_definitions(-DTBB_USE_CAPTURED_EXCEPTION=0) find_package(CURL REQUIRED) include_directories(${CURL_INCLUDE_DIRS}) @@ -379,6 +400,16 @@ add_custom_target(pot COMMENT "Generate pot file from strings in the source tree" ) +find_package(NLopt 1.4 REQUIRED) + +if(SLIC3R_STATIC) + set(OPENVDB_USE_STATIC_LIBS ON) + set(USE_BLOSC TRUE) +endif() + +#find_package(OpenVDB 5.0 COMPONENTS openvdb) +#slic3r_remap_configs(IlmBase::Half RelWithDebInfo Release) + # libslic3r, Slic3r GUI and the slic3r executable. add_subdirectory(src) set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT slic3r_app_console) diff --git a/cmake/modules/Catch2/Catch.cmake b/cmake/modules/Catch2/Catch.cmake new file mode 100644 index 000000000..0ffe978dc --- /dev/null +++ b/cmake/modules/Catch2/Catch.cmake @@ -0,0 +1,175 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +Catch +----- + +This module defines a function to help use the Catch test framework. + +The :command:`catch_discover_tests` discovers tests by asking the compiled test +executable to enumerate its tests. This does not require CMake to be re-run +when tests change. However, it may not work in a cross-compiling environment, +and setting test properties is less convenient. + +This command is intended to replace use of :command:`add_test` to register +tests, and will create a separate CTest test for each Catch test case. Note +that this is in some cases less efficient, as common set-up and tear-down logic +cannot be shared by multiple test cases executing in the same instance. +However, it provides more fine-grained pass/fail information to CTest, which is +usually considered as more beneficial. By default, the CTest test name is the +same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``. + +.. command:: catch_discover_tests + + Automatically add tests with CTest by querying the compiled test executable + for available tests:: + + catch_discover_tests(target + [TEST_SPEC arg1...] + [EXTRA_ARGS arg1...] + [WORKING_DIRECTORY dir] + [TEST_PREFIX prefix] + [TEST_SUFFIX suffix] + [PROPERTIES name1 value1...] + [TEST_LIST var] + ) + + ``catch_discover_tests`` sets up a post-build command on the test executable + that generates the list of tests by parsing the output from running the test + with the ``--list-test-names-only`` argument. This ensures that the full + list of tests is obtained. Since test discovery occurs at build time, it is + not necessary to re-run CMake when the list of tests changes. + However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` is properly set + in order to function in a cross-compiling environment. + + Additionally, setting properties on tests is somewhat less convenient, since + the tests are not available at CMake time. Additional test properties may be + assigned to the set of tests as a whole using the ``PROPERTIES`` option. If + more fine-grained test control is needed, custom content may be provided + through an external CTest script using the :prop_dir:`TEST_INCLUDE_FILES` + directory property. The set of discovered tests is made accessible to such a + script via the ``_TESTS`` variable. + + The options are: + + ``target`` + Specifies the Catch executable, which must be a known CMake executable + target. CMake will substitute the location of the built executable when + running the test. + + ``TEST_SPEC arg1...`` + Specifies test cases, wildcarded test cases, tags and tag expressions to + pass to the Catch executable with the ``--list-test-names-only`` argument. + + ``EXTRA_ARGS arg1...`` + Any extra arguments to pass on the command line to each test case. + + ``WORKING_DIRECTORY dir`` + Specifies the directory in which to run the discovered test cases. If this + option is not provided, the current binary directory is used. + + ``TEST_PREFIX prefix`` + Specifies a ``prefix`` to be prepended to the name of each discovered test + case. This can be useful when the same test executable is being used in + multiple calls to ``catch_discover_tests()`` but with different + ``TEST_SPEC`` or ``EXTRA_ARGS``. + + ``TEST_SUFFIX suffix`` + Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of + every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may + be specified. + + ``PROPERTIES name1 value1...`` + Specifies additional properties to be set on all tests discovered by this + invocation of ``catch_discover_tests``. + + ``TEST_LIST var`` + Make the list of tests available in the variable ``var``, rather than the + default ``_TESTS``. This can be useful when the same test + executable is being used in multiple calls to ``catch_discover_tests()``. + Note that this variable is only available in CTest. + +#]=======================================================================] + +#------------------------------------------------------------------------------ +function(catch_discover_tests TARGET) + cmake_parse_arguments( + "" + "" + "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST" + "TEST_SPEC;EXTRA_ARGS;PROPERTIES" + ${ARGN} + ) + + if(NOT _WORKING_DIRECTORY) + set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + endif() + if(NOT _TEST_LIST) + set(_TEST_LIST ${TARGET}_TESTS) + endif() + + ## Generate a unique name based on the extra arguments + string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS}") + string(SUBSTRING ${args_hash} 0 7 args_hash) + + # Define rule to generate test list for aforementioned test executable + set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_include-${args_hash}.cmake") + set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_tests-${args_hash}.cmake") + get_property(crosscompiling_emulator + TARGET ${TARGET} + PROPERTY CROSSCOMPILING_EMULATOR + ) + add_custom_command( + TARGET ${TARGET} POST_BUILD + BYPRODUCTS "${ctest_tests_file}" + COMMAND "${CMAKE_COMMAND}" + -D "TEST_TARGET=${TARGET}" + -D "TEST_EXECUTABLE=$" + -D "TEST_EXECUTOR=${crosscompiling_emulator}" + -D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}" + -D "TEST_SPEC=${_TEST_SPEC}" + -D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}" + -D "TEST_PROPERTIES=${_PROPERTIES}" + -D "TEST_PREFIX='${_TEST_PREFIX}'" + -D "TEST_SUFFIX='${_TEST_SUFFIX}'" + -D "TEST_LIST=${_TEST_LIST}" + -D "CTEST_FILE=${ctest_tests_file}" + -P "${_CATCH_DISCOVER_TESTS_SCRIPT}" + VERBATIM + ) + + file(WRITE "${ctest_include_file}" + "if(EXISTS \"${ctest_tests_file}\")\n" + " include(\"${ctest_tests_file}\")\n" + "else()\n" + " add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n" + "endif()\n" + ) + + if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0") + # Add discovered tests to directory TEST_INCLUDE_FILES + set_property(DIRECTORY + APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}" + ) + else() + # Add discovered tests as directory TEST_INCLUDE_FILE if possible + get_property(test_include_file_set DIRECTORY PROPERTY TEST_INCLUDE_FILE SET) + if (NOT ${test_include_file_set}) + set_property(DIRECTORY + PROPERTY TEST_INCLUDE_FILE "${ctest_include_file}" + ) + else() + message(FATAL_ERROR + "Cannot set more than one TEST_INCLUDE_FILE" + ) + endif() + endif() + +endfunction() + +############################################################################### + +set(_CATCH_DISCOVER_TESTS_SCRIPT + ${CMAKE_CURRENT_LIST_DIR}/CatchAddTests.cmake +) diff --git a/cmake/modules/Catch2/CatchAddTests.cmake b/cmake/modules/Catch2/CatchAddTests.cmake new file mode 100644 index 000000000..ca5ebc17e --- /dev/null +++ b/cmake/modules/Catch2/CatchAddTests.cmake @@ -0,0 +1,106 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +set(prefix "${TEST_PREFIX}") +set(suffix "${TEST_SUFFIX}") +set(spec ${TEST_SPEC}) +set(extra_args ${TEST_EXTRA_ARGS}) +set(properties ${TEST_PROPERTIES}) +set(script) +set(suite) +set(tests) + +function(add_command NAME) + set(_args "") + foreach(_arg ${ARGN}) + if(_arg MATCHES "[^-./:a-zA-Z0-9_]") + set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument + else() + set(_args "${_args} ${_arg}") + endif() + endforeach() + set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE) +endfunction() + +macro(_add_catch_test_labels LINE) + # convert to list of tags + string(REPLACE "][" "]\\;[" tags ${line}) + + add_command( + set_tests_properties "${prefix}${test}${suffix}" + PROPERTIES + LABELS "${tags}" + ) +endmacro() + +macro(_add_catch_test LINE) + set(test ${line}) + # use escape commas to handle properly test cases with commans inside the name + string(REPLACE "," "\\," test_name ${test}) + # ...and add to script + add_command( + add_test "${prefix}${test}${suffix}" + ${TEST_EXECUTOR} + "${TEST_EXECUTABLE}" + "${test_name}" + ${extra_args} + ) + + add_command( + set_tests_properties "${prefix}${test}${suffix}" + PROPERTIES + WORKING_DIRECTORY "${TEST_WORKING_DIR}" + ${properties} + ) + list(APPEND tests "${prefix}${test}${suffix}") +endmacro() + +# Run test executable to get list of available tests +if(NOT EXISTS "${TEST_EXECUTABLE}") + message(FATAL_ERROR + "Specified test executable '${TEST_EXECUTABLE}' does not exist" + ) +endif() +execute_process( + COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-tests + OUTPUT_VARIABLE output + RESULT_VARIABLE result +) +# Catch --list-test-names-only reports the number of tests, so 0 is... surprising +if(${result} EQUAL 0) + message(WARNING + "Test executable '${TEST_EXECUTABLE}' contains no tests!\n" + ) +elseif(${result} LESS 0) + message(FATAL_ERROR + "Error running test executable '${TEST_EXECUTABLE}':\n" + " Result: ${result}\n" + " Output: ${output}\n" + ) +endif() + +string(REPLACE "\n" ";" output "${output}") +set(test) +set(tags_regex "(\\[([^\\[]*)\\])+$") + +# Parse output +foreach(line ${output}) + # lines without leading whitespaces are catch output not tests + if(${line} MATCHES "^[ \t]+") + # strip leading spaces and tabs + string(REGEX REPLACE "^[ \t]+" "" line ${line}) + + if(${line} MATCHES "${tags_regex}") + _add_catch_test_labels(${line}) + else() + _add_catch_test(${line}) + endif() + endif() +endforeach() + +# Create a list of all discovered tests, which users may use to e.g. set +# properties on the tests +add_command(set ${TEST_LIST} ${tests}) + +# Write CTest script +file(WRITE "${CTEST_FILE}" "${script}") diff --git a/cmake/modules/Catch2/ParseAndAddCatchTests.cmake b/cmake/modules/Catch2/ParseAndAddCatchTests.cmake new file mode 100644 index 000000000..925d93281 --- /dev/null +++ b/cmake/modules/Catch2/ParseAndAddCatchTests.cmake @@ -0,0 +1,225 @@ +#==================================================================================================# +# supported macros # +# - TEST_CASE, # +# - SCENARIO, # +# - TEST_CASE_METHOD, # +# - CATCH_TEST_CASE, # +# - CATCH_SCENARIO, # +# - CATCH_TEST_CASE_METHOD. # +# # +# Usage # +# 1. make sure this module is in the path or add this otherwise: # +# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/") # +# 2. make sure that you've enabled testing option for the project by the call: # +# enable_testing() # +# 3. add the lines to the script for testing target (sample CMakeLists.txt): # +# project(testing_target) # +# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/") # +# enable_testing() # +# # +# find_path(CATCH_INCLUDE_DIR "catch.hpp") # +# include_directories(${INCLUDE_DIRECTORIES} ${CATCH_INCLUDE_DIR}) # +# # +# file(GLOB SOURCE_FILES "*.cpp") # +# add_executable(${PROJECT_NAME} ${SOURCE_FILES}) # +# # +# include(ParseAndAddCatchTests) # +# ParseAndAddCatchTests(${PROJECT_NAME}) # +# # +# The following variables affect the behavior of the script: # +# # +# PARSE_CATCH_TESTS_VERBOSE (Default OFF) # +# -- enables debug messages # +# PARSE_CATCH_TESTS_NO_HIDDEN_TESTS (Default OFF) # +# -- excludes tests marked with [!hide], [.] or [.foo] tags # +# PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME (Default ON) # +# -- adds fixture class name to the test name # +# PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME (Default ON) # +# -- adds cmake target name to the test name # +# PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS (Default OFF) # +# -- causes CMake to rerun when file with tests changes so that new tests will be discovered # +# # +# One can also set (locally) the optional variable OptionalCatchTestLauncher to precise the way # +# a test should be run. For instance to use test MPI, one can write # +# set(OptionalCatchTestLauncher ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${NUMPROC}) # +# just before calling this ParseAndAddCatchTests function # +# # +# The AdditionalCatchParameters optional variable can be used to pass extra argument to the test # +# command. For example, to include successful tests in the output, one can write # +# set(AdditionalCatchParameters --success) # +# # +# After the script, the ParseAndAddCatchTests_TESTS property for the target, and for each source # +# file in the target is set, and contains the list of the tests extracted from that target, or # +# from that file. This is useful, for example to add further labels or properties to the tests. # +# # +#==================================================================================================# + +if (CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.8) + message(FATAL_ERROR "ParseAndAddCatchTests requires CMake 2.8.8 or newer") +endif() + +option(PARSE_CATCH_TESTS_VERBOSE "Print Catch to CTest parser debug messages" OFF) +option(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS "Exclude tests with [!hide], [.] or [.foo] tags" OFF) +option(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME "Add fixture class name to the test name" ON) +option(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME "Add target name to the test name" ON) +option(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS "Add test file to CMAKE_CONFIGURE_DEPENDS property" OFF) + +function(ParseAndAddCatchTests_PrintDebugMessage) + if(PARSE_CATCH_TESTS_VERBOSE) + message(STATUS "ParseAndAddCatchTests: ${ARGV}") + endif() +endfunction() + +# This removes the contents between +# - block comments (i.e. /* ... */) +# - full line comments (i.e. // ... ) +# contents have been read into '${CppCode}'. +# !keep partial line comments +function(ParseAndAddCatchTests_RemoveComments CppCode) + string(ASCII 2 CMakeBeginBlockComment) + string(ASCII 3 CMakeEndBlockComment) + string(REGEX REPLACE "/\\*" "${CMakeBeginBlockComment}" ${CppCode} "${${CppCode}}") + string(REGEX REPLACE "\\*/" "${CMakeEndBlockComment}" ${CppCode} "${${CppCode}}") + string(REGEX REPLACE "${CMakeBeginBlockComment}[^${CMakeEndBlockComment}]*${CMakeEndBlockComment}" "" ${CppCode} "${${CppCode}}") + string(REGEX REPLACE "\n[ \t]*//+[^\n]+" "\n" ${CppCode} "${${CppCode}}") + + set(${CppCode} "${${CppCode}}" PARENT_SCOPE) +endfunction() + +# Worker function +function(ParseAndAddCatchTests_ParseFile SourceFile TestTarget) + # If SourceFile is an object library, do not scan it (as it is not a file). Exit without giving a warning about a missing file. + if(SourceFile MATCHES "\\\$") + ParseAndAddCatchTests_PrintDebugMessage("Detected OBJECT library: ${SourceFile} this will not be scanned for tests.") + return() + endif() + # According to CMake docs EXISTS behavior is well-defined only for full paths. + get_filename_component(SourceFile ${SourceFile} ABSOLUTE) + if(NOT EXISTS ${SourceFile}) + message(WARNING "Cannot find source file: ${SourceFile}") + return() + endif() + ParseAndAddCatchTests_PrintDebugMessage("parsing ${SourceFile}") + file(STRINGS ${SourceFile} Contents NEWLINE_CONSUME) + + # Remove block and fullline comments + ParseAndAddCatchTests_RemoveComments(Contents) + + # Find definition of test names + string(REGEX MATCHALL "[ \t]*(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^\)]+\\)+[ \t\n]*{+[ \t]*(//[^\n]*[Tt][Ii][Mm][Ee][Oo][Uu][Tt][ \t]*[0-9]+)*" Tests "${Contents}") + + if(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS AND Tests) + ParseAndAddCatchTests_PrintDebugMessage("Adding ${SourceFile} to CMAKE_CONFIGURE_DEPENDS property") + set_property( + DIRECTORY + APPEND + PROPERTY CMAKE_CONFIGURE_DEPENDS ${SourceFile} + ) + endif() + + foreach(TestName ${Tests}) + # Strip newlines + string(REGEX REPLACE "\\\\\n|\n" "" TestName "${TestName}") + + # Get test type and fixture if applicable + string(REGEX MATCH "(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^,^\"]*" TestTypeAndFixture "${TestName}") + string(REGEX MATCH "(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)" TestType "${TestTypeAndFixture}") + string(REGEX REPLACE "${TestType}\\([ \t]*" "" TestFixture "${TestTypeAndFixture}") + + # Get string parts of test definition + string(REGEX MATCHALL "\"+([^\\^\"]|\\\\\")+\"+" TestStrings "${TestName}") + + # Strip wrapping quotation marks + string(REGEX REPLACE "^\"(.*)\"$" "\\1" TestStrings "${TestStrings}") + string(REPLACE "\";\"" ";" TestStrings "${TestStrings}") + + # Validate that a test name and tags have been provided + list(LENGTH TestStrings TestStringsLength) + if(TestStringsLength GREATER 2 OR TestStringsLength LESS 1) + message(FATAL_ERROR "You must provide a valid test name and tags for all tests in ${SourceFile}") + endif() + + # Assign name and tags + list(GET TestStrings 0 Name) + if("${TestType}" STREQUAL "SCENARIO") + set(Name "Scenario: ${Name}") + endif() + if(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME AND TestFixture) + set(CTestName "${TestFixture}:${Name}") + else() + set(CTestName "${Name}") + endif() + if(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME) + set(CTestName "${TestTarget}:${CTestName}") + endif() + # add target to labels to enable running all tests added from this target + set(Labels ${TestTarget}) + if(TestStringsLength EQUAL 2) + list(GET TestStrings 1 Tags) + string(TOLOWER "${Tags}" Tags) + # remove target from labels if the test is hidden + if("${Tags}" MATCHES ".*\\[!?(hide|\\.)\\].*") + list(REMOVE_ITEM Labels ${TestTarget}) + endif() + string(REPLACE "]" ";" Tags "${Tags}") + string(REPLACE "[" "" Tags "${Tags}") + else() + # unset tags variable from previous loop + unset(Tags) + endif() + + list(APPEND Labels ${Tags}) + + set(HiddenTagFound OFF) + foreach(label ${Labels}) + string(REGEX MATCH "^!hide|^\\." result ${label}) + if(result) + set(HiddenTagFound ON) + break() + endif(result) + endforeach(label) + if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound} AND ${CMAKE_VERSION} VERSION_LESS "3.9") + ParseAndAddCatchTests_PrintDebugMessage("Skipping test \"${CTestName}\" as it has [!hide], [.] or [.foo] label") + else() + ParseAndAddCatchTests_PrintDebugMessage("Adding test \"${CTestName}\"") + if(Labels) + ParseAndAddCatchTests_PrintDebugMessage("Setting labels to ${Labels}") + endif() + + # Escape commas in the test spec + string(REPLACE "," "\\," Name ${Name}) + + # Add the test and set its properties + add_test(NAME "\"${CTestName}\"" COMMAND ${OptionalCatchTestLauncher} $ ${Name} ${AdditionalCatchParameters}) + # Old CMake versions do not document VERSION_GREATER_EQUAL, so we use VERSION_GREATER with 3.8 instead + if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound} AND ${CMAKE_VERSION} VERSION_GREATER "3.8") + ParseAndAddCatchTests_PrintDebugMessage("Setting DISABLED test property") + set_tests_properties("\"${CTestName}\"" PROPERTIES DISABLED ON) + else() + set_tests_properties("\"${CTestName}\"" PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran" + LABELS "${Labels}") + endif() + set_property( + TARGET ${TestTarget} + APPEND + PROPERTY ParseAndAddCatchTests_TESTS "\"${CTestName}\"") + set_property( + SOURCE ${SourceFile} + APPEND + PROPERTY ParseAndAddCatchTests_TESTS "\"${CTestName}\"") + endif() + + + endforeach() +endfunction() + +# entry point +function(ParseAndAddCatchTests TestTarget) + ParseAndAddCatchTests_PrintDebugMessage("Started parsing ${TestTarget}") + get_target_property(SourceFiles ${TestTarget} SOURCES) + ParseAndAddCatchTests_PrintDebugMessage("Found the following sources: ${SourceFiles}") + foreach(SourceFile ${SourceFiles}) + ParseAndAddCatchTests_ParseFile(${SourceFile} ${TestTarget}) + endforeach() + ParseAndAddCatchTests_PrintDebugMessage("Finished parsing ${TestTarget}") +endfunction() diff --git a/src/libnest2d/cmake_modules/FindNLopt.cmake b/cmake/modules/FindNLopt.cmake similarity index 92% rename from src/libnest2d/cmake_modules/FindNLopt.cmake rename to cmake/modules/FindNLopt.cmake index 2f813b6aa..912ce8d30 100644 --- a/src/libnest2d/cmake_modules/FindNLopt.cmake +++ b/cmake/modules/FindNLopt.cmake @@ -21,8 +21,7 @@ set(NLopt_FOUND FALSE) set(NLopt_ERROR_REASON "") set(NLopt_DEFINITIONS "") -set(NLopt_LIBS) - +unset(NLopt_LIBS CACHE) set(NLopt_DIR $ENV{NLOPT}) if(NOT NLopt_DIR) @@ -48,15 +47,14 @@ if(NOT NLopt_DIR) set(NLopt_ERROR_REASON "${NLopt_ERROR_REASON} Cannot find NLopt header file '${_NLopt_HEADER_FILE_NAME}'.") endif() unset(_NLopt_HEADER_FILE_NAME) - unset(_NLopt_HEADER_FILE) - + if(NOT NLopt_FOUND) set(NLopt_ERROR_REASON "${NLopt_ERROR_REASON} NLopt not found in system directories (and environment variable NLOPT is not set).") else() get_filename_component(NLopt_INCLUDE_DIR ${_NLopt_HEADER_FILE} DIRECTORY ) endif() - + unset(_NLopt_HEADER_FILE CACHE) else() @@ -95,7 +93,7 @@ else() set(NLopt_ERROR_REASON "${NLopt_ERROR_REASON} Cannot find NLopt header file '${_NLopt_HEADER_FILE_NAME}' in '${NLopt_INCLUDE_DIR}'.") endif() unset(_NLopt_HEADER_FILE_NAME) - unset(_NLopt_HEADER_FILE) + unset(_NLopt_HEADER_FILE CACHE) endif() @@ -114,10 +112,10 @@ if(NLopt_FOUND) message(STATUS "Found NLopt in '${NLopt_DIR}'.") message(STATUS "Using NLopt include directory '${NLopt_INCLUDE_DIR}'.") message(STATUS "Using NLopt library '${NLopt_LIBS}'.") - add_library(Nlopt::Nlopt INTERFACE IMPORTED) - set_target_properties(Nlopt::Nlopt PROPERTIES INTERFACE_LINK_LIBRARIES ${NLopt_LIBS}) - set_target_properties(Nlopt::Nlopt PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${NLopt_INCLUDE_DIR}) - set_target_properties(Nlopt::Nlopt PROPERTIES INTERFACE_COMPILE_DEFINITIONS "${NLopt_DEFINITIONS}") + add_library(NLopt::nlopt INTERFACE IMPORTED) + set_target_properties(NLopt::nlopt PROPERTIES INTERFACE_LINK_LIBRARIES ${NLopt_LIBS}) + set_target_properties(NLopt::nlopt PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${NLopt_INCLUDE_DIR}) + set_target_properties(NLopt::nlopt PROPERTIES INTERFACE_COMPILE_DEFINITIONS "${NLopt_DEFINITIONS}") # target_link_libraries(Nlopt::Nlopt INTERFACE ${NLopt_LIBS}) # target_include_directories(Nlopt::Nlopt INTERFACE ${NLopt_INCLUDE_DIR}) # target_compile_definitions(Nlopt::Nlopt INTERFACE ${NLopt_DEFINITIONS}) diff --git a/cmake/modules/FindOpenVDB.cmake b/cmake/modules/FindOpenVDB.cmake new file mode 100644 index 000000000..9afe8a235 --- /dev/null +++ b/cmake/modules/FindOpenVDB.cmake @@ -0,0 +1,490 @@ +# Copyright (c) DreamWorks Animation LLC +# +# All rights reserved. This software is distributed under the +# Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) +# +# Redistributions of source code must retain the above copyright +# and license notice and the following restrictions and disclaimer. +# +# * 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. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# 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. +# +#[=======================================================================[.rst: + +FindOpenVDB +----------- + +Find OpenVDB include dirs, libraries and settings + +Use this module by invoking find_package with the form:: + + find_package(OpenVDB + [version] [EXACT] # Minimum or EXACT version + [REQUIRED] # Fail with error if OpenVDB is not found + [COMPONENTS ...] # OpenVDB libraries by their canonical name + # e.g. "openvdb" for "libopenvdb" + ) + +IMPORTED Targets +^^^^^^^^^^^^^^^^ + +``OpenVDB::openvdb`` + The core openvdb library target. + +Result Variables +^^^^^^^^^^^^^^^^ + +This will define the following variables: + +``OpenVDB_FOUND`` + True if the system has the OpenVDB library. +``OpenVDB_VERSION`` + The version of the OpenVDB library which was found. +``OpenVDB_INCLUDE_DIRS`` + Include directories needed to use OpenVDB. +``OpenVDB_LIBRARIES`` + Libraries needed to link to OpenVDB. +``OpenVDB_LIBRARY_DIRS`` + OpenVDB library directories. +``OpenVDB_DEFINITIONS`` + Definitions to use when compiling code that uses OpenVDB. +``OpenVDB_{COMPONENT}_FOUND`` + True if the system has the named OpenVDB component. +``OpenVDB_USES_BLOSC`` + True if the OpenVDB Library has been built with blosc support +``OpenVDB_USES_LOG4CPLUS`` + True if the OpenVDB Library has been built with log4cplus support +``OpenVDB_USES_EXR`` + True if the OpenVDB Library has been built with openexr support +``OpenVDB_ABI`` + Set if this module was able to determine the ABI number the located + OpenVDB Library was built against. Unset otherwise. + +Cache Variables +^^^^^^^^^^^^^^^ + +The following cache variables may also be set: + +``OpenVDB_INCLUDE_DIR`` + The directory containing ``openvdb/version.h``. +``OpenVDB_{COMPONENT}_LIBRARY`` + Individual component libraries for OpenVDB + +Hints +^^^^^ + +Instead of explicitly setting the cache variables, the following variables +may be provided to tell this module where to look. + +``OPENVDB_ROOT`` + Preferred installation prefix. +``OPENVDB_INCLUDEDIR`` + Preferred include directory e.g. /include +``OPENVDB_LIBRARYDIR`` + Preferred library directory e.g. /lib +``SYSTEM_LIBRARY_PATHS`` + Paths appended to all include and lib searches. + +#]=======================================================================] + +cmake_minimum_required(VERSION 3.3) +# Monitoring _ROOT variables +if(POLICY CMP0074) + cmake_policy(SET CMP0074 NEW) +endif() + +# Include utility functions for version information +include(${CMAKE_CURRENT_LIST_DIR}/OpenVDBUtils.cmake) + +mark_as_advanced( + OpenVDB_INCLUDE_DIR + OpenVDB_LIBRARY +) + +set(_OPENVDB_COMPONENT_LIST + openvdb +) + +if(OpenVDB_FIND_COMPONENTS) + set(OPENVDB_COMPONENTS_PROVIDED TRUE) + set(_IGNORED_COMPONENTS "") + foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS}) + if(NOT ${COMPONENT} IN_LIST _OPENVDB_COMPONENT_LIST) + list(APPEND _IGNORED_COMPONENTS ${COMPONENT}) + endif() + endforeach() + + if(_IGNORED_COMPONENTS) + message(STATUS "Ignoring unknown components of OpenVDB:") + foreach(COMPONENT ${_IGNORED_COMPONENTS}) + message(STATUS " ${COMPONENT}") + endforeach() + list(REMOVE_ITEM OpenVDB_FIND_COMPONENTS ${_IGNORED_COMPONENTS}) + endif() +else() + set(OPENVDB_COMPONENTS_PROVIDED FALSE) + set(OpenVDB_FIND_COMPONENTS ${_OPENVDB_COMPONENT_LIST}) +endif() + +# Append OPENVDB_ROOT or $ENV{OPENVDB_ROOT} if set (prioritize the direct cmake var) +set(_OPENVDB_ROOT_SEARCH_DIR "") + +# Additionally try and use pkconfig to find OpenVDB + +find_package(PkgConfig) +pkg_check_modules(PC_OpenVDB QUIET OpenVDB) + +# ------------------------------------------------------------------------ +# Search for OpenVDB include DIR +# ------------------------------------------------------------------------ + +set(_OPENVDB_INCLUDE_SEARCH_DIRS "") +list(APPEND _OPENVDB_INCLUDE_SEARCH_DIRS + ${OPENVDB_INCLUDEDIR} + ${_OPENVDB_ROOT_SEARCH_DIR} + ${PC_OpenVDB_INCLUDE_DIRS} + ${SYSTEM_LIBRARY_PATHS} +) + +# Look for a standard OpenVDB header file. +find_path(OpenVDB_INCLUDE_DIR openvdb/version.h + PATHS ${_OPENVDB_INCLUDE_SEARCH_DIRS} + PATH_SUFFIXES include +) + +OPENVDB_VERSION_FROM_HEADER("${OpenVDB_INCLUDE_DIR}/openvdb/version.h" + VERSION OpenVDB_VERSION + MAJOR OpenVDB_MAJOR_VERSION + MINOR OpenVDB_MINOR_VERSION + PATCH OpenVDB_PATCH_VERSION +) + +# ------------------------------------------------------------------------ +# Search for OPENVDB lib DIR +# ------------------------------------------------------------------------ + +set(_OPENVDB_LIBRARYDIR_SEARCH_DIRS "") + +# Append to _OPENVDB_LIBRARYDIR_SEARCH_DIRS in priority order + +list(APPEND _OPENVDB_LIBRARYDIR_SEARCH_DIRS + ${OPENVDB_LIBRARYDIR} + ${_OPENVDB_ROOT_SEARCH_DIR} + ${PC_OpenVDB_LIBRARY_DIRS} + ${SYSTEM_LIBRARY_PATHS} +) + +# Build suffix directories + +set(OPENVDB_PATH_SUFFIXES + lib64 + lib +) + +# Static library setup +if(UNIX AND OPENVDB_USE_STATIC_LIBS) + set(_OPENVDB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) + set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") +endif() + +set(OpenVDB_LIB_COMPONENTS "") + +foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS}) + set(LIB_NAME ${COMPONENT}) + find_library(OpenVDB_${COMPONENT}_LIBRARY ${LIB_NAME} lib${LIB_NAME} + PATHS ${_OPENVDB_LIBRARYDIR_SEARCH_DIRS} + PATH_SUFFIXES ${OPENVDB_PATH_SUFFIXES} + ) + list(APPEND OpenVDB_LIB_COMPONENTS ${OpenVDB_${COMPONENT}_LIBRARY}) + + if(OpenVDB_${COMPONENT}_LIBRARY) + set(OpenVDB_${COMPONENT}_FOUND TRUE) + else() + set(OpenVDB_${COMPONENT}_FOUND FALSE) + endif() +endforeach() + +if(UNIX AND OPENVDB_USE_STATIC_LIBS) + set(CMAKE_FIND_LIBRARY_SUFFIXES ${_OPENVDB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) + unset(_OPENVDB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES) +endif() + +# ------------------------------------------------------------------------ +# Cache and set OPENVDB_FOUND +# ------------------------------------------------------------------------ + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(OpenVDB + FOUND_VAR OpenVDB_FOUND + REQUIRED_VARS + OpenVDB_INCLUDE_DIR + OpenVDB_LIB_COMPONENTS + VERSION_VAR OpenVDB_VERSION + HANDLE_COMPONENTS +) + +# ------------------------------------------------------------------------ +# Determine ABI number +# ------------------------------------------------------------------------ + +# Set the ABI number the library was built against. Uses vdb_print +find_program(OPENVDB_PRINT vdb_print PATHS ${OpenVDB_INCLUDE_DIR} ) + +OPENVDB_ABI_VERSION_FROM_PRINT( + "${OPENVDB_PRINT}" + ABI OpenVDB_ABI +) + +if(NOT OpenVDB_FIND_QUIET) + if(NOT OpenVDB_ABI) + message(WARNING "Unable to determine OpenVDB ABI version from OpenVDB " + "installation. The library major version \"${OpenVDB_MAJOR_VERSION}\" " + "will be inferred. If this is not correct, use " + "add_definitions(-DOPENVDB_ABI_VERSION_NUMBER=N)" + ) + else() + message(STATUS "OpenVDB ABI Version: ${OpenVDB_ABI}") + endif() +endif() + +# ------------------------------------------------------------------------ +# Handle OpenVDB dependencies +# ------------------------------------------------------------------------ + +# Add standard dependencies + +find_package(IlmBase COMPONENTS Half) +if(NOT IlmBase_FOUND) + pkg_check_modules(IlmBase QUIET IlmBase) +endif() +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!") + endif() + + add_library(IlmBase::Half UNKNOWN IMPORTED) + set_target_properties(IlmBase::Half PROPERTIES + IMPORTED_LOCATION "${IlmHalf_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES ${IlmBase_INCLUDE_DIRS}) +elseif(NOT IlmBase_FOUND) + message(FATAL_ERROR "IlmBase::Half can not be found!") +endif() +find_package(TBB REQUIRED COMPONENTS tbb) +find_package(ZLIB REQUIRED) +find_package(Boost 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 +# etc to track deps. We could use a vdb_config binary tools here to improve +# this process + +include(GetPrerequisites) + +set(_EXCLUDE_SYSTEM_PREREQUISITES 1) +set(_RECURSE_PREREQUISITES 0) +set(_OPENVDB_PREREQUISITE_LIST) + +if(NOT OPENVDB_USE_STATIC_LIBS) +get_prerequisites(${OpenVDB_openvdb_LIBRARY} + _OPENVDB_PREREQUISITE_LIST + ${_EXCLUDE_SYSTEM_PREREQUISITES} + ${_RECURSE_PREREQUISITES} + "" + "${SYSTEM_LIBRARY_PATHS}" +) +endif() + +unset(_EXCLUDE_SYSTEM_PREREQUISITES) +unset(_RECURSE_PREREQUISITES) + +# As the way we resolve optional libraries relies on library file names, use +# the configuration options from the main CMakeLists.txt to allow users +# to manually identify the requirements of OpenVDB builds if they know them. + +set(OpenVDB_USES_BLOSC ${USE_BLOSC}) +set(OpenVDB_USES_LOG4CPLUS ${USE_LOG4CPLUS}) +set(OpenVDB_USES_ILM ${USE_EXR}) +set(OpenVDB_USES_EXR ${USE_EXR}) + +# Search for optional dependencies + +foreach(PREREQUISITE ${_OPENVDB_PREREQUISITE_LIST}) + set(_HAS_DEP) + get_filename_component(PREREQUISITE ${PREREQUISITE} NAME) + + string(FIND ${PREREQUISITE} "blosc" _HAS_DEP) + if(NOT ${_HAS_DEP} EQUAL -1) + set(OpenVDB_USES_BLOSC ON) + endif() + + string(FIND ${PREREQUISITE} "log4cplus" _HAS_DEP) + if(NOT ${_HAS_DEP} EQUAL -1) + set(OpenVDB_USES_LOG4CPLUS ON) + endif() + + string(FIND ${PREREQUISITE} "IlmImf" _HAS_DEP) + if(NOT ${_HAS_DEP} EQUAL -1) + set(OpenVDB_USES_ILM ON) + endif() +endforeach() + +unset(_OPENVDB_PREREQUISITE_LIST) +unset(_HAS_DEP) + +if(OpenVDB_USES_BLOSC) + find_package(Blosc ) + 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) + find_library(Blosc_LIBRARY NAMES blosc) + if (Blosc_INCLUDE_DIR AND Blosc_LIBRARY) + set(Blosc_FOUND TRUE) + add_library(Blosc::blosc UNKNOWN IMPORTED) + set_target_properties(Blosc::blosc PROPERTIES + IMPORTED_LOCATION "${Blosc_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES ${Blosc_INCLUDE_DIR}) + elseif() + message(FATAL_ERROR "Blosc library can not be found!") + endif() + endif() +endif() + +if(OpenVDB_USES_LOG4CPLUS) + find_package(Log4cplus REQUIRED) +endif() + +if(OpenVDB_USES_ILM) + find_package(IlmBase REQUIRED) +endif() + +if(OpenVDB_USES_EXR) + find_package(OpenEXR REQUIRED) +endif() + +if(UNIX) + find_package(Threads REQUIRED) +endif() + +# Set deps. Note that the order here is important. If we're building against +# Houdini 17.5 we must include OpenEXR and IlmBase deps first to ensure the +# users chosen namespaced headers are correctly prioritized. Otherwise other +# include paths from shared installs (including houdini) may pull in the wrong +# headers + +set(_OPENVDB_VISIBLE_DEPENDENCIES + Boost::iostreams + Boost::system + IlmBase::Half +) + +set(_OPENVDB_DEFINITIONS) +if(OpenVDB_ABI) + list(APPEND _OPENVDB_DEFINITIONS "-DOPENVDB_ABI_VERSION_NUMBER=${OpenVDB_ABI}") +endif() + +if(OpenVDB_USES_EXR) + list(APPEND _OPENVDB_VISIBLE_DEPENDENCIES + IlmBase::IlmThread + IlmBase::Iex + IlmBase::Imath + OpenEXR::IlmImf + ) + list(APPEND _OPENVDB_DEFINITIONS "-DOPENVDB_TOOLS_RAYTRACER_USE_EXR") +endif() + +if(OpenVDB_USES_LOG4CPLUS) + list(APPEND _OPENVDB_VISIBLE_DEPENDENCIES Log4cplus::log4cplus) + list(APPEND _OPENVDB_DEFINITIONS "-DOPENVDB_USE_LOG4CPLUS") +endif() + +list(APPEND _OPENVDB_VISIBLE_DEPENDENCIES + TBB::tbb +) +if(UNIX) + list(APPEND _OPENVDB_VISIBLE_DEPENDENCIES + Threads::Threads + ) +endif() + +set(_OPENVDB_HIDDEN_DEPENDENCIES) + +if(OpenVDB_USES_BLOSC) + if(OPENVDB_USE_STATIC_LIBS) + list(APPEND _OPENVDB_VISIBLE_DEPENDENCIES $) + else() + list(APPEND _OPENVDB_HIDDEN_DEPENDENCIES Blosc::blosc) + endif() +endif() + +if(OPENVDB_USE_STATIC_LIBS) + list(APPEND _OPENVDB_VISIBLE_DEPENDENCIES $) +else() + list(APPEND _OPENVDB_HIDDEN_DEPENDENCIES ZLIB::ZLIB) +endif() + +# ------------------------------------------------------------------------ +# Configure imported target +# ------------------------------------------------------------------------ + +set(OpenVDB_LIBRARIES + ${OpenVDB_LIB_COMPONENTS} +) +set(OpenVDB_INCLUDE_DIRS ${OpenVDB_INCLUDE_DIR}) + +set(OpenVDB_DEFINITIONS) +list(APPEND OpenVDB_DEFINITIONS "${PC_OpenVDB_CFLAGS_OTHER}") +list(APPEND OpenVDB_DEFINITIONS "${_OPENVDB_DEFINITIONS}") +list(REMOVE_DUPLICATES OpenVDB_DEFINITIONS) + +set(OpenVDB_LIBRARY_DIRS "") +foreach(LIB ${OpenVDB_LIB_COMPONENTS}) + get_filename_component(_OPENVDB_LIBDIR ${LIB} DIRECTORY) + list(APPEND OpenVDB_LIBRARY_DIRS ${_OPENVDB_LIBDIR}) +endforeach() +list(REMOVE_DUPLICATES OpenVDB_LIBRARY_DIRS) + +foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS}) + if(NOT TARGET OpenVDB::${COMPONENT}) + add_library(OpenVDB::${COMPONENT} UNKNOWN IMPORTED) + set_target_properties(OpenVDB::${COMPONENT} PROPERTIES + IMPORTED_LOCATION "${OpenVDB_${COMPONENT}_LIBRARY}" + 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_COMPILE_FEATURES cxx_std_11 + ) + + if (OPENVDB_USE_STATIC_LIBS) + set_target_properties(OpenVDB::${COMPONENT} PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "OPENVDB_STATICLIB;OPENVDB_OPENEXR_STATICLIB" + ) + endif() + endif() +endforeach() + +if(OpenVDB_FOUND AND NOT ${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY) + message(STATUS "OpenVDB libraries: ${OpenVDB_LIBRARIES}") +endif() + +unset(_OPENVDB_DEFINITIONS) +unset(_OPENVDB_VISIBLE_DEPENDENCIES) +unset(_OPENVDB_HIDDEN_DEPENDENCIES) diff --git a/cmake/modules/FindTBB.cmake b/cmake/modules/FindTBB.cmake index e5115ab44..c6bdec985 100644 --- a/cmake/modules/FindTBB.cmake +++ b/cmake/modules/FindTBB.cmake @@ -93,8 +93,16 @@ # This module will also create the "tbb" target that may be used when building # executables and libraries. +unset(TBB_FOUND CACHE) +unset(TBB_INCLUDE_DIRS CACHE) +unset(TBB_LIBRARIES) +unset(TBB_LIBRARIES_DEBUG) +unset(TBB_LIBRARIES_RELEASE) + include(FindPackageHandleStandardArgs) +find_package(Threads QUIET REQUIRED) + if(NOT TBB_FOUND) ################################## @@ -215,6 +223,9 @@ if(NOT TBB_FOUND) foreach(_comp ${TBB_SEARCH_COMPOMPONENTS}) if(";${TBB_FIND_COMPONENTS};tbb;" MATCHES ";${_comp};") + unset(TBB_${_comp}_LIBRARY_DEBUG CACHE) + unset(TBB_${_comp}_LIBRARY_RELEASE CACHE) + # Search for the libraries find_library(TBB_${_comp}_LIBRARY_RELEASE ${_comp}${TBB_STATIC_SUFFIX} HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} @@ -250,28 +261,31 @@ if(NOT TBB_FOUND) endif() endforeach() - unset(TBB_STATIC_SUFFIX) - ################################## # Set compile flags and libraries ################################## set(TBB_DEFINITIONS_RELEASE "") - set(TBB_DEFINITIONS_DEBUG "-DTBB_USE_DEBUG=1") + set(TBB_DEFINITIONS_DEBUG "TBB_USE_DEBUG=1") if(TBB_LIBRARIES_${TBB_BUILD_TYPE}) - set(TBB_DEFINITIONS "${TBB_DEFINITIONS_${TBB_BUILD_TYPE}}") set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}") - elseif(TBB_LIBRARIES_RELEASE) - set(TBB_DEFINITIONS "${TBB_DEFINITIONS_RELEASE}") - set(TBB_LIBRARIES "${TBB_LIBRARIES_RELEASE}") - elseif(TBB_LIBRARIES_DEBUG) - set(TBB_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}") - set(TBB_LIBRARIES "${TBB_LIBRARIES_DEBUG}") 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) @@ -280,25 +294,20 @@ if(NOT TBB_FOUND) ################################## if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND) - add_library(tbb UNKNOWN IMPORTED) - set_target_properties(tbb PROPERTIES + 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 PROPERTIES - INTERFACE_COMPILE_DEFINITIONS "$<$,$>:TBB_USE_DEBUG=1>" + 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} ) - elseif(TBB_LIBRARIES_RELEASE) - set_target_properties(tbb PROPERTIES IMPORTED_LOCATION ${TBB_LIBRARIES_RELEASE}) - else() - set_target_properties(tbb PROPERTIES - INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}" - IMPORTED_LOCATION ${TBB_LIBRARIES_DEBUG} - ) endif() endif() diff --git a/cmake/modules/OpenVDBUtils.cmake b/cmake/modules/OpenVDBUtils.cmake new file mode 100644 index 000000000..bb3ce6e65 --- /dev/null +++ b/cmake/modules/OpenVDBUtils.cmake @@ -0,0 +1,166 @@ +# Copyright (c) DreamWorks Animation LLC +# +# All rights reserved. This software is distributed under the +# Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) +# +# Redistributions of source code must retain the above copyright +# and license notice and the following restrictions and disclaimer. +# +# * 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. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# 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. +# +#[=======================================================================[.rst: + +OpenVDBUtils.cmake +------------------ + +A utility CMake file which provides helper functions for configuring an +OpenVDB installation. + +Use this module by invoking include with the form:: + + include ( OpenVDBUtils ) + + +The following functions are provided: + +``OPENVDB_VERSION_FROM_HEADER`` + + OPENVDB_VERSION_FROM_HEADER ( + VERSION [] + MAJOR [] + MINOR [] + PATCH [] ) + + Parse the provided version file to retrieve the current OpenVDB + version information. The file is expected to be a version.h file + as found in the following path of an OpenVDB repository: + openvdb/version.h + + If the file does not exist, variables are unmodified. + +``OPENVDB_ABI_VERSION_FROM_PRINT`` + + OPENVDB_ABI_VERSION_FROM_PRINT ( + [QUIET] + ABI [] ) + + Retrieve the ABI version that an installation of OpenVDB was compiled + for using the provided vdb_print binary. Parses the result of: + vdb_print --version + + If the binary does not exist or fails to launch, variables are + unmodified. + +#]=======================================================================] + + +function(OPENVDB_VERSION_FROM_HEADER OPENVDB_VERSION_FILE) + cmake_parse_arguments(_VDB "" "VERSION;MAJOR;MINOR;PATCH" "" ${ARGN}) + + if(NOT EXISTS ${OPENVDB_VERSION_FILE}) + return() + endif() + + file(STRINGS "${OPENVDB_VERSION_FILE}" openvdb_version_str + REGEX "^#define[\t ]+OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER[\t ]+.*" + ) + string(REGEX REPLACE "^.*OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER[\t ]+([0-9]*).*$" "\\1" + _OpenVDB_MAJOR_VERSION "${openvdb_version_str}" + ) + + file(STRINGS "${OPENVDB_VERSION_FILE}" openvdb_version_str + REGEX "^#define[\t ]+OPENVDB_LIBRARY_MINOR_VERSION_NUMBER[\t ]+.*" + ) + string(REGEX REPLACE "^.*OPENVDB_LIBRARY_MINOR_VERSION_NUMBER[\t ]+([0-9]*).*$" "\\1" + _OpenVDB_MINOR_VERSION "${openvdb_version_str}" + ) + + file(STRINGS "${OPENVDB_VERSION_FILE}" openvdb_version_str + REGEX "^#define[\t ]+OPENVDB_LIBRARY_PATCH_VERSION_NUMBER[\t ]+.*" + ) + string(REGEX REPLACE "^.*OPENVDB_LIBRARY_PATCH_VERSION_NUMBER[\t ]+([0-9]*).*$" "\\1" + _OpenVDB_PATCH_VERSION "${openvdb_version_str}" + ) + unset(openvdb_version_str) + + if(_VDB_VERSION) + set(${_VDB_VERSION} + ${_OpenVDB_MAJOR_VERSION}.${_OpenVDB_MINOR_VERSION}.${_OpenVDB_PATCH_VERSION} + PARENT_SCOPE + ) + endif() + if(_VDB_MAJOR) + set(${_VDB_MAJOR} ${_OpenVDB_MAJOR_VERSION} PARENT_SCOPE) + endif() + if(_VDB_MINOR) + set(${_VDB_MINOR} ${_OpenVDB_MINOR_VERSION} PARENT_SCOPE) + endif() + if(_VDB_PATCH) + set(${_VDB_PATCH} ${_OpenVDB_PATCH_VERSION} PARENT_SCOPE) + endif() +endfunction() + + +######################################################################## +######################################################################## + + +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}") + return() + endif() + + set(_VDB_PRINT_VERSION_STRING "") + set(_VDB_PRINT_RETURN_STATUS "") + + if(${_VDB_QUIET}) + execute_process(COMMAND ${OPENVDB_PRINT} "--version" + RESULT_VARIABLE _VDB_PRINT_RETURN_STATUS + OUTPUT_VARIABLE _VDB_PRINT_VERSION_STRING + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + else() + execute_process(COMMAND ${OPENVDB_PRINT} "--version" + RESULT_VARIABLE _VDB_PRINT_RETURN_STATUS + OUTPUT_VARIABLE _VDB_PRINT_VERSION_STRING + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + endif() + + if(${_VDB_PRINT_RETURN_STATUS}) + message(WARNING "vdb_print returned with status ${_VDB_PRINT_RETURN_STATUS}") + return() + endif() + + set(_OpenVDB_ABI) + string(REGEX REPLACE ".*abi([0-9]*).*" "\\1" _OpenVDB_ABI ${_VDB_PRINT_VERSION_STRING}) + if(${_OpenVDB_ABI} STREQUAL ${_VDB_PRINT_VERSION_STRING}) + set(_OpenVDB_ABI "") + endif() + unset(_VDB_PRINT_RETURN_STATUS) + unset(_VDB_PRINT_VERSION_STRING) + + if(_VDB_ABI) + set(${_VDB_ABI} ${_OpenVDB_ABI} PARENT_SCOPE) + endif() +endfunction() diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 9da42446d..948310a11 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -71,7 +71,7 @@ elseif (APPLE) message(FATAL_ERROR "Could not determine OS X SDK version. Please use -DCMAKE_OSX_DEPLOYMENT_TARGET=") endif () - message("OS X Deployment Target (inferred from default): ${DEP_OSX_TARGET}") + message("OS X Deployment Target (inferred from SDK): ${DEP_OSX_TARGET}") endif () include("deps-macos.cmake") @@ -95,6 +95,7 @@ if (MSVC) dep_nlopt # dep_qhull # Experimental dep_zlib # on Windows we still need zlib + dep_openvdb ) else() @@ -109,6 +110,7 @@ else() dep_cereal dep_nlopt dep_qhull + dep_openvdb # dep_libigl # Not working, static build has different Eigen ) diff --git a/deps/blosc-mods.patch b/deps/blosc-mods.patch new file mode 100644 index 000000000..9b1b9cb27 --- /dev/null +++ b/deps/blosc-mods.patch @@ -0,0 +1,469 @@ +From 7cf6c014a36f1712efbdbe9bc52d2d4922b54673 Mon Sep 17 00:00:00 2001 +From: tamasmeszaros +Date: Wed, 30 Oct 2019 12:54:52 +0100 +Subject: [PATCH] Blosc 1.17 fixes and cmake config script + +Signed-off-by: tamasmeszaros +--- + CMakeLists.txt | 105 +++++++++++++++++----------------- + blosc/CMakeLists.txt | 118 +++++++++------------------------------ + cmake/FindLZ4.cmake | 6 +- + cmake/FindSnappy.cmake | 8 ++- + cmake/FindZstd.cmake | 8 ++- + cmake_config.cmake.in | 24 ++++++++ + internal-complibs/CMakeLists.txt | 35 ++++++++++++ + 7 files changed, 157 insertions(+), 147 deletions(-) + create mode 100644 cmake_config.cmake.in + create mode 100644 internal-complibs/CMakeLists.txt + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 59d9fab..e9134c2 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -71,7 +71,7 @@ + # DEV: static includes blosc.a and blosc.h + + +-cmake_minimum_required(VERSION 2.8.12) ++cmake_minimum_required(VERSION 3.1) # Threads::Threads target available from 3.1 + if (NOT CMAKE_VERSION VERSION_LESS 3.3) + cmake_policy(SET CMP0063 NEW) + endif() +@@ -124,55 +124,30 @@ option(PREFER_EXTERNAL_ZSTD + + set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") + +- +-if(NOT DEACTIVATE_LZ4) +- if(PREFER_EXTERNAL_LZ4) +- find_package(LZ4) +- else() +- message(STATUS "Using LZ4 internal sources.") +- endif(PREFER_EXTERNAL_LZ4) +- # HAVE_LZ4 will be set to true because even if the library is +- # not found, we will use the included sources for it +- set(HAVE_LZ4 TRUE) +-endif(NOT DEACTIVATE_LZ4) +- +-if(NOT DEACTIVATE_SNAPPY) +- if(PREFER_EXTERNAL_SNAPPY) +- find_package(Snappy) +- else() +- message(STATUS "Using Snappy internal sources.") +- endif(PREFER_EXTERNAL_SNAPPY) +- # HAVE_SNAPPY will be set to true because even if the library is not found, +- # we will use the included sources for it +- set(HAVE_SNAPPY TRUE) +-endif(NOT DEACTIVATE_SNAPPY) +- +-if(NOT DEACTIVATE_ZLIB) +- # import the ZLIB_ROOT environment variable to help finding the zlib library +- if(PREFER_EXTERNAL_ZLIB) +- set(ZLIB_ROOT $ENV{ZLIB_ROOT}) +- find_package(ZLIB) +- if (NOT ZLIB_FOUND ) +- message(STATUS "No zlib found. Using internal sources.") +- endif (NOT ZLIB_FOUND ) +- else() +- message(STATUS "Using zlib internal sources.") +- endif(PREFER_EXTERNAL_ZLIB) +- # HAVE_ZLIB will be set to true because even if the library is not found, +- # we will use the included sources for it +- set(HAVE_ZLIB TRUE) +-endif(NOT DEACTIVATE_ZLIB) +- +-if (NOT DEACTIVATE_ZSTD) +- if (PREFER_EXTERNAL_ZSTD) +- find_package(Zstd) +- else () +- message(STATUS "Using ZSTD internal sources.") +- endif (PREFER_EXTERNAL_ZSTD) +- # HAVE_ZSTD will be set to true because even if the library is +- # not found, we will use the included sources for it +- set(HAVE_ZSTD TRUE) +-endif (NOT DEACTIVATE_ZSTD) ++set(LIBS "") ++macro(use_package _pkg _tgt) ++ string(TOUPPER ${_pkg} _PKG) ++ if(NOT DEACTIVATE_${_PKG}) ++ if(PREFER_EXTERNAL_${_PKG}) ++ find_package(${_pkg}) ++ if (NOT ${_pkg}_FOUND ) ++ message(STATUS "No ${_pkg} found. Using internal sources.") ++ endif() ++ else() ++ message(STATUS "Using ${_pkg} internal sources.") ++ endif(PREFER_EXTERNAL_${_PKG}) ++ # HAVE_${_pkg} will be set to true because even if the library is ++ # not found, we will use the included sources for it ++ set(HAVE_${_PKG} TRUE) ++ list(APPEND LIBS ${_pkg}::${_tgt}) ++ endif(NOT DEACTIVATE_${_PKG}) ++endmacro() ++ ++set(ZLIB_ROOT $ENV{ZLIB_ROOT}) ++use_package(ZLIB ZLIB) ++use_package(LZ4 LZ4) ++use_package(Snappy snappy) ++use_package(Zstd Zstd) + + # create the config.h file + configure_file ("blosc/config.h.in" "blosc/config.h" ) +@@ -316,6 +291,7 @@ endif() + + + # subdirectories ++add_subdirectory(internal-complibs) + add_subdirectory(blosc) + + if(BUILD_TESTS) +@@ -328,7 +304,6 @@ if(BUILD_BENCHMARKS) + add_subdirectory(bench) + endif(BUILD_BENCHMARKS) + +- + # uninstall target + if (BLOSC_INSTALL) + configure_file( +@@ -338,10 +313,38 @@ if (BLOSC_INSTALL) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/blosc.pc" + DESTINATION lib/pkgconfig COMPONENT DEV) + ++ configure_file( ++ "${CMAKE_CURRENT_SOURCE_DIR}/cmake_config.cmake.in" ++ "${CMAKE_CURRENT_BINARY_DIR}/cmakeexports/BloscConfig.cmake" ++ @ONLY) ++ + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) ++ ++ include(CMakePackageConfigHelpers) ++ write_basic_package_version_file( ++ "${CMAKE_CURRENT_BINARY_DIR}/cmakeexports/BloscConfigVersion.cmake" ++ VERSION ${BLOSC_VERSION_MAJOR}.${BLOSC_VERSION_MINOR}.${BLOSC_VERSION_PATCH} ++ COMPATIBILITY AnyNewerVersion ++ ) ++ ++ export(EXPORT BloscTargets ++ FILE "${CMAKE_CURRENT_BINARY_DIR}/cmakeexports/BloscTargets.cmake" ++ NAMESPACE Blosc::) ++ ++ install(EXPORT BloscTargets ++ FILE BloscTargets.cmake ++ NAMESPACE Blosc:: ++ DESTINATION lib/cmake/Blosc ++ EXPORT_LINK_INTERFACE_LIBRARIES) ++ ++ install(FILES ++ "${CMAKE_CURRENT_BINARY_DIR}/cmakeexports/BloscConfig.cmake" ++ "${CMAKE_CURRENT_BINARY_DIR}/cmakeexports/BloscConfigVersion.cmake" ++ DESTINATION lib/cmake/Blosc COMPONENT DEV) ++ + add_custom_target(uninstall + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) + endif() +diff --git a/blosc/CMakeLists.txt b/blosc/CMakeLists.txt +index 1d1bebe..f554abe 100644 +--- a/blosc/CMakeLists.txt ++++ b/blosc/CMakeLists.txt +@@ -1,52 +1,11 @@ + # a simple way to detect that we are using CMAKE + add_definitions(-DUSING_CMAKE) + +-set(INTERNAL_LIBS ${PROJECT_SOURCE_DIR}/internal-complibs) +- + # Hide symbols by default unless they're specifically exported. + # This makes it easier to keep the set of exported symbols the + # same across all compilers/platforms. + set(CMAKE_C_VISIBILITY_PRESET hidden) + +-# includes +-set(BLOSC_INCLUDE_DIRS ${BLOSC_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}) +-if(NOT DEACTIVATE_LZ4) +- if (LZ4_FOUND) +- set(BLOSC_INCLUDE_DIRS ${BLOSC_INCLUDE_DIRS} ${LZ4_INCLUDE_DIR}) +- else(LZ4_FOUND) +- set(LZ4_LOCAL_DIR ${INTERNAL_LIBS}/lz4-1.9.1) +- set(BLOSC_INCLUDE_DIRS ${BLOSC_INCLUDE_DIRS} ${LZ4_LOCAL_DIR}) +- endif(LZ4_FOUND) +-endif(NOT DEACTIVATE_LZ4) +- +-if(NOT DEACTIVATE_SNAPPY) +- if (SNAPPY_FOUND) +- set(BLOSC_INCLUDE_DIRS ${BLOSC_INCLUDE_DIRS} ${SNAPPY_INCLUDE_DIR}) +- else(SNAPPY_FOUND) +- set(SNAPPY_LOCAL_DIR ${INTERNAL_LIBS}/snappy-1.1.1) +- set(BLOSC_INCLUDE_DIRS ${BLOSC_INCLUDE_DIRS} ${SNAPPY_LOCAL_DIR}) +- endif(SNAPPY_FOUND) +-endif(NOT DEACTIVATE_SNAPPY) +- +-if(NOT DEACTIVATE_ZLIB) +- if (ZLIB_FOUND) +- set(BLOSC_INCLUDE_DIRS ${BLOSC_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIR}) +- else(ZLIB_FOUND) +- set(ZLIB_LOCAL_DIR ${INTERNAL_LIBS}/zlib-1.2.8) +- set(BLOSC_INCLUDE_DIRS ${BLOSC_INCLUDE_DIRS} ${ZLIB_LOCAL_DIR}) +- endif(ZLIB_FOUND) +-endif(NOT DEACTIVATE_ZLIB) +- +-if (NOT DEACTIVATE_ZSTD) +- if (ZSTD_FOUND) +- set(BLOSC_INCLUDE_DIRS ${BLOSC_INCLUDE_DIRS} ${ZSTD_INCLUDE_DIR}) +- else (ZSTD_FOUND) +- set(ZSTD_LOCAL_DIR ${INTERNAL_LIBS}/zstd-1.4.1) +- set(BLOSC_INCLUDE_DIRS ${BLOSC_INCLUDE_DIRS} ${ZSTD_LOCAL_DIR} ${ZSTD_LOCAL_DIR}/common) +- endif (ZSTD_FOUND) +-endif (NOT DEACTIVATE_ZSTD) +- +-include_directories(${BLOSC_INCLUDE_DIRS}) + + # library sources + set(SOURCES blosc.c blosclz.c fastcopy.c shuffle-generic.c bitshuffle-generic.c +@@ -73,53 +32,13 @@ if(WIN32) + message(STATUS "using the internal pthread library for win32 systems.") + set(SOURCES ${SOURCES} win32/pthread.c) + else(NOT Threads_FOUND) +- set(LIBS ${LIBS} ${CMAKE_THREAD_LIBS_INIT}) ++ list(APPEND LIBS Threads::Threads) + endif(NOT Threads_FOUND) + else(WIN32) + find_package(Threads REQUIRED) +- set(LIBS ${LIBS} ${CMAKE_THREAD_LIBS_INIT}) ++ list(APPEND LIBS Threads::Threads) + endif(WIN32) + +-if(NOT DEACTIVATE_LZ4) +- if(LZ4_FOUND) +- set(LIBS ${LIBS} ${LZ4_LIBRARY}) +- else(LZ4_FOUND) +- file(GLOB LZ4_FILES ${LZ4_LOCAL_DIR}/*.c) +- set(SOURCES ${SOURCES} ${LZ4_FILES}) +- endif(LZ4_FOUND) +-endif(NOT DEACTIVATE_LZ4) +- +-if(NOT DEACTIVATE_SNAPPY) +- if(SNAPPY_FOUND) +- set(LIBS ${LIBS} ${SNAPPY_LIBRARY}) +- else(SNAPPY_FOUND) +- file(GLOB SNAPPY_FILES ${SNAPPY_LOCAL_DIR}/*.cc) +- set(SOURCES ${SOURCES} ${SNAPPY_FILES}) +- endif(SNAPPY_FOUND) +-endif(NOT DEACTIVATE_SNAPPY) +- +-if(NOT DEACTIVATE_ZLIB) +- if(ZLIB_FOUND) +- set(LIBS ${LIBS} ${ZLIB_LIBRARY}) +- else(ZLIB_FOUND) +- file(GLOB ZLIB_FILES ${ZLIB_LOCAL_DIR}/*.c) +- set(SOURCES ${SOURCES} ${ZLIB_FILES}) +- endif(ZLIB_FOUND) +-endif(NOT DEACTIVATE_ZLIB) +- +-if (NOT DEACTIVATE_ZSTD) +- if (ZSTD_FOUND) +- set(LIBS ${LIBS} ${ZSTD_LIBRARY}) +- else (ZSTD_FOUND) +- file(GLOB ZSTD_FILES +- ${ZSTD_LOCAL_DIR}/common/*.c +- ${ZSTD_LOCAL_DIR}/compress/*.c +- ${ZSTD_LOCAL_DIR}/decompress/*.c) +- set(SOURCES ${SOURCES} ${ZSTD_FILES}) +- endif (ZSTD_FOUND) +-endif (NOT DEACTIVATE_ZSTD) +- +- + # targets + if (BUILD_SHARED) + add_library(blosc_shared SHARED ${SOURCES}) +@@ -191,14 +110,17 @@ if (BUILD_TESTS) + endif() + endif() + ++add_library(blosc INTERFACE) ++ + if (BUILD_SHARED) +- target_link_libraries(blosc_shared ${LIBS}) +- target_include_directories(blosc_shared PUBLIC ${BLOSC_INCLUDE_DIRS}) ++ target_link_libraries(blosc_shared PRIVATE ${LIBS}) ++ target_include_directories(blosc_shared PUBLIC $) ++ target_link_libraries(blosc INTERFACE blosc_shared) + endif() + + if (BUILD_TESTS) +- target_link_libraries(blosc_shared_testing ${LIBS}) +- target_include_directories(blosc_shared_testing PUBLIC ${BLOSC_INCLUDE_DIRS}) ++ target_link_libraries(blosc_shared_testing PRIVATE ${LIBS}) ++ target_include_directories(blosc_shared_testing PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + endif() + + if(BUILD_STATIC) +@@ -207,17 +129,31 @@ if(BUILD_STATIC) + if (MSVC) + set_target_properties(blosc_static PROPERTIES PREFIX lib) + endif() +- target_link_libraries(blosc_static ${LIBS}) +- target_include_directories(blosc_static PUBLIC ${BLOSC_INCLUDE_DIRS}) ++ # With the static library, cmake has to deal with transitive dependencies ++ target_link_libraries(blosc_static PRIVATE ${LIBS}) ++ target_include_directories(blosc_static PUBLIC $) ++ if (NOT BUILD_SHARED) ++ target_link_libraries(blosc INTERFACE blosc_static) ++ endif() + endif(BUILD_STATIC) + ++ + # install + if(BLOSC_INSTALL) + install(FILES blosc.h blosc-export.h DESTINATION include COMPONENT DEV) ++ set(_inst_libs "blosc") + if(BUILD_SHARED) +- install(TARGETS blosc_shared DESTINATION ${lib_dir} COMPONENT LIB) ++ list(APPEND _inst_libs blosc_shared) + endif(BUILD_SHARED) + if(BUILD_STATIC) +- install(TARGETS blosc_static DESTINATION ${lib_dir} COMPONENT DEV) ++ list(APPEND _inst_libs blosc_static) + endif(BUILD_STATIC) ++ ++ install(TARGETS ${_inst_libs} ++ EXPORT BloscTargets ++ LIBRARY DESTINATION ${lib_dir} ++ ARCHIVE DESTINATION ${lib_dir} ++ RUNTIME DESTINATION bin ++ COMPONENT DEV ++ INCLUDES DESTINATION include) + endif(BLOSC_INSTALL) +diff --git a/cmake/FindLZ4.cmake b/cmake/FindLZ4.cmake +index e581a80..05de6ef 100644 +--- a/cmake/FindLZ4.cmake ++++ b/cmake/FindLZ4.cmake +@@ -5,6 +5,10 @@ find_library(LZ4_LIBRARY NAMES lz4) + if (LZ4_INCLUDE_DIR AND LZ4_LIBRARY) + set(LZ4_FOUND TRUE) + message(STATUS "Found LZ4 library: ${LZ4_LIBRARY}") ++ add_library(LZ4::LZ4 UNKNOWN IMPORTED) ++ set_target_properties(LZ4::LZ4 PROPERTIES ++ IMPORTED_LOCATION ${LZ4_LIBRARY} ++ INTERFACE_INCLUDE_DIRECTORIES ${LZ4_INCLUDE_DIR}) + else () + message(STATUS "No LZ4 library found. Using internal sources.") +-endif () ++endif () +\ No newline at end of file +diff --git a/cmake/FindSnappy.cmake b/cmake/FindSnappy.cmake +index 688d4d5..21dbee1 100644 +--- a/cmake/FindSnappy.cmake ++++ b/cmake/FindSnappy.cmake +@@ -3,8 +3,12 @@ find_path(SNAPPY_INCLUDE_DIR snappy-c.h) + find_library(SNAPPY_LIBRARY NAMES snappy) + + if (SNAPPY_INCLUDE_DIR AND SNAPPY_LIBRARY) +- set(SNAPPY_FOUND TRUE) ++ set(Snappy_FOUND TRUE) ++ add_library(Snappy::snappy UNKNOWN IMPORTED) ++ set_target_properties(Snappy::snappy PROPERTIES ++ IMPORTED_LOCATION ${SNAPPY_LIBRARY} ++ INTERFACE_INCLUDE_DIRECTORIES ${SNAPPY_INCLUDE_DIR}) + message(STATUS "Found SNAPPY library: ${SNAPPY_LIBRARY}") + else () + message(STATUS "No snappy found. Using internal sources.") +-endif () ++endif () +\ No newline at end of file +diff --git a/cmake/FindZstd.cmake b/cmake/FindZstd.cmake +index 7db4bb9..cabc2f8 100644 +--- a/cmake/FindZstd.cmake ++++ b/cmake/FindZstd.cmake +@@ -3,8 +3,12 @@ find_path(ZSTD_INCLUDE_DIR zstd.h) + find_library(ZSTD_LIBRARY NAMES zstd) + + if (ZSTD_INCLUDE_DIR AND ZSTD_LIBRARY) +- set(ZSTD_FOUND TRUE) ++ set(Zstd_FOUND TRUE) ++ add_library(Zstd::Zstd UNKNOWN IMPORTED) ++ set_target_properties(Zstd::Zstd PROPERTIES ++ IMPORTED_LOCATION ${ZSTD_LIBRARY} ++ INTERFACE_INCLUDE_DIRECTORIES ${ZSTD_INCLUDE_DIR}) + message(STATUS "Found Zstd library: ${ZSTD_LIBRARY}") + else () + message(STATUS "No Zstd library found. Using internal sources.") +-endif () ++endif () +\ No newline at end of file +diff --git a/cmake_config.cmake.in b/cmake_config.cmake.in +new file mode 100644 +index 0000000..0f6af24 +--- /dev/null ++++ b/cmake_config.cmake.in +@@ -0,0 +1,24 @@ ++include(CMakeFindDependencyMacro) ++ ++include("${CMAKE_CURRENT_LIST_DIR}/BloscTargets.cmake") ++ ++function(_blosc_remap_configs from_Cfg to_Cfg) ++ string(TOUPPER ${from_Cfg} from_CFG) ++ string(TOLOWER ${from_Cfg} from_cfg) ++ ++ if(NOT EXISTS ${CMAKE_CURRENT_LIST_DIR}/BloscTargets-${from_cfg}.cmake) ++ foreach(tgt IN ITEMS blosc_static blosc_shared blosc) ++ if(TARGET Blosc::${tgt}) ++ set_target_properties(Blosc::${tgt} PROPERTIES ++ MAP_IMPORTED_CONFIG_${from_CFG} ${to_Cfg}) ++ endif() ++ endforeach() ++ endif() ++endfunction() ++ ++# MSVC will try to link RelWithDebInfo or MinSizeRel target with debug config ++# if no matching installation is present which would result in link errors. ++if(MSVC) ++ _blosc_remap_configs(RelWithDebInfo Release) ++ _blosc_remap_configs(MinSizeRel Release) ++endif() +diff --git a/internal-complibs/CMakeLists.txt b/internal-complibs/CMakeLists.txt +new file mode 100644 +index 0000000..4586efa +--- /dev/null ++++ b/internal-complibs/CMakeLists.txt +@@ -0,0 +1,35 @@ ++macro(add_lib_target pkg tgt incdir files) ++ string(TOUPPER ${pkg} TGT) ++ if(NOT DEACTIVATE_${TGT} AND NOT ${pkg}_FOUND) ++ add_library(${tgt}_objs OBJECT ${files}) ++ add_library(${tgt} INTERFACE) ++ target_include_directories(${tgt}_objs PRIVATE $) ++ target_include_directories(${tgt} INTERFACE $) ++ #set_target_properties(${tgt} PROPERTIES INTERFACE_SOURCES "$") ++ set_target_properties(${tgt}_objs PROPERTIES POSITION_INDEPENDENT_CODE ON) ++ target_sources(${tgt} INTERFACE "$>") ++ add_library(${pkg}::${tgt} ALIAS ${tgt}) ++ ++ # This creates dummy (empty) interface targets in the exported config. ++ install(TARGETS ${tgt} EXPORT BloscTargets INCLUDES DESTINATION include) ++ endif() ++ unset(TGT) ++endmacro() ++ ++set(ZLIB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/zlib-1.2.8) ++file(GLOB ZLIB_FILES ${ZLIB_DIR}/*.c) ++add_lib_target(ZLIB ZLIB ${ZLIB_DIR} "${ZLIB_FILES}") ++ ++set(SNAPPY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/snappy-1.1.1) ++file(GLOB SNAPPY_FILES ${SNAPPY_DIR}/*.cc) ++add_lib_target(Snappy snappy ${SNAPPY_DIR} "${SNAPPY_FILES}") ++ ++set(LZ4_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lz4-1.9.1) ++file(GLOB LZ4_FILES ${LZ4_DIR}/*.c) ++add_lib_target(LZ4 LZ4 ${LZ4_DIR} "${LZ4_FILES}") ++ ++set(ZSTD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/zstd-1.4.1) ++file(GLOB ZSTD_FILES ${ZSTD_DIR}/common/*.c ${ZSTD_DIR}/compress/*.c ${ZSTD_DIR}/decompress/*.c) ++add_lib_target(Zstd Zstd ${ZSTD_DIR} "${ZSTD_FILES}") ++target_include_directories(Zstd INTERFACE $) ++target_include_directories(Zstd_objs PRIVATE $) +\ No newline at end of file +-- +2.16.2.windows.1 + diff --git a/deps/deps-linux.cmake b/deps/deps-linux.cmake index 03e8e12d5..368ea1fc1 100644 --- a/deps/deps-linux.cmake +++ b/deps/deps-linux.cmake @@ -5,11 +5,11 @@ include("deps-unix-common.cmake") ExternalProject_Add(dep_boost EXCLUDE_FROM_ALL 1 - URL "https://dl.bintray.com/boostorg/release/1.66.0/source/boost_1_66_0.tar.gz" - URL_HASH SHA256=bd0df411efd9a585e5a2212275f8762079fed8842264954675a4fddc46cfcf60 + URL "https://dl.bintray.com/boostorg/release/1.70.0/source/boost_1_70_0.tar.gz" + URL_HASH SHA256=882b48708d211a5f48e60b0124cf5863c1534cd544ecd0664bb534a4b5d506e9 BUILD_IN_SOURCE 1 CONFIGURE_COMMAND ./bootstrap.sh - --with-libraries=system,filesystem,thread,log,locale,regex + --with-libraries=system,iostreams,filesystem,thread,log,locale,regex "--prefix=${DESTDIR}/usr/local" BUILD_COMMAND ./b2 -j ${NPROC} @@ -26,8 +26,8 @@ ExternalProject_Add(dep_boost ExternalProject_Add(dep_libopenssl EXCLUDE_FROM_ALL 1 - URL "https://github.com/openssl/openssl/archive/OpenSSL_1_1_0g.tar.gz" - URL_HASH SHA256=8e9516b8635bb9113c51a7b5b27f9027692a56b104e75b709e588c3ffd6a0422 + URL "https://github.com/openssl/openssl/archive/OpenSSL_1_1_0l.tar.gz" + URL_HASH SHA256=e2acf0cf58d9bff2b42f2dc0aee79340c8ffe2c5e45d3ca4533dd5d4f5775b1d BUILD_IN_SOURCE 1 CONFIGURE_COMMAND ./config "--prefix=${DESTDIR}/usr/local" @@ -88,19 +88,17 @@ ExternalProject_Add(dep_libcurl ) if (DEP_WX_STABLE) - set(DEP_WX_URL "https://github.com/wxWidgets/wxWidgets/releases/download/v3.0.4/wxWidgets-3.0.4.tar.bz2") - set(DEP_WX_HASH "SHA256=96157f988d261b7368e5340afa1a0cad943768f35929c22841f62c25b17bf7f0") + set(DEP_WX_TAG "v3.0.4") else () - set(DEP_WX_URL "https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.1/wxWidgets-3.1.1.tar.bz2") - set(DEP_WX_HASH "SHA256=c925dfe17e8f8b09eb7ea9bfdcfcc13696a3e14e92750effd839f5e10726159e") + set(DEP_WX_TAG "v3.1.1-patched") endif() ExternalProject_Add(dep_wxwidgets EXCLUDE_FROM_ALL 1 - URL "${DEP_WX_URL}" - URL_HASH "${DEP_WX_HASH}" + GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" + GIT_TAG "${DEP_WX_TAG}" BUILD_IN_SOURCE 1 - PATCH_COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/wxwidgets-pngprefix.h" src/png/pngprefix.h + # PATCH_COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/wxwidgets-pngprefix.h" src/png/pngprefix.h CONFIGURE_COMMAND ./configure "--prefix=${DESTDIR}/usr/local" --disable-shared @@ -123,3 +121,5 @@ ExternalProject_Add(dep_wxwidgets BUILD_COMMAND make "-j${NPROC}" && make -C locale allmo INSTALL_COMMAND make install ) + +add_dependencies(dep_openvdb dep_boost) diff --git a/deps/deps-macos.cmake b/deps/deps-macos.cmake index d22e4a2e2..9e51735fd 100644 --- a/deps/deps-macos.cmake +++ b/deps/deps-macos.cmake @@ -6,7 +6,7 @@ set(DEP_WERRORS_SDK "-Werror=partial-availability -Werror=unguarded-availability set(DEP_CMAKE_OPTS "-DCMAKE_POSITION_INDEPENDENT_CODE=ON" "-DCMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT}" - "-DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}" + "-DCMAKE_OSX_DEPLOYMENT_TARGET=${DEP_OSX_TARGET}" "-DCMAKE_CXX_FLAGS=${DEP_WERRORS_SDK}" "-DCMAKE_C_FLAGS=${DEP_WERRORS_SDK}" ) @@ -14,28 +14,27 @@ set(DEP_CMAKE_OPTS include("deps-unix-common.cmake") -set(DEP_BOOST_OSX_TARGET "") -if (CMAKE_OSX_DEPLOYMENT_TARGET) - set(DEP_BOOST_OSX_TARGET "-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") -endif () - ExternalProject_Add(dep_boost EXCLUDE_FROM_ALL 1 - URL "https://dl.bintray.com/boostorg/release/1.66.0/source/boost_1_66_0.tar.gz" - URL_HASH SHA256=bd0df411efd9a585e5a2212275f8762079fed8842264954675a4fddc46cfcf60 + URL "https://dl.bintray.com/boostorg/release/1.70.0/source/boost_1_70_0.tar.gz" + URL_HASH SHA256=882b48708d211a5f48e60b0124cf5863c1534cd544ecd0664bb534a4b5d506e9 BUILD_IN_SOURCE 1 CONFIGURE_COMMAND ./bootstrap.sh - --with-libraries=system,filesystem,thread,log,locale,regex + --with-toolset=clang + --with-libraries=system,iostreams,filesystem,thread,log,locale,regex "--prefix=${DESTDIR}/usr/local" BUILD_COMMAND ./b2 -j ${NPROC} --reconfigure + toolset=clang link=static variant=release threading=multi boost.locale.icu=off - "cflags=-fPIC ${DEP_BOOST_OSX_TARGET}" - "cxxflags=-fPIC ${DEP_BOOST_OSX_TARGET}" + "cflags=-fPIC -mmacosx-version-min=${DEP_OSX_TARGET}" + "cxxflags=-fPIC -mmacosx-version-min=${DEP_OSX_TARGET}" + "mflags=-fPIC -mmacosx-version-min=${DEP_OSX_TARGET}" + "mmflags=-fPIC -mmacosx-version-min=${DEP_OSX_TARGET}" install INSTALL_COMMAND "" # b2 does that already ) @@ -91,8 +90,6 @@ ExternalProject_Add(dep_wxwidgets EXCLUDE_FROM_ALL 1 GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" GIT_TAG v3.1.1-patched -# URL "https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.2/wxWidgets-3.1.2.tar.bz2" -# URL_HASH SHA256=4cb8d23d70f9261debf7d6cfeca667fc0a7d2b6565adb8f1c484f9b674f1f27a BUILD_IN_SOURCE 1 # PATCH_COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/wxwidgets-pngprefix.h" src/png/pngprefix.h CONFIGURE_COMMAND env "CXXFLAGS=${DEP_WERRORS_SDK}" "CFLAGS=${DEP_WERRORS_SDK}" ./configure @@ -114,3 +111,5 @@ ExternalProject_Add(dep_wxwidgets BUILD_COMMAND make "-j${NPROC}" && PATH=/usr/local/opt/gettext/bin/:$ENV{PATH} make -C locale allmo INSTALL_COMMAND make install ) + +add_dependencies(dep_openvdb dep_boost) \ No newline at end of file diff --git a/deps/deps-unix-common.cmake b/deps/deps-unix-common.cmake index 6e559d05a..b6e6653cd 100644 --- a/deps/deps-unix-common.cmake +++ b/deps/deps-unix-common.cmake @@ -7,6 +7,8 @@ else () set(TBB_MINGW_WORKAROUND "") endif () +find_package(ZLIB REQUIRED) + ExternalProject_Add(dep_tbb EXCLUDE_FROM_ALL 1 URL "https://github.com/wjakob/tbb/archive/a0dc9bf76d0120f917b641ed095360448cabc85b.tar.gz" @@ -53,40 +55,67 @@ find_package(Git REQUIRED) ExternalProject_Add(dep_qhull EXCLUDE_FROM_ALL 1 - URL "https://github.com/qhull/qhull/archive/v7.2.1.tar.gz" - URL_HASH SHA256=6fc251e0b75467e00943bfb7191e986fce0e1f8f6f0251f9c6ce5a843821ea78 + #URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" + #URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 + GIT_REPOSITORY https://github.com/qhull/qhull.git + GIT_TAG 7afedcc73666e46a9f1d74632412ebecf53b1b30 # v7.3.2 plus the mac build patch CMAKE_ARGS -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local ${DEP_CMAKE_OPTS} - PATCH_COMMAND ${GIT_EXECUTABLE} apply --ignore-space-change --ignore-whitespace ${CMAKE_CURRENT_SOURCE_DIR}/qhull-mods.patch ) -ExternalProject_Add(dep_libigl +ExternalProject_Add(dep_blosc EXCLUDE_FROM_ALL 1 - URL "https://github.com/libigl/libigl/archive/v2.0.0.tar.gz" - URL_HASH SHA256=42518e6b106c7209c73435fd260ed5d34edeb254852495b4c95dce2d95401328 + GIT_REPOSITORY https://github.com/Blosc/c-blosc.git + GIT_TAG e63775855294b50820ef44d1b157f4de1cc38d3e #v1.17.0 + DEPENDS CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local - -DLIBIGL_BUILD_PYTHON=OFF - -DLIBIGL_BUILD_TESTS=OFF - -DLIBIGL_BUILD_TUTORIALS=OFF - -DLIBIGL_USE_STATIC_LIBRARY=OFF #${DEP_BUILD_IGL_STATIC} - -DLIBIGL_WITHOUT_COPYLEFT=OFF - -DLIBIGL_WITH_CGAL=OFF - -DLIBIGL_WITH_COMISO=OFF - -DLIBIGL_WITH_CORK=OFF - -DLIBIGL_WITH_EMBREE=OFF - -DLIBIGL_WITH_MATLAB=OFF - -DLIBIGL_WITH_MOSEK=OFF - -DLIBIGL_WITH_OPENGL=OFF - -DLIBIGL_WITH_OPENGL_GLFW=OFF - -DLIBIGL_WITH_OPENGL_GLFW_IMGUI=OFF - -DLIBIGL_WITH_PNG=OFF - -DLIBIGL_WITH_PYTHON=OFF - -DLIBIGL_WITH_TETGEN=OFF - -DLIBIGL_WITH_TRIANGLE=OFF - -DLIBIGL_WITH_XML=OFF - PATCH_COMMAND ${GIT_EXECUTABLE} apply --ignore-space-change --ignore-whitespace ${CMAKE_CURRENT_SOURCE_DIR}/igl-fixes.patch + -DBUILD_SHARED_LIBS=OFF + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + -DCMAKE_DEBUG_POSTFIX=d + -DBUILD_SHARED=OFF + -DBUILD_STATIC=ON + -DBUILD_TESTS=OFF + -DBUILD_BENCHMARKS=OFF + -DPREFER_EXTERNAL_ZLIB=ON + PATCH_COMMAND ${GIT_EXECUTABLE} reset --hard && git clean -df && + ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch ) +ExternalProject_Add(dep_openexr + EXCLUDE_FROM_ALL 1 + GIT_REPOSITORY https://github.com/openexr/openexr.git + GIT_TAG eae0e337c9f5117e78114fd05f7a415819df413a #v2.4.0 + CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local + -DBUILD_SHARED_LIBS=OFF + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + -DBUILD_TESTING=OFF + -DPYILMBASE_ENABLE:BOOL=OFF + -DOPENEXR_VIEWERS_ENABLE:BOOL=OFF + -DOPENEXR_BUILD_UTILS:BOOL=OFF +) + +ExternalProject_Add(dep_openvdb + EXCLUDE_FROM_ALL 1 + GIT_REPOSITORY https://github.com/AcademySoftwareFoundation/openvdb.git + GIT_TAG aebaf8d95be5e57fd33949281ec357db4a576c2e #v6.2.1 + DEPENDS dep_blosc dep_openexr dep_tbb + CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local + -DCMAKE_DEBUG_POSTFIX=d + -DCMAKE_PREFIX_PATH=${DESTDIR}/usr/local + -DBUILD_SHARED_LIBS=OFF + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + -DOPENVDB_BUILD_PYTHON_MODULE=OFF + -DUSE_BLOSC=ON + -DOPENVDB_CORE_SHARED=OFF + -DOPENVDB_CORE_STATIC=ON + -DTBB_STATIC=ON + -DOPENVDB_BUILD_VDB_PRINT=ON + -DDISABLE_DEPENDENCY_VERSION_CHECKS=ON + PATCH_COMMAND PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && + ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch +) diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index 2c65fc81f..f60b5c245 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -44,6 +44,18 @@ else () set(DEP_BOOST_DEBUG "") endif () +macro(add_debug_dep _dep) +if (${DEP_DEBUG}) + ExternalProject_Get_Property(${_dep} BINARY_DIR) + ExternalProject_Add_Step(${_dep} build_debug + DEPENDEES build + DEPENDERS install + COMMAND msbuild /m /P:Configuration=Debug INSTALL.vcxproj + WORKING_DIRECTORY "${BINARY_DIR}" + ) +endif () +endmacro() + ExternalProject_Add(dep_boost EXCLUDE_FROM_ALL 1 URL "https://dl.bintray.com/boostorg/release/1.70.0/source/boost_1_70_0.tar.gz" @@ -53,6 +65,7 @@ ExternalProject_Add(dep_boost BUILD_COMMAND b2.exe -j "${NPROC}" --with-system + --with-iostreams --with-filesystem --with-thread --with-log @@ -69,7 +82,6 @@ ExternalProject_Add(dep_boost INSTALL_COMMAND "" # b2 does that already ) - ExternalProject_Add(dep_tbb EXCLUDE_FROM_ALL 1 URL "https://github.com/wjakob/tbb/archive/a0dc9bf76d0120f917b641ed095360448cabc85b.tar.gz" @@ -86,43 +98,25 @@ ExternalProject_Add(dep_tbb BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj INSTALL_COMMAND "" ) -if (${DEP_DEBUG}) - ExternalProject_Get_Property(dep_tbb BINARY_DIR) - ExternalProject_Add_Step(dep_tbb build_debug - DEPENDEES build - DEPENDERS install - COMMAND msbuild /m /P:Configuration=Debug INSTALL.vcxproj - WORKING_DIRECTORY "${BINARY_DIR}" - ) -endif () +add_debug_dep(dep_tbb) -ExternalProject_Add(dep_gtest - EXCLUDE_FROM_ALL 1 - URL "https://github.com/google/googletest/archive/release-1.8.1.tar.gz" - URL_HASH SHA256=9bf1fe5182a604b4135edc1a425ae356c9ad15e9b23f9f12a02e80184c3a249c - CMAKE_GENERATOR "${DEP_MSVC_GEN}" -# if (DEP_VS_VER MORE 15) -# CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" -# endif() - CMAKE_ARGS - -DBUILD_GMOCK=OFF - -Dgtest_force_shared_crt=ON - -DCMAKE_POSITION_INDEPENDENT_CODE=ON - "-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local" - BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj - INSTALL_COMMAND "" -) -if (${DEP_DEBUG}) - ExternalProject_Get_Property(dep_gtest BINARY_DIR) - ExternalProject_Add_Step(dep_gtest build_debug - DEPENDEES build - DEPENDERS install - COMMAND msbuild /m /P:Configuration=Debug INSTALL.vcxproj - WORKING_DIRECTORY "${BINARY_DIR}" - ) -endif () +# ExternalProject_Add(dep_gtest +# EXCLUDE_FROM_ALL 1 +# URL "https://github.com/google/googletest/archive/release-1.8.1.tar.gz" +# URL_HASH SHA256=9bf1fe5182a604b4135edc1a425ae356c9ad15e9b23f9f12a02e80184c3a249c +# CMAKE_GENERATOR "${DEP_MSVC_GEN}" +# CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" +# CMAKE_ARGS +# -DBUILD_GMOCK=OFF +# -Dgtest_force_shared_crt=ON +# -DCMAKE_POSITION_INDEPENDENT_CODE=ON +# "-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local" +# BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj +# INSTALL_COMMAND "" +# ) +# add_debug_dep(dep_gtest) ExternalProject_Add(dep_cereal EXCLUDE_FROM_ALL 1 @@ -137,7 +131,6 @@ ExternalProject_Add(dep_cereal INSTALL_COMMAND "" ) - ExternalProject_Add(dep_nlopt EXCLUDE_FROM_ALL 1 URL "https://github.com/stevengj/nlopt/archive/v2.5.0.tar.gz" @@ -158,16 +151,8 @@ ExternalProject_Add(dep_nlopt BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj INSTALL_COMMAND "" ) -if (${DEP_DEBUG}) - ExternalProject_Get_Property(dep_nlopt BINARY_DIR) - ExternalProject_Add_Step(dep_nlopt build_debug - DEPENDEES build - DEPENDERS install - COMMAND msbuild /m /P:Configuration=Debug INSTALL.vcxproj - WORKING_DIRECTORY "${BINARY_DIR}" - ) -endif () +add_debug_dep(dep_nlopt) ExternalProject_Add(dep_zlib EXCLUDE_FROM_ALL 1 @@ -185,15 +170,9 @@ ExternalProject_Add(dep_zlib BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj INSTALL_COMMAND "" ) -if (${DEP_DEBUG}) - ExternalProject_Get_Property(dep_zlib BINARY_DIR) - ExternalProject_Add_Step(dep_zlib build_debug - DEPENDEES build - DEPENDERS install - COMMAND msbuild /m /P:Configuration=Debug INSTALL.vcxproj - WORKING_DIRECTORY "${BINARY_DIR}" - ) -endif () + +add_debug_dep(dep_zlib) + # The following steps are unfortunately needed to remove the _static suffix on libraries ExternalProject_Add_Step(dep_zlib fix_static DEPENDEES install @@ -208,7 +187,6 @@ if (${DEP_DEBUG}) ) endif () - if (${DEPS_BITS} EQUAL 32) set(DEP_LIBCURL_TARGET "x86") else () @@ -247,29 +225,21 @@ find_package(Git REQUIRED) ExternalProject_Add(dep_qhull EXCLUDE_FROM_ALL 1 - URL "https://github.com/qhull/qhull/archive/v7.2.1.tar.gz" - URL_HASH SHA256=6fc251e0b75467e00943bfb7191e986fce0e1f8f6f0251f9c6ce5a843821ea78 + #URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" + #URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 + GIT_REPOSITORY https://github.com/qhull/qhull.git + GIT_TAG 7afedcc73666e46a9f1d74632412ebecf53b1b30 # v7.3.2 plus the mac build patch CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local -DBUILD_SHARED_LIBS=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_DEBUG_POSTFIX=d - PATCH_COMMAND ${GIT_EXECUTABLE} apply --ignore-space-change --ignore-whitespace ${CMAKE_CURRENT_SOURCE_DIR}/qhull-mods.patch BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj INSTALL_COMMAND "" ) -if (${DEP_DEBUG}) - ExternalProject_Get_Property(dep_qhull BINARY_DIR) - ExternalProject_Add_Step(dep_qhull build_debug - DEPENDEES build - DEPENDERS install - COMMAND msbuild /m /P:Configuration=Debug INSTALL.vcxproj - WORKING_DIRECTORY "${BINARY_DIR}" - ) -endif () - +add_debug_dep(dep_qhull) if (${DEPS_BITS} EQUAL 32) set(DEP_WXWIDGETS_TARGET "") @@ -281,49 +251,6 @@ endif () find_package(Git REQUIRED) -ExternalProject_Add(dep_libigl - EXCLUDE_FROM_ALL 1 - URL "https://github.com/libigl/libigl/archive/v2.0.0.tar.gz" - URL_HASH SHA256=42518e6b106c7209c73435fd260ed5d34edeb254852495b4c95dce2d95401328 - CMAKE_GENERATOR "${DEP_MSVC_GEN}" - CMAKE_ARGS - -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local - -DLIBIGL_BUILD_PYTHON=OFF - -DLIBIGL_BUILD_TESTS=OFF - -DLIBIGL_BUILD_TUTORIALS=OFF - -DLIBIGL_USE_STATIC_LIBRARY=OFF #${DEP_BUILD_IGL_STATIC} - -DLIBIGL_WITHOUT_COPYLEFT=OFF - -DLIBIGL_WITH_CGAL=OFF - -DLIBIGL_WITH_COMISO=OFF - -DLIBIGL_WITH_CORK=OFF - -DLIBIGL_WITH_EMBREE=OFF - -DLIBIGL_WITH_MATLAB=OFF - -DLIBIGL_WITH_MOSEK=OFF - -DLIBIGL_WITH_OPENGL=OFF - -DLIBIGL_WITH_OPENGL_GLFW=OFF - -DLIBIGL_WITH_OPENGL_GLFW_IMGUI=OFF - -DLIBIGL_WITH_PNG=OFF - -DLIBIGL_WITH_PYTHON=OFF - -DLIBIGL_WITH_TETGEN=OFF - -DLIBIGL_WITH_TRIANGLE=OFF - -DLIBIGL_WITH_XML=OFF - -DCMAKE_POSITION_INDEPENDENT_CODE=ON - -DCMAKE_DEBUG_POSTFIX=d - PATCH_COMMAND ${GIT_EXECUTABLE} apply --ignore-space-change --ignore-whitespace ${CMAKE_CURRENT_SOURCE_DIR}/igl-fixes.patch - BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj - INSTALL_COMMAND "" -) - -if (${DEP_DEBUG}) - ExternalProject_Get_Property(dep_libigl BINARY_DIR) - ExternalProject_Add_Step(dep_libigl build_debug - DEPENDEES build - DEPENDERS install - COMMAND msbuild /m /P:Configuration=Debug INSTALL.vcxproj - WORKING_DIRECTORY "${BINARY_DIR}" - ) -endif () - ExternalProject_Add(dep_wxwidgets EXCLUDE_FROM_ALL 1 GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" @@ -346,3 +273,91 @@ if (${DEP_DEBUG}) WORKING_DIRECTORY "${SOURCE_DIR}" ) endif () + +ExternalProject_Add(dep_blosc + EXCLUDE_FROM_ALL 1 + #URL https://github.com/Blosc/c-blosc/archive/v1.17.0.zip + #URL_HASH SHA256=7463a1df566704f212263312717ab2c36b45d45cba6cd0dccebf91b2cc4b4da9 + GIT_REPOSITORY https://github.com/Blosc/c-blosc.git + GIT_TAG e63775855294b50820ef44d1b157f4de1cc38d3e #v1.17.0 + DEPENDS dep_zlib + CMAKE_GENERATOR "${DEP_MSVC_GEN}" + CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" + CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local + -DBUILD_SHARED_LIBS=OFF + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + -DCMAKE_DEBUG_POSTFIX=d + -DBUILD_SHARED=OFF + -DBUILD_STATIC=ON + -DBUILD_TESTS=OFF + -DBUILD_BENCHMARKS=OFF + -DPREFER_EXTERNAL_ZLIB=ON + -DBLOSC_IS_SUBPROJECT:BOOL=ON + -DBLOSC_INSTALL:BOOL=ON + PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && + ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch + BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj + INSTALL_COMMAND "" +) + +add_debug_dep(dep_blosc) + +ExternalProject_Add(dep_openexr + EXCLUDE_FROM_ALL 1 + GIT_REPOSITORY https://github.com/openexr/openexr.git + GIT_TAG eae0e337c9f5117e78114fd05f7a415819df413a #v2.4.0 + DEPENDS dep_zlib + CMAKE_GENERATOR "${DEP_MSVC_GEN}" + CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" + CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local + -DBUILD_SHARED_LIBS=OFF + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + -DBUILD_TESTING=OFF + -DPYILMBASE_ENABLE:BOOL=OFF + -DOPENEXR_VIEWERS_ENABLE:BOOL=OFF + -DOPENEXR_BUILD_UTILS:BOOL=OFF + BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj + INSTALL_COMMAND "" +) + +add_debug_dep(dep_openexr) + +ExternalProject_Add(dep_openvdb + EXCLUDE_FROM_ALL 1 + #URL https://github.com/AcademySoftwareFoundation/openvdb/archive/v6.2.1.zip + #URL_HASH SHA256=dc337399dce8e1c9f21f20e97b1ce7e4933cb0a63bb3b8b734d8fcc464aa0c48 + GIT_REPOSITORY https://github.com/AcademySoftwareFoundation/openvdb.git + GIT_TAG aebaf8d95be5e57fd33949281ec357db4a576c2e #v6.2.1 + DEPENDS dep_blosc dep_openexr dep_tbb dep_boost + CMAKE_GENERATOR "${DEP_MSVC_GEN}" + CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" + CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local + -DCMAKE_DEBUG_POSTFIX=d + -DCMAKE_PREFIX_PATH=${DESTDIR}/usr/local + -DBUILD_SHARED_LIBS=OFF + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + -DOPENVDB_BUILD_PYTHON_MODULE=OFF + -DUSE_BLOSC=ON + -DOPENVDB_CORE_SHARED=OFF + -DOPENVDB_CORE_STATIC=ON + -DTBB_STATIC=ON + -DOPENVDB_BUILD_VDB_PRINT=ON + BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj + PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && + ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch + INSTALL_COMMAND "" +) + +if (${DEP_DEBUG}) + ExternalProject_Get_Property(dep_openvdb BINARY_DIR) + ExternalProject_Add_Step(dep_openvdb build_debug + DEPENDEES build + DEPENDERS install + COMMAND ${CMAKE_COMMAND} ../dep_openvdb -DOPENVDB_BUILD_VDB_PRINT=OFF + COMMAND msbuild /m /P:Configuration=Debug INSTALL.vcxproj + WORKING_DIRECTORY "${BINARY_DIR}" + ) +endif () diff --git a/deps/igl-fixes.patch b/deps/igl-fixes.patch deleted file mode 100644 index b0ff9205d..000000000 --- a/deps/igl-fixes.patch +++ /dev/null @@ -1,128 +0,0 @@ -diff --git a/cmake/libigl-config.cmake.in b/cmake/libigl-config.cmake.in -index 317c745c..f9808e1e 100644 ---- a/cmake/libigl-config.cmake.in -+++ b/cmake/libigl-config.cmake.in -@@ -2,28 +2,28 @@ - - include(${CMAKE_CURRENT_LIST_DIR}/libigl-export.cmake) - --if (TARGET igl::core) -- if (NOT TARGET Eigen3::Eigen) -- find_package(Eigen3 QUIET) -- if (NOT Eigen3_FOUND) -- # try with PkgCOnfig -- find_package(PkgConfig REQUIRED) -- pkg_check_modules(Eigen3 QUIET IMPORTED_TARGET eigen3) -- endif() -- -- if (NOT Eigen3_FOUND) -- message(FATAL_ERROR "Could not find required dependency Eigen3") -- set(libigl_core_FOUND FALSE) -- else() -- target_link_libraries(igl::core INTERFACE PkgConfig::Eigen3) -- set(libigl_core_FOUND TRUE) -- endif() -- else() -- target_link_libraries(igl::core INTERFACE Eigen3::Eigen) -- set(libigl_core_FOUND TRUE) -- endif() -- --endif() -+# if (TARGET igl::core) -+# if (NOT TARGET Eigen3::Eigen) -+# find_package(Eigen3 QUIET) -+# if (NOT Eigen3_FOUND) -+# # try with PkgCOnfig -+# find_package(PkgConfig REQUIRED) -+# pkg_check_modules(Eigen3 QUIET IMPORTED_TARGET eigen3) -+# endif() -+# -+# if (NOT Eigen3_FOUND) -+# message(FATAL_ERROR "Could not find required dependency Eigen3") -+# set(libigl_core_FOUND FALSE) -+# else() -+# target_link_libraries(igl::core INTERFACE PkgConfig::Eigen3) -+# set(libigl_core_FOUND TRUE) -+# endif() -+# else() -+# target_link_libraries(igl::core INTERFACE Eigen3::Eigen) -+# set(libigl_core_FOUND TRUE) -+# endif() -+# -+# endif() - - check_required_components(libigl) - -diff --git a/cmake/libigl.cmake b/cmake/libigl.cmake -index 4b11007a..47e6c395 100644 ---- a/cmake/libigl.cmake -+++ b/cmake/libigl.cmake -@@ -445,6 +445,7 @@ function(install_dir_files dir_name) - if(NOT LIBIGL_USE_STATIC_LIBRARY) - file(GLOB public_sources - ${CMAKE_CURRENT_SOURCE_DIR}/include/igl${subpath}/*.cpp -+ ${CMAKE_CURRENT_SOURCE_DIR}/include/igl${subpath}/*.c - ) - endif() - list(APPEND files_to_install ${public_sources}) -diff --git a/include/igl/AABB.cpp b/include/igl/AABB.cpp -index 09537335..92e90cb7 100644 ---- a/include/igl/AABB.cpp -+++ b/include/igl/AABB.cpp -@@ -1071,5 +1071,11 @@ template void igl::AABB, 3>::init, 2>::init >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); - template double igl::AABB, 3>::squared_distance >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix const&, double, int&, Eigen::PlainObjectBase >&) const; -+template float igl::AABB const, 0, Eigen::Stride<0, 0> >, 3>::squared_distance const, 0, Eigen::Stride<0, 0> > >(Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&, Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&, Eigen::Matrix const&, int&, Eigen::PlainObjectBase >&) const; - template bool igl::AABB, 3>::intersect_ray >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix const&, Eigen::Matrix const&, igl::Hit&) const; -+template bool igl::AABB, 3>::intersect_ray >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix const&, Eigen::Matrix const&, std::vector&) const; -+ -+template void igl::AABB const, 0, Eigen::Stride<0, 0> >, 3>::init const, 0, Eigen::Stride<0, 0> > >(Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&, Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&); -+ -+template bool igl::AABB const, 0, Eigen::Stride<0, 0> >, 3>::intersect_ray const, 0, Eigen::Stride<0, 0> > >(Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&, Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&, Eigen::Matrix const&, Eigen::Matrix const&, std::vector >&) const; - #endif -diff --git a/include/igl/barycenter.cpp b/include/igl/barycenter.cpp -index 065f82aa..ec2d96cd 100644 ---- a/include/igl/barycenter.cpp -+++ b/include/igl/barycenter.cpp -@@ -54,4 +54,6 @@ template void igl::barycenter, Eigen::M - template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); - template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); - template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); -+ -+template void igl::barycenter const, 0, Eigen::Stride<0, 0> >, Eigen::Map const, 0, Eigen::Stride<0, 0> >, Eigen::Matrix >(Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&, Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&, Eigen::PlainObjectBase >&); - #endif -diff --git a/include/igl/point_simplex_squared_distance.cpp b/include/igl/point_simplex_squared_distance.cpp -index 2b98bd28..c66d9ae1 100644 ---- a/include/igl/point_simplex_squared_distance.cpp -+++ b/include/igl/point_simplex_squared_distance.cpp -@@ -178,4 +178,6 @@ template void igl::point_simplex_squared_distance<3, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix::Index, double&, Eigen::MatrixBase >&, Eigen::PlainObjectBase >&); - template void igl::point_simplex_squared_distance<2, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix::Index, double&, Eigen::MatrixBase >&, Eigen::PlainObjectBase >&); - template void igl::point_simplex_squared_distance<2, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix::Index, double&, Eigen::MatrixBase >&, Eigen::PlainObjectBase >&); -+ -+template void igl::point_simplex_squared_distance<3, Eigen::Matrix, Eigen::Map const, 0, Eigen::Stride<0, 0> >, Eigen::Map const, 0, Eigen::Stride<0, 0> >, float, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&, Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&, Eigen::Map const, 0, Eigen::Stride<0, 0> >::Index, float&, Eigen::MatrixBase >&); - #endif -diff --git a/include/igl/ray_box_intersect.cpp b/include/igl/ray_box_intersect.cpp -index 4a88b89e..b547f8f8 100644 ---- a/include/igl/ray_box_intersect.cpp -+++ b/include/igl/ray_box_intersect.cpp -@@ -147,4 +147,6 @@ IGL_INLINE bool igl::ray_box_intersect( - #ifdef IGL_STATIC_LIBRARY - // Explicit template instantiation - template bool igl::ray_box_intersect, Eigen::Matrix, double>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::AlignedBox const&, double const&, double const&, double&, double&); -+ -+template bool igl::ray_box_intersect, Eigen::Matrix, float>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::AlignedBox const&, float const&, float const&, float&, float&); - #endif -diff --git a/include/igl/ray_mesh_intersect.cpp b/include/igl/ray_mesh_intersect.cpp -index 9a70a22b..4233e722 100644 ---- a/include/igl/ray_mesh_intersect.cpp -+++ b/include/igl/ray_mesh_intersect.cpp -@@ -83,4 +83,7 @@ IGL_INLINE bool igl::ray_mesh_intersect( - template bool igl::ray_mesh_intersect, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector >&); - template bool igl::ray_mesh_intersect, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::Hit&); - template bool igl::ray_mesh_intersect, Eigen::Matrix, Eigen::Matrix, Eigen::Block const, 1, -1, false> >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase const, 1, -1, false> > const&, igl::Hit&); -+template bool igl::ray_mesh_intersect, Eigen::Matrix, Eigen::Matrix, Eigen::Block const, 1, -1, false> >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase const, 1, -1, false> > const&, std::vector >&); -+ -+template bool igl::ray_mesh_intersect, Eigen::Matrix, Eigen::Map const, 0, Eigen::Stride<0, 0> >, Eigen::Block const, 0, Eigen::Stride<0, 0> > const, 1, -1, true> >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&, Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > const, 1, -1, true> > const&, std::vector >&); - #endif diff --git a/deps/openvdb-mods.patch b/deps/openvdb-mods.patch new file mode 100644 index 000000000..023cb5308 --- /dev/null +++ b/deps/openvdb-mods.patch @@ -0,0 +1,1783 @@ +From dbe038fce8a15ddc9a5c83ec5156d7bc9e178015 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 + +Signed-off-by: tamasmeszaros +--- + CMakeLists.txt | 3 - + cmake/FindBlosc.cmake | 218 --------------- + cmake/FindCppUnit.cmake | 4 +- + cmake/FindIlmBase.cmake | 337 ---------------------- + cmake/FindOpenEXR.cmake | 329 ---------------------- + cmake/FindOpenVDB.cmake | 19 +- + cmake/FindTBB.cmake | 605 ++++++++++++++++++++-------------------- + openvdb/CMakeLists.txt | 13 +- + 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(-) + delete mode 100644 cmake/FindBlosc.cmake + delete mode 100644 cmake/FindIlmBase.cmake + delete mode 100644 cmake/FindOpenEXR.cmake + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 580b353..6d364c1 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -267,12 +267,9 @@ endif() + + if(OPENVDB_INSTALL_CMAKE_MODULES) + set(OPENVDB_CMAKE_MODULES +- cmake/FindBlosc.cmake + cmake/FindCppUnit.cmake + cmake/FindJemalloc.cmake +- cmake/FindIlmBase.cmake + cmake/FindLog4cplus.cmake +- cmake/FindOpenEXR.cmake + cmake/FindOpenVDB.cmake + cmake/FindTBB.cmake + cmake/OpenVDBGLFW3Setup.cmake +diff --git a/cmake/FindBlosc.cmake b/cmake/FindBlosc.cmake +deleted file mode 100644 +index 5aacfdd..0000000 +--- a/cmake/FindBlosc.cmake ++++ /dev/null +@@ -1,218 +0,0 @@ +-# Copyright (c) DreamWorks Animation LLC +-# +-# All rights reserved. This software is distributed under the +-# Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) +-# +-# Redistributions of source code must retain the above copyright +-# and license notice and the following restrictions and disclaimer. +-# +-# * 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. +-# +-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, +-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +-# 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. +-# +-#[=======================================================================[.rst: +- +-FindBlosc +---------- +- +-Find Blosc include dirs and libraries +- +-Use this module by invoking find_package with the form:: +- +- find_package(Blosc +- [version] [EXACT] # Minimum or EXACT version e.g. 1.5.0 +- [REQUIRED] # Fail with error if Blosc is not found +- ) +- +-IMPORTED Targets +-^^^^^^^^^^^^^^^^ +- +-``Blosc::blosc`` +- This module defines IMPORTED target Blosc::Blosc, if Blosc has been found. +- +-Result Variables +-^^^^^^^^^^^^^^^^ +- +-This will define the following variables: +- +-``Blosc_FOUND`` +- True if the system has the Blosc library. +-``Blosc_VERSION`` +- The version of the Blosc library which was found. +-``Blosc_INCLUDE_DIRS`` +- Include directories needed to use Blosc. +-``Blosc_LIBRARIES`` +- Libraries needed to link to Blosc. +-``Blosc_LIBRARY_DIRS`` +- Blosc library directories. +- +-Cache Variables +-^^^^^^^^^^^^^^^ +- +-The following cache variables may also be set: +- +-``Blosc_INCLUDE_DIR`` +- The directory containing ``blosc.h``. +-``Blosc_LIBRARY`` +- The path to the Blosc library. +- +-Hints +-^^^^^ +- +-Instead of explicitly setting the cache variables, the following variables +-may be provided to tell this module where to look. +- +-``BLOSC_ROOT`` +- Preferred installation prefix. +-``BLOSC_INCLUDEDIR`` +- Preferred include directory e.g. /include +-``BLOSC_LIBRARYDIR`` +- Preferred library directory e.g. /lib +-``SYSTEM_LIBRARY_PATHS`` +- Paths appended to all include and lib searches. +- +-#]=======================================================================] +- +-mark_as_advanced( +- Blosc_INCLUDE_DIR +- Blosc_LIBRARY +-) +- +-# Append BLOSC_ROOT or $ENV{BLOSC_ROOT} if set (prioritize the direct cmake var) +-set(_BLOSC_ROOT_SEARCH_DIR "") +- +-if(BLOSC_ROOT) +- list(APPEND _BLOSC_ROOT_SEARCH_DIR ${BLOSC_ROOT}) +-else() +- set(_ENV_BLOSC_ROOT $ENV{BLOSC_ROOT}) +- if(_ENV_BLOSC_ROOT) +- list(APPEND _BLOSC_ROOT_SEARCH_DIR ${_ENV_BLOSC_ROOT}) +- endif() +-endif() +- +-# Additionally try and use pkconfig to find blosc +- +-find_package(PkgConfig) +-pkg_check_modules(PC_Blosc QUIET blosc) +- +-# ------------------------------------------------------------------------ +-# Search for blosc include DIR +-# ------------------------------------------------------------------------ +- +-set(_BLOSC_INCLUDE_SEARCH_DIRS "") +-list(APPEND _BLOSC_INCLUDE_SEARCH_DIRS +- ${BLOSC_INCLUDEDIR} +- ${_BLOSC_ROOT_SEARCH_DIR} +- ${PC_Blosc_INCLUDE_DIRS} +- ${SYSTEM_LIBRARY_PATHS} +-) +- +-# Look for a standard blosc header file. +-find_path(Blosc_INCLUDE_DIR blosc.h +- NO_DEFAULT_PATH +- PATHS ${_BLOSC_INCLUDE_SEARCH_DIRS} +- PATH_SUFFIXES include +-) +- +-if(EXISTS "${Blosc_INCLUDE_DIR}/blosc.h") +- file(STRINGS "${Blosc_INCLUDE_DIR}/blosc.h" +- _blosc_version_major_string REGEX "#define BLOSC_VERSION_MAJOR +[0-9]+ " +- ) +- string(REGEX REPLACE "#define BLOSC_VERSION_MAJOR +([0-9]+).*$" "\\1" +- _blosc_version_major_string "${_blosc_version_major_string}" +- ) +- string(STRIP "${_blosc_version_major_string}" Blosc_VERSION_MAJOR) +- +- file(STRINGS "${Blosc_INCLUDE_DIR}/blosc.h" +- _blosc_version_minor_string REGEX "#define BLOSC_VERSION_MINOR +[0-9]+ " +- ) +- string(REGEX REPLACE "#define BLOSC_VERSION_MINOR +([0-9]+).*$" "\\1" +- _blosc_version_minor_string "${_blosc_version_minor_string}" +- ) +- string(STRIP "${_blosc_version_minor_string}" Blosc_VERSION_MINOR) +- +- unset(_blosc_version_major_string) +- unset(_blosc_version_minor_string) +- +- set(Blosc_VERSION ${Blosc_VERSION_MAJOR}.${Blosc_VERSION_MINOR}) +-endif() +- +-# ------------------------------------------------------------------------ +-# Search for blosc lib DIR +-# ------------------------------------------------------------------------ +- +-set(_BLOSC_LIBRARYDIR_SEARCH_DIRS "") +-list(APPEND _BLOSC_LIBRARYDIR_SEARCH_DIRS +- ${BLOSC_LIBRARYDIR} +- ${_BLOSC_ROOT_SEARCH_DIR} +- ${PC_Blosc_LIBRARY_DIRS} +- ${SYSTEM_LIBRARY_PATHS} +-) +- +-# Static library setup +-if(UNIX AND BLOSC_USE_STATIC_LIBS) +- set(_BLOSC_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) +- set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") +-endif() +- +-set(BLOSC_PATH_SUFFIXES +- lib64 +- lib +-) +- +-find_library(Blosc_LIBRARY blosc +- NO_DEFAULT_PATH +- PATHS ${_BLOSC_LIBRARYDIR_SEARCH_DIRS} +- PATH_SUFFIXES ${BLOSC_PATH_SUFFIXES} +-) +- +-if(UNIX AND BLOSC_USE_STATIC_LIBS) +- set(CMAKE_FIND_LIBRARY_SUFFIXES ${_BLOSC_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) +- unset(_BLOSC_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES) +-endif() +- +-# ------------------------------------------------------------------------ +-# Cache and set Blosc_FOUND +-# ------------------------------------------------------------------------ +- +-include(FindPackageHandleStandardArgs) +-find_package_handle_standard_args(Blosc +- FOUND_VAR Blosc_FOUND +- REQUIRED_VARS +- Blosc_LIBRARY +- Blosc_INCLUDE_DIR +- VERSION_VAR Blosc_VERSION +-) +- +-if(Blosc_FOUND) +- set(Blosc_LIBRARIES ${Blosc_LIBRARY}) +- set(Blosc_INCLUDE_DIRS ${Blosc_INCLUDE_DIR}) +- set(Blosc_DEFINITIONS ${PC_Blosc_CFLAGS_OTHER}) +- +- get_filename_component(Blosc_LIBRARY_DIRS ${Blosc_LIBRARY} DIRECTORY) +- +- if(NOT TARGET Blosc::blosc) +- add_library(Blosc::blosc UNKNOWN IMPORTED) +- set_target_properties(Blosc::blosc PROPERTIES +- IMPORTED_LOCATION "${Blosc_LIBRARIES}" +- INTERFACE_COMPILE_DEFINITIONS "${Blosc_DEFINITIONS}" +- INTERFACE_INCLUDE_DIRECTORIES "${Blosc_INCLUDE_DIRS}" +- ) +- endif() +-elseif(Blosc_FIND_REQUIRED) +- message(FATAL_ERROR "Unable to find Blosc") +-endif() +diff --git a/cmake/FindCppUnit.cmake b/cmake/FindCppUnit.cmake +index e2beb93..a891624 100644 +--- a/cmake/FindCppUnit.cmake ++++ b/cmake/FindCppUnit.cmake +@@ -125,7 +125,7 @@ list(APPEND _CPPUNIT_INCLUDE_SEARCH_DIRS + + # Look for a standard cppunit header file. + find_path(CppUnit_INCLUDE_DIR cppunit/Portability.h +- NO_DEFAULT_PATH ++ # NO_DEFAULT_PATH + PATHS ${_CPPUNIT_INCLUDE_SEARCH_DIRS} + PATH_SUFFIXES include + ) +@@ -177,7 +177,7 @@ set(CPPUNIT_PATH_SUFFIXES + ) + + find_library(CppUnit_LIBRARY cppunit +- NO_DEFAULT_PATH ++ # NO_DEFAULT_PATH + PATHS ${_CPPUNIT_LIBRARYDIR_SEARCH_DIRS} + PATH_SUFFIXES ${CPPUNIT_PATH_SUFFIXES} + ) +diff --git a/cmake/FindIlmBase.cmake b/cmake/FindIlmBase.cmake +deleted file mode 100644 +index 9dbc252..0000000 +--- a/cmake/FindIlmBase.cmake ++++ /dev/null +@@ -1,337 +0,0 @@ +-# Copyright (c) DreamWorks Animation LLC +-# +-# All rights reserved. This software is distributed under the +-# Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) +-# +-# Redistributions of source code must retain the above copyright +-# and license notice and the following restrictions and disclaimer. +-# +-# * 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. +-# +-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, +-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +-# 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. +-# +-#[=======================================================================[.rst: +- +-FindIlmBase +------------ +- +-Find IlmBase include dirs and libraries +- +-Use this module by invoking find_package with the form:: +- +- find_package(IlmBase +- [version] [EXACT] # Minimum or EXACT version +- [REQUIRED] # Fail with error if IlmBase is not found +- [COMPONENTS ...] # IlmBase libraries by their canonical name +- # e.g. "Half" for "libHalf" +- ) +- +-IMPORTED Targets +-^^^^^^^^^^^^^^^^ +- +-``IlmBase::Half`` +- The Half library target. +-``IlmBase::Iex`` +- The Iex library target. +-``IlmBase::IexMath`` +- The IexMath library target. +-``IlmBase::IlmThread`` +- The IlmThread library target. +-``IlmBase::Imath`` +- The Imath library target. +- +-Result Variables +-^^^^^^^^^^^^^^^^ +- +-This will define the following variables: +- +-``IlmBase_FOUND`` +- True if the system has the IlmBase library. +-``IlmBase_VERSION`` +- The version of the IlmBase library which was found. +-``IlmBase_INCLUDE_DIRS`` +- Include directories needed to use IlmBase. +-``IlmBase_LIBRARIES`` +- Libraries needed to link to IlmBase. +-``IlmBase_LIBRARY_DIRS`` +- IlmBase library directories. +-``IlmBase_{COMPONENT}_FOUND`` +- True if the system has the named IlmBase component. +- +-Cache Variables +-^^^^^^^^^^^^^^^ +- +-The following cache variables may also be set: +- +-``IlmBase_INCLUDE_DIR`` +- The directory containing ``IlmBase/config-auto.h``. +-``IlmBase_{COMPONENT}_LIBRARY`` +- Individual component libraries for IlmBase +-``IlmBase_{COMPONENT}_DLL`` +- Individual component dlls for IlmBase on Windows. +- +-Hints +-^^^^^ +- +-Instead of explicitly setting the cache variables, the following variables +-may be provided to tell this module where to look. +- +-``ILMBASE_ROOT`` +- Preferred installation prefix. +-``ILMBASE_INCLUDEDIR`` +- Preferred include directory e.g. /include +-``ILMBASE_LIBRARYDIR`` +- Preferred library directory e.g. /lib +-``SYSTEM_LIBRARY_PATHS`` +- Paths appended to all include and lib searches. +- +-#]=======================================================================] +- +-# Support new if() IN_LIST operator +-if(POLICY CMP0057) +- cmake_policy(SET CMP0057 NEW) +-endif() +- +-mark_as_advanced( +- IlmBase_INCLUDE_DIR +- IlmBase_LIBRARY +-) +- +-set(_ILMBASE_COMPONENT_LIST +- Half +- Iex +- IexMath +- IlmThread +- Imath +-) +- +-if(IlmBase_FIND_COMPONENTS) +- set(ILMBASE_COMPONENTS_PROVIDED TRUE) +- set(_IGNORED_COMPONENTS "") +- foreach(COMPONENT ${IlmBase_FIND_COMPONENTS}) +- if(NOT ${COMPONENT} IN_LIST _ILMBASE_COMPONENT_LIST) +- list(APPEND _IGNORED_COMPONENTS ${COMPONENT}) +- endif() +- endforeach() +- +- if(_IGNORED_COMPONENTS) +- message(STATUS "Ignoring unknown components of IlmBase:") +- foreach(COMPONENT ${_IGNORED_COMPONENTS}) +- message(STATUS " ${COMPONENT}") +- endforeach() +- list(REMOVE_ITEM IlmBase_FIND_COMPONENTS ${_IGNORED_COMPONENTS}) +- endif() +-else() +- set(ILMBASE_COMPONENTS_PROVIDED FALSE) +- set(IlmBase_FIND_COMPONENTS ${_ILMBASE_COMPONENT_LIST}) +-endif() +- +-# Append ILMBASE_ROOT or $ENV{ILMBASE_ROOT} if set (prioritize the direct cmake var) +-set(_ILMBASE_ROOT_SEARCH_DIR "") +- +-if(ILMBASE_ROOT) +- list(APPEND _ILMBASE_ROOT_SEARCH_DIR ${ILMBASE_ROOT}) +-else() +- set(_ENV_ILMBASE_ROOT $ENV{ILMBASE_ROOT}) +- if(_ENV_ILMBASE_ROOT) +- list(APPEND _ILMBASE_ROOT_SEARCH_DIR ${_ENV_ILMBASE_ROOT}) +- endif() +-endif() +- +-# Additionally try and use pkconfig to find IlmBase +- +-find_package(PkgConfig) +-pkg_check_modules(PC_IlmBase QUIET IlmBase) +- +-# ------------------------------------------------------------------------ +-# Search for IlmBase include DIR +-# ------------------------------------------------------------------------ +- +-set(_ILMBASE_INCLUDE_SEARCH_DIRS "") +-list(APPEND _ILMBASE_INCLUDE_SEARCH_DIRS +- ${ILMBASE_INCLUDEDIR} +- ${_ILMBASE_ROOT_SEARCH_DIR} +- ${PC_IlmBase_INCLUDEDIR} +- ${SYSTEM_LIBRARY_PATHS} +-) +- +-# Look for a standard IlmBase header file. +-find_path(IlmBase_INCLUDE_DIR IlmBaseConfig.h +- NO_DEFAULT_PATH +- PATHS ${_ILMBASE_INCLUDE_SEARCH_DIRS} +- PATH_SUFFIXES include/OpenEXR OpenEXR +-) +- +-if(EXISTS "${IlmBase_INCLUDE_DIR}/IlmBaseConfig.h") +- # Get the ILMBASE version information from the config header +- file(STRINGS "${IlmBase_INCLUDE_DIR}/IlmBaseConfig.h" +- _ilmbase_version_major_string REGEX "#define ILMBASE_VERSION_MAJOR " +- ) +- string(REGEX REPLACE "#define ILMBASE_VERSION_MAJOR" "" +- _ilmbase_version_major_string "${_ilmbase_version_major_string}" +- ) +- string(STRIP "${_ilmbase_version_major_string}" IlmBase_VERSION_MAJOR) +- +- file(STRINGS "${IlmBase_INCLUDE_DIR}/IlmBaseConfig.h" +- _ilmbase_version_minor_string REGEX "#define ILMBASE_VERSION_MINOR " +- ) +- string(REGEX REPLACE "#define ILMBASE_VERSION_MINOR" "" +- _ilmbase_version_minor_string "${_ilmbase_version_minor_string}" +- ) +- string(STRIP "${_ilmbase_version_minor_string}" IlmBase_VERSION_MINOR) +- +- unset(_ilmbase_version_major_string) +- unset(_ilmbase_version_minor_string) +- +- set(IlmBase_VERSION ${IlmBase_VERSION_MAJOR}.${IlmBase_VERSION_MINOR}) +-endif() +- +-# ------------------------------------------------------------------------ +-# Search for ILMBASE lib DIR +-# ------------------------------------------------------------------------ +- +-set(_ILMBASE_LIBRARYDIR_SEARCH_DIRS "") +- +-# Append to _ILMBASE_LIBRARYDIR_SEARCH_DIRS in priority order +- +-list(APPEND _ILMBASE_LIBRARYDIR_SEARCH_DIRS +- ${ILMBASE_LIBRARYDIR} +- ${_ILMBASE_ROOT_SEARCH_DIR} +- ${PC_IlmBase_LIBDIR} +- ${SYSTEM_LIBRARY_PATHS} +-) +- +-# Build suffix directories +- +-set(ILMBASE_PATH_SUFFIXES +- lib64 +- lib +-) +- +-if(UNIX) +- list(INSERT ILMBASE_PATH_SUFFIXES 0 lib/x86_64-linux-gnu) +-endif() +- +-set(_ILMBASE_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) +- +-# library suffix handling +-if(WIN32) +- list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES +- "-${IlmBase_VERSION_MAJOR}_${IlmBase_VERSION_MINOR}.lib" +- ) +-else() +- if(ILMBASE_USE_STATIC_LIBS) +- list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES +- "-${IlmBase_VERSION_MAJOR}_${IlmBase_VERSION_MINOR}.a" +- ) +- else() +- if(APPLE) +- list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES +- "-${IlmBase_VERSION_MAJOR}_${IlmBase_VERSION_MINOR}.dylib" +- ) +- else() +- list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES +- "-${IlmBase_VERSION_MAJOR}_${IlmBase_VERSION_MINOR}.so" +- ) +- endif() +- endif() +-endif() +- +-set(IlmBase_LIB_COMPONENTS "") +- +-foreach(COMPONENT ${IlmBase_FIND_COMPONENTS}) +- find_library(IlmBase_${COMPONENT}_LIBRARY ${COMPONENT} +- NO_DEFAULT_PATH +- PATHS ${_ILMBASE_LIBRARYDIR_SEARCH_DIRS} +- PATH_SUFFIXES ${ILMBASE_PATH_SUFFIXES} +- ) +- list(APPEND IlmBase_LIB_COMPONENTS ${IlmBase_${COMPONENT}_LIBRARY}) +- +- if(WIN32 AND NOT ILMBASE_USE_STATIC_LIBS) +- set(_ILMBASE_TMP ${CMAKE_FIND_LIBRARY_SUFFIXES}) +- set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll") +- find_library(IlmBase_${COMPONENT}_DLL ${COMPONENT} +- NO_DEFAULT_PATH +- PATHS ${_ILMBASE_LIBRARYDIR_SEARCH_DIRS} +- PATH_SUFFIXES bin +- ) +- set(CMAKE_FIND_LIBRARY_SUFFIXES ${_ILMBASE_TMP}) +- unset(_ILMBASE_TMP) +- endif() +- +- if(IlmBase_${COMPONENT}_LIBRARY) +- set(IlmBase_${COMPONENT}_FOUND TRUE) +- else() +- set(IlmBase_${COMPONENT}_FOUND FALSE) +- endif() +-endforeach() +- +-# reset lib suffix +- +-set(CMAKE_FIND_LIBRARY_SUFFIXES ${_ILMBASE_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) +-unset(_ILMBASE_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES) +- +-# ------------------------------------------------------------------------ +-# Cache and set ILMBASE_FOUND +-# ------------------------------------------------------------------------ +- +-include(FindPackageHandleStandardArgs) +-find_package_handle_standard_args(IlmBase +- FOUND_VAR IlmBase_FOUND +- REQUIRED_VARS +- IlmBase_INCLUDE_DIR +- IlmBase_LIB_COMPONENTS +- VERSION_VAR IlmBase_VERSION +- HANDLE_COMPONENTS +-) +- +-if(IlmBase_FOUND) +- set(IlmBase_LIBRARIES ${IlmBase_LIB_COMPONENTS}) +- +- # We have to add both include and include/OpenEXR to the include +- # path in case OpenEXR and IlmBase are installed separately +- +- set(IlmBase_INCLUDE_DIRS) +- list(APPEND IlmBase_INCLUDE_DIRS +- ${IlmBase_INCLUDE_DIR}/../ +- ${IlmBase_INCLUDE_DIR} +- ) +- set(IlmBase_DEFINITIONS ${PC_IlmBase_CFLAGS_OTHER}) +- +- set(IlmBase_LIBRARY_DIRS "") +- foreach(LIB ${IlmBase_LIB_COMPONENTS}) +- get_filename_component(_ILMBASE_LIBDIR ${LIB} DIRECTORY) +- list(APPEND IlmBase_LIBRARY_DIRS ${_ILMBASE_LIBDIR}) +- endforeach() +- list(REMOVE_DUPLICATES IlmBase_LIBRARY_DIRS) +- +- # Configure imported targets +- +- foreach(COMPONENT ${IlmBase_FIND_COMPONENTS}) +- if(NOT TARGET IlmBase::${COMPONENT}) +- add_library(IlmBase::${COMPONENT} UNKNOWN IMPORTED) +- set_target_properties(IlmBase::${COMPONENT} PROPERTIES +- IMPORTED_LOCATION "${IlmBase_${COMPONENT}_LIBRARY}" +- INTERFACE_COMPILE_OPTIONS "${IlmBase_DEFINITIONS}" +- INTERFACE_INCLUDE_DIRECTORIES "${IlmBase_INCLUDE_DIRS}" +- ) +- endif() +- endforeach() +- +-elseif(IlmBase_FIND_REQUIRED) +- message(FATAL_ERROR "Unable to find IlmBase") +-endif() +diff --git a/cmake/FindOpenEXR.cmake b/cmake/FindOpenEXR.cmake +deleted file mode 100644 +index 339c1a2..0000000 +--- a/cmake/FindOpenEXR.cmake ++++ /dev/null +@@ -1,329 +0,0 @@ +-# Copyright (c) DreamWorks Animation LLC +-# +-# All rights reserved. This software is distributed under the +-# Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) +-# +-# Redistributions of source code must retain the above copyright +-# and license notice and the following restrictions and disclaimer. +-# +-# * 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. +-# +-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, +-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +-# 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. +-# +-#[=======================================================================[.rst: +- +-FindOpenEXR +------------ +- +-Find OpenEXR include dirs and libraries +- +-Use this module by invoking find_package with the form:: +- +- find_package(OpenEXR +- [version] [EXACT] # Minimum or EXACT version +- [REQUIRED] # Fail with error if OpenEXR is not found +- [COMPONENTS ...] # OpenEXR libraries by their canonical name +- # e.g. "IlmImf" for "libIlmImf" +- ) +- +-IMPORTED Targets +-^^^^^^^^^^^^^^^^ +- +-``OpenEXR::IlmImf`` +- The IlmImf library target. +-``OpenEXR::IlmImfUtil`` +- The IlmImfUtil library target. +- +-Result Variables +-^^^^^^^^^^^^^^^^ +- +-This will define the following variables: +- +-``OpenEXR_FOUND`` +- True if the system has the OpenEXR library. +-``OpenEXR_VERSION`` +- The version of the OpenEXR library which was found. +-``OpenEXR_INCLUDE_DIRS`` +- Include directories needed to use OpenEXR. +-``OpenEXR_LIBRARIES`` +- Libraries needed to link to OpenEXR. +-``OpenEXR_LIBRARY_DIRS`` +- OpenEXR library directories. +-``OpenEXR_DEFINITIONS`` +- Definitions to use when compiling code that uses OpenEXR. +-``OpenEXR_{COMPONENT}_FOUND`` +- True if the system has the named OpenEXR component. +- +-Cache Variables +-^^^^^^^^^^^^^^^ +- +-The following cache variables may also be set: +- +-``OpenEXR_INCLUDE_DIR`` +- The directory containing ``OpenEXR/config-auto.h``. +-``OpenEXR_{COMPONENT}_LIBRARY`` +- Individual component libraries for OpenEXR +-``OpenEXR_{COMPONENT}_DLL`` +- Individual component dlls for OpenEXR on Windows. +- +-Hints +-^^^^^ +- +-Instead of explicitly setting the cache variables, the following variables +-may be provided to tell this module where to look. +- +-``OPENEXR_ROOT`` +- Preferred installation prefix. +-``OPENEXR_INCLUDEDIR`` +- Preferred include directory e.g. /include +-``OPENEXR_LIBRARYDIR`` +- Preferred library directory e.g. /lib +-``SYSTEM_LIBRARY_PATHS`` +- Paths appended to all include and lib searches. +- +-#]=======================================================================] +- +-# Support new if() IN_LIST operator +-if(POLICY CMP0057) +- cmake_policy(SET CMP0057 NEW) +-endif() +- +-mark_as_advanced( +- OpenEXR_INCLUDE_DIR +- OpenEXR_LIBRARY +-) +- +-set(_OPENEXR_COMPONENT_LIST +- IlmImf +- IlmImfUtil +-) +- +-if(OpenEXR_FIND_COMPONENTS) +- set(OPENEXR_COMPONENTS_PROVIDED TRUE) +- set(_IGNORED_COMPONENTS "") +- foreach(COMPONENT ${OpenEXR_FIND_COMPONENTS}) +- if(NOT ${COMPONENT} IN_LIST _OPENEXR_COMPONENT_LIST) +- list(APPEND _IGNORED_COMPONENTS ${COMPONENT}) +- endif() +- endforeach() +- +- if(_IGNORED_COMPONENTS) +- message(STATUS "Ignoring unknown components of OpenEXR:") +- foreach(COMPONENT ${_IGNORED_COMPONENTS}) +- message(STATUS " ${COMPONENT}") +- endforeach() +- list(REMOVE_ITEM OpenEXR_FIND_COMPONENTS ${_IGNORED_COMPONENTS}) +- endif() +-else() +- set(OPENEXR_COMPONENTS_PROVIDED FALSE) +- set(OpenEXR_FIND_COMPONENTS ${_OPENEXR_COMPONENT_LIST}) +-endif() +- +-# Append OPENEXR_ROOT or $ENV{OPENEXR_ROOT} if set (prioritize the direct cmake var) +-set(_OPENEXR_ROOT_SEARCH_DIR "") +- +-if(OPENEXR_ROOT) +- list(APPEND _OPENEXR_ROOT_SEARCH_DIR ${OPENEXR_ROOT}) +-else() +- set(_ENV_OPENEXR_ROOT $ENV{OPENEXR_ROOT}) +- if(_ENV_OPENEXR_ROOT) +- list(APPEND _OPENEXR_ROOT_SEARCH_DIR ${_ENV_OPENEXR_ROOT}) +- endif() +-endif() +- +-# Additionally try and use pkconfig to find OpenEXR +- +-find_package(PkgConfig) +-pkg_check_modules(PC_OpenEXR QUIET OpenEXR) +- +-# ------------------------------------------------------------------------ +-# Search for OpenEXR include DIR +-# ------------------------------------------------------------------------ +- +-set(_OPENEXR_INCLUDE_SEARCH_DIRS "") +-list(APPEND _OPENEXR_INCLUDE_SEARCH_DIRS +- ${OPENEXR_INCLUDEDIR} +- ${_OPENEXR_ROOT_SEARCH_DIR} +- ${PC_OpenEXR_INCLUDEDIR} +- ${SYSTEM_LIBRARY_PATHS} +-) +- +-# Look for a standard OpenEXR header file. +-find_path(OpenEXR_INCLUDE_DIR OpenEXRConfig.h +- NO_DEFAULT_PATH +- PATHS ${_OPENEXR_INCLUDE_SEARCH_DIRS} +- PATH_SUFFIXES include/OpenEXR OpenEXR +-) +- +-if(EXISTS "${OpenEXR_INCLUDE_DIR}/OpenEXRConfig.h") +- # Get the EXR version information from the config header +- file(STRINGS "${OpenEXR_INCLUDE_DIR}/OpenEXRConfig.h" +- _openexr_version_major_string REGEX "#define OPENEXR_VERSION_MAJOR " +- ) +- string(REGEX REPLACE "#define OPENEXR_VERSION_MAJOR" "" +- _openexr_version_major_string "${_openexr_version_major_string}" +- ) +- string(STRIP "${_openexr_version_major_string}" OpenEXR_VERSION_MAJOR) +- +- file(STRINGS "${OpenEXR_INCLUDE_DIR}/OpenEXRConfig.h" +- _openexr_version_minor_string REGEX "#define OPENEXR_VERSION_MINOR " +- ) +- string(REGEX REPLACE "#define OPENEXR_VERSION_MINOR" "" +- _openexr_version_minor_string "${_openexr_version_minor_string}" +- ) +- string(STRIP "${_openexr_version_minor_string}" OpenEXR_VERSION_MINOR) +- +- unset(_openexr_version_major_string) +- unset(_openexr_version_minor_string) +- +- set(OpenEXR_VERSION ${OpenEXR_VERSION_MAJOR}.${OpenEXR_VERSION_MINOR}) +-endif() +- +-# ------------------------------------------------------------------------ +-# Search for OPENEXR lib DIR +-# ------------------------------------------------------------------------ +- +-set(_OPENEXR_LIBRARYDIR_SEARCH_DIRS "") +- +-# Append to _OPENEXR_LIBRARYDIR_SEARCH_DIRS in priority order +- +-list(APPEND _OPENEXR_LIBRARYDIR_SEARCH_DIRS +- ${OPENEXR_LIBRARYDIR} +- ${_OPENEXR_ROOT_SEARCH_DIR} +- ${PC_OpenEXR_LIBDIR} +- ${SYSTEM_LIBRARY_PATHS} +-) +- +-# Build suffix directories +- +-set(OPENEXR_PATH_SUFFIXES +- lib64 +- lib +-) +- +-if(UNIX ) +- list(INSERT OPENEXR_PATH_SUFFIXES 0 lib/x86_64-linux-gnu) +-endif() +- +-set(_OPENEXR_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) +- +-# library suffix handling +-if(WIN32) +- list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES +- "-${OpenEXR_VERSION_MAJOR}_${OpenEXR_VERSION_MINOR}.lib" +- ) +-else() +- if(OPENEXR_USE_STATIC_LIBS) +- list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES +- "-${OpenEXR_VERSION_MAJOR}_${OpenEXR_VERSION_MINOR}.a" +- ) +- else() +- if(APPLE) +- list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES +- "-${OpenEXR_VERSION_MAJOR}_${OpenEXR_VERSION_MINOR}.dylib" +- ) +- else() +- list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES +- "-${OpenEXR_VERSION_MAJOR}_${OpenEXR_VERSION_MINOR}.so" +- ) +- endif() +- endif() +-endif() +- +-set(OpenEXR_LIB_COMPONENTS "") +- +-foreach(COMPONENT ${OpenEXR_FIND_COMPONENTS}) +- find_library(OpenEXR_${COMPONENT}_LIBRARY ${COMPONENT} +- NO_DEFAULT_PATH +- PATHS ${_OPENEXR_LIBRARYDIR_SEARCH_DIRS} +- PATH_SUFFIXES ${OPENEXR_PATH_SUFFIXES} +- ) +- list(APPEND OpenEXR_LIB_COMPONENTS ${OpenEXR_${COMPONENT}_LIBRARY}) +- +- if(WIN32 AND NOT OPENEXR_USE_STATIC_LIBS) +- set(_OPENEXR_TMP ${CMAKE_FIND_LIBRARY_SUFFIXES}) +- set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll") +- find_library(OpenEXR_${COMPONENT}_DLL ${COMPONENT} +- NO_DEFAULT_PATH +- PATHS ${_OPENEXR_LIBRARYDIR_SEARCH_DIRS} +- PATH_SUFFIXES bin +- ) +- set(CMAKE_FIND_LIBRARY_SUFFIXES ${_OPENEXR_TMP}) +- unset(_OPENEXR_TMP) +- endif() +- +- if(OpenEXR_${COMPONENT}_LIBRARY) +- set(OpenEXR_${COMPONENT}_FOUND TRUE) +- else() +- set(OpenEXR_${COMPONENT}_FOUND FALSE) +- endif() +-endforeach() +- +-# reset lib suffix +- +-set(CMAKE_FIND_LIBRARY_SUFFIXES ${_OPENEXR_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) +-unset(_OPENEXR_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES) +- +-# ------------------------------------------------------------------------ +-# Cache and set OPENEXR_FOUND +-# ------------------------------------------------------------------------ +- +-include(FindPackageHandleStandardArgs) +-find_package_handle_standard_args(OpenEXR +- FOUND_VAR OpenEXR_FOUND +- REQUIRED_VARS +- OpenEXR_INCLUDE_DIR +- OpenEXR_LIB_COMPONENTS +- VERSION_VAR OpenEXR_VERSION +- HANDLE_COMPONENTS +-) +- +-if(OpenEXR_FOUND) +- set(OpenEXR_LIBRARIES ${OpenEXR_LIB_COMPONENTS}) +- +- # We have to add both include and include/OpenEXR to the include +- # path in case OpenEXR and IlmBase are installed separately +- +- set(OpenEXR_INCLUDE_DIRS) +- list(APPEND OpenEXR_INCLUDE_DIRS +- ${OpenEXR_INCLUDE_DIR}/../ +- ${OpenEXR_INCLUDE_DIR} +- ) +- set(OpenEXR_DEFINITIONS ${PC_OpenEXR_CFLAGS_OTHER}) +- +- set(OpenEXR_LIBRARY_DIRS "") +- foreach(LIB ${OpenEXR_LIB_COMPONENTS}) +- get_filename_component(_OPENEXR_LIBDIR ${LIB} DIRECTORY) +- list(APPEND OpenEXR_LIBRARY_DIRS ${_OPENEXR_LIBDIR}) +- endforeach() +- list(REMOVE_DUPLICATES OpenEXR_LIBRARY_DIRS) +- +- # Configure imported target +- +- foreach(COMPONENT ${OpenEXR_FIND_COMPONENTS}) +- if(NOT TARGET OpenEXR::${COMPONENT}) +- add_library(OpenEXR::${COMPONENT} UNKNOWN IMPORTED) +- set_target_properties(OpenEXR::${COMPONENT} PROPERTIES +- IMPORTED_LOCATION "${OpenEXR_${COMPONENT}_LIBRARY}" +- INTERFACE_COMPILE_OPTIONS "${OpenEXR_DEFINITIONS}" +- INTERFACE_INCLUDE_DIRECTORIES "${OpenEXR_INCLUDE_DIRS}" +- ) +- endif() +- endforeach() +-elseif(OpenEXR_FIND_REQUIRED) +- message(FATAL_ERROR "Unable to find OpenEXR") +-endif() +diff --git a/cmake/FindOpenVDB.cmake b/cmake/FindOpenVDB.cmake +index 63a2eda..6211071 100644 +--- a/cmake/FindOpenVDB.cmake ++++ b/cmake/FindOpenVDB.cmake +@@ -244,7 +244,7 @@ set(OpenVDB_LIB_COMPONENTS "") + + foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS}) + set(LIB_NAME ${COMPONENT}) +- find_library(OpenVDB_${COMPONENT}_LIBRARY ${LIB_NAME} ++ find_library(OpenVDB_${COMPONENT}_LIBRARY ${LIB_NAME} lib${LIB_NAME} + NO_DEFAULT_PATH + PATHS ${_OPENVDB_LIBRARYDIR_SEARCH_DIRS} + PATH_SUFFIXES ${OPENVDB_PATH_SUFFIXES} +@@ -282,16 +282,13 @@ find_package_handle_standard_args(OpenVDB + # ------------------------------------------------------------------------ + + # Set the ABI number the library was built against. Uses vdb_print ++find_program(OPENVDB_PRINT vdb_print ++ PATHS ${_OPENVDB_INSTALL}/bin ${OpenVDB_INCLUDE_DIR} ++ NO_DEFAULT_PATH) + + if(_OPENVDB_INSTALL) + OPENVDB_ABI_VERSION_FROM_PRINT( +- "${_OPENVDB_INSTALL}/bin/vdb_print" +- ABI OpenVDB_ABI +- ) +-else() +- # Try and find vdb_print from the include path +- OPENVDB_ABI_VERSION_FROM_PRINT( +- "${OpenVDB_INCLUDE_DIR}/../bin/vdb_print" ++ "${OPENVDB_PRINT}" + ABI OpenVDB_ABI + ) + endif() +@@ -472,6 +469,12 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS}) + INTERFACE_LINK_LIBRARIES "${_OPENVDB_VISIBLE_DEPENDENCIES}" # visible deps (headers) + INTERFACE_COMPILE_FEATURES cxx_std_11 + ) ++ ++ if (OPENVDB_USE_STATIC_LIBS) ++ set_target_properties(OpenVDB::${COMPONENT} PROPERTIES ++ INTERFACE_COMPILE_DEFINITIONS "OPENVDB_STATICLIB;OPENVDB_OPENEXR_STATICLIB" ++ ) ++ endif() + endif() + endforeach() + +diff --git a/cmake/FindTBB.cmake b/cmake/FindTBB.cmake +index bdf9c81..c6bdec9 100644 +--- a/cmake/FindTBB.cmake ++++ b/cmake/FindTBB.cmake +@@ -1,333 +1,332 @@ +-# Copyright (c) DreamWorks Animation LLC ++# The MIT License (MIT) + # +-# 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 ++# ++# 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. + # +-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, +-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +-# 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: + # +-#[=======================================================================[.rst: +- +-FindTBB +-------- +- +-Find Tbb include dirs and libraries +- +-Use this module by invoking find_package with the form:: +- +- find_package(TBB +- [version] [EXACT] # Minimum or EXACT version +- [REQUIRED] # Fail with error if Tbb is not found +- [COMPONENTS ...] # Tbb libraries by their canonical name +- # e.g. "tbb" for "libtbb" +- ) +- +-IMPORTED Targets +-^^^^^^^^^^^^^^^^ +- +-``TBB::tbb`` +- The tbb library target. +-``TBB::tbbmalloc`` +- The tbbmalloc library target. +-``TBB::tbbmalloc_proxy`` +- The tbbmalloc_proxy library target. +- +-Result Variables +-^^^^^^^^^^^^^^^^ +- +-This will define the following variables: +- +-``Tbb_FOUND`` +- True if the system has the Tbb library. +-``Tbb_VERSION`` +- The version of the Tbb library which was found. +-``Tbb_INCLUDE_DIRS`` +- Include directories needed to use Tbb. +-``Tbb_LIBRARIES`` +- Libraries needed to link to Tbb. +-``Tbb_LIBRARY_DIRS`` +- Tbb library directories. +-``TBB_{COMPONENT}_FOUND`` +- True if the system has the named TBB component. +- +-Cache Variables +-^^^^^^^^^^^^^^^ +- +-The following cache variables may also be set: +- +-``Tbb_INCLUDE_DIR`` +- The directory containing ``tbb/tbb_stddef.h``. +-``Tbb_{COMPONENT}_LIBRARY`` +- Individual component libraries for Tbb +- +-Hints +-^^^^^ +- +-Instead of explicitly setting the cache variables, the following variables +-may be provided to tell this module where to look. +- +-``TBB_ROOT`` +- Preferred installation prefix. +-``TBB_INCLUDEDIR`` +- Preferred include directory e.g. /include +-``TBB_LIBRARYDIR`` +- Preferred library directory e.g. /lib +-``SYSTEM_LIBRARY_PATHS`` +- Paths appended to all include and lib searches. +- +-#]=======================================================================] +- +-# Support new if() IN_LIST operator +-if(POLICY CMP0057) +- cmake_policy(SET CMP0057 NEW) +-endif() ++# find_package(TBB [major[.minor]] [EXACT] ++# [QUIET] [REQUIRED] ++# [[COMPONENTS] [components...]] ++# [OPTIONAL_COMPONENTS components...]) ++# ++# 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 ++# corresponding library search results, where ++# may be tbb, tbb_debug, tbbmalloc, tbbmalloc_debug, ++# tbb_preview, or tbb_preview_debug. ++# * TBB_USE_DEBUG_BUILD - The debug version of tbb libraries, if present, will ++# be used instead of the release version. ++# * TBB_STATIC - Static linking of libraries with a _static suffix. ++# For example, on Windows a tbb_static.lib will be searched for ++# instead of tbb.lib. ++# ++# Users may modify the behavior of this module with the following environment ++# variables: ++# ++# * TBB_INSTALL_DIR ++# * TBBROOT ++# * LIBRARY_PATH ++# ++# This module will set the following variables: ++# ++# * TBB_FOUND - Set to false, or undefined, if we haven’t found, or ++# don’t want to use TBB. ++# * TBB__FOUND - If False, optional part of TBB sytem is ++# not available. ++# * 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/tbb_stddef.h. ++# * TBB__LIBRARY_RELEASE - The path of the TBB release version of ++# , where may be tbb, tbb_debug, ++# tbbmalloc, tbbmalloc_debug, tbb_preview, or ++# tbb_preview_debug. ++# * TBB__LIBRARY_DEGUG - The path of the TBB release version of ++# , where may be tbb, tbb_debug, ++# tbbmalloc, tbbmalloc_debug, tbb_preview, or ++# tbb_preview_debug. ++# ++# The following varibles should be used to build and link with TBB: ++# ++# * TBB_INCLUDE_DIRS - The include directory for TBB. ++# * TBB_LIBRARIES - The libraries to link against to use TBB. ++# * TBB_LIBRARIES_RELEASE - The release libraries to link against to use TBB. ++# * TBB_LIBRARIES_DEBUG - The debug libraries to link against to use TBB. ++# * TBB_DEFINITIONS - Definitions to use when compiling code that uses ++# TBB. ++# * TBB_DEFINITIONS_RELEASE - Definitions to use when compiling release code that ++# uses TBB. ++# * TBB_DEFINITIONS_DEBUG - Definitions to use when compiling debug code that ++# uses TBB. ++# ++# This module will also create the "tbb" target that may be used when building ++# executables and libraries. + +-mark_as_advanced( +- Tbb_INCLUDE_DIR +- Tbb_LIBRARY +-) +- +-set(_TBB_COMPONENT_LIST +- tbb +- tbbmalloc +- tbbmalloc_proxy +-) +- +-if(TBB_FIND_COMPONENTS) +- set(_TBB_COMPONENTS_PROVIDED TRUE) +- set(_IGNORED_COMPONENTS "") +- foreach(COMPONENT ${TBB_FIND_COMPONENTS}) +- if(NOT ${COMPONENT} IN_LIST _TBB_COMPONENT_LIST) +- list(APPEND _IGNORED_COMPONENTS ${COMPONENT}) +- endif() +- endforeach() ++unset(TBB_FOUND CACHE) ++unset(TBB_INCLUDE_DIRS CACHE) ++unset(TBB_LIBRARIES) ++unset(TBB_LIBRARIES_DEBUG) ++unset(TBB_LIBRARIES_RELEASE) + +- if(_IGNORED_COMPONENTS) +- message(STATUS "Ignoring unknown components of TBB:") +- foreach(COMPONENT ${_IGNORED_COMPONENTS}) +- message(STATUS " ${COMPONENT}") +- endforeach() +- list(REMOVE_ITEM TBB_FIND_COMPONENTS ${_IGNORED_COMPONENTS}) +- endif() +-else() +- set(_TBB_COMPONENTS_PROVIDED FALSE) +- 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) + +-if(TBB_ROOT) +- list(APPEND _TBB_ROOT_SEARCH_DIR ${TBB_ROOT}) +-else() +- 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() ++ ++ ################################## ++ # Set the TBB search directories ++ ################################## ++ ++ # Define search paths based on user input and environment variables ++ set(TBB_SEARCH_DIR ${TBB_ROOT_DIR} $ENV{TBB_INSTALL_DIR} $ENV{TBBROOT}) ++ ++ # Define the search directories based on the current platform ++ if(CMAKE_SYSTEM_NAME STREQUAL "Windows") ++ set(TBB_DEFAULT_SEARCH_DIR "C:/Program Files/Intel/TBB" ++ "C:/Program Files (x86)/Intel/TBB") ++ ++ # Set the target architecture ++ if(CMAKE_SIZEOF_VOID_P EQUAL 8) ++ set(TBB_ARCHITECTURE "intel64") ++ else() ++ set(TBB_ARCHITECTURE "ia32") ++ endif() + +-# Additionally try and use pkconfig to find Tbb +- +-find_package(PkgConfig) +-pkg_check_modules(PC_Tbb QUIET tbb) +- +-# ------------------------------------------------------------------------ +-# Search for tbb include DIR +-# ------------------------------------------------------------------------ +- +-set(_TBB_INCLUDE_SEARCH_DIRS "") +-list(APPEND _TBB_INCLUDE_SEARCH_DIRS +- ${TBB_INCLUDEDIR} +- ${_TBB_ROOT_SEARCH_DIR} +- ${PC_Tbb_INCLUDE_DIRS} +- ${SYSTEM_LIBRARY_PATHS} +-) +- +-# Look for a standard tbb header file. +-find_path(Tbb_INCLUDE_DIR tbb/tbb_stddef.h +- NO_DEFAULT_PATH +- PATHS ${_TBB_INCLUDE_SEARCH_DIRS} +- PATH_SUFFIXES include +-) +- +-if(EXISTS "${Tbb_INCLUDE_DIR}/tbb/tbb_stddef.h") +- file(STRINGS "${Tbb_INCLUDE_DIR}/tbb/tbb_stddef.h" +- _tbb_version_major_string REGEX "#define TBB_VERSION_MAJOR " +- ) +- string(REGEX REPLACE "#define TBB_VERSION_MAJOR" "" +- _tbb_version_major_string "${_tbb_version_major_string}" +- ) +- string(STRIP "${_tbb_version_major_string}" Tbb_VERSION_MAJOR) +- +- file(STRINGS "${Tbb_INCLUDE_DIR}/tbb/tbb_stddef.h" +- _tbb_version_minor_string REGEX "#define TBB_VERSION_MINOR " +- ) +- string(REGEX REPLACE "#define TBB_VERSION_MINOR" "" +- _tbb_version_minor_string "${_tbb_version_minor_string}" +- ) +- string(STRIP "${_tbb_version_minor_string}" Tbb_VERSION_MINOR) +- +- unset(_tbb_version_major_string) +- unset(_tbb_version_minor_string) +- +- set(Tbb_VERSION ${Tbb_VERSION_MAJOR}.${Tbb_VERSION_MINOR}) +-endif() ++ # 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") ++ elseif(MSVC14) ++ set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc14") ++ elseif(MSVC12) ++ set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc12") ++ elseif(MSVC11) ++ set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11") ++ elseif(MSVC10) ++ set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc10") ++ endif() + +-# ------------------------------------------------------------------------ +-# Search for TBB lib DIR +-# ------------------------------------------------------------------------ ++ # 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}) + +-if(APPLE) +- if(TBB_FOR_CLANG) +- list(INSERT TBB_PATH_SUFFIXES 0 lib/libc++) +- endif() +-elseif(WIN32) +- if(MSVC10) +- set(TBB_VC_DIR vc10) +- elseif(MSVC11) +- set(TBB_VC_DIR vc11) +- elseif(MSVC12) +- set(TBB_VC_DIR vc12) +- endif() +- list(INSERT TBB_PATH_SUFFIXES 0 lib/intel64/${TBB_VC_DIR}) +-else() +- if(${CMAKE_CXX_COMPILER_ID} STREQUAL GNU) +- if(TBB_MATCH_COMPILER_VERSION) +- string(REGEX MATCHALL "[0-9]+" GCC_VERSION_COMPONENTS ${CMAKE_CXX_COMPILER_VERSION}) +- 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() +- list(INSERT TBB_PATH_SUFFIXES 0 lib/intel64/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 "") +- +-foreach(COMPONENT ${TBB_FIND_COMPONENTS}) +- find_library(Tbb_${COMPONENT}_LIBRARY ${COMPONENT} +- NO_DEFAULT_PATH +- PATHS ${_TBB_LIBRARYDIR_SEARCH_DIRS} +- PATH_SUFFIXES ${TBB_PATH_SUFFIXES} +- ) +- +- # On Unix, TBB sometimes uses linker scripts instead of symlinks, so parse the linker script +- # and correct the library name if so +- if(UNIX AND EXISTS ${Tbb_${COMPONENT}_LIBRARY}) +- # Ignore files where the first four bytes equals the ELF magic number +- file(READ ${Tbb_${COMPONENT}_LIBRARY} Tbb_${COMPONENT}_HEX OFFSET 0 LIMIT 4 HEX) +- if(NOT ${Tbb_${COMPONENT}_HEX} STREQUAL "7f454c46") +- # Read the first 1024 bytes of the library and match against an "INPUT (file)" regex +- file(READ ${Tbb_${COMPONENT}_LIBRARY} Tbb_${COMPONENT}_ASCII OFFSET 0 LIMIT 1024) +- if("${Tbb_${COMPONENT}_ASCII}" MATCHES "INPUT \\(([^(]+)\\)") +- # Extract the directory and apply the matched text (in brackets) +- get_filename_component(Tbb_${COMPONENT}_DIR "${Tbb_${COMPONENT}_LIBRARY}" DIRECTORY) +- set(Tbb_${COMPONENT}_LIBRARY "${Tbb_${COMPONENT}_DIR}/${CMAKE_MATCH_1}") ++ if(TBB_${_comp}_LIBRARY AND EXISTS "${TBB_${_comp}_LIBRARY}") ++ set(TBB_${_comp}_FOUND TRUE) ++ else() ++ set(TBB_${_comp}_FOUND FALSE) + endif() ++ ++ # Mark internal variables as advanced ++ mark_as_advanced(TBB_${_comp}_LIBRARY_RELEASE) ++ mark_as_advanced(TBB_${_comp}_LIBRARY_DEBUG) ++ mark_as_advanced(TBB_${_comp}_LIBRARY) ++ + endif() +- endif() ++ endforeach() + +- list(APPEND Tbb_LIB_COMPONENTS ${Tbb_${COMPONENT}_LIBRARY}) ++ ################################## ++ # Set compile flags and libraries ++ ################################## + +- if(Tbb_${COMPONENT}_LIBRARY) +- set(TBB_${COMPONENT}_FOUND TRUE) +- 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}) + endif() +-endforeach() + +-if(UNIX AND TBB_USE_STATIC_LIBS) +- 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() ++ endif() + +-# ------------------------------------------------------------------------ +-# Cache and set TBB_FOUND +-# ------------------------------------------------------------------------ ++ 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() + +-include(FindPackageHandleStandardArgs) +-find_package_handle_standard_args(TBB +- FOUND_VAR TBB_FOUND +- REQUIRED_VARS +- Tbb_INCLUDE_DIR +- Tbb_LIB_COMPONENTS +- VERSION_VAR Tbb_VERSION +- HANDLE_COMPONENTS +-) +- +-if(TBB_FOUND) +- set(Tbb_LIBRARIES +- ${Tbb_LIB_COMPONENTS} +- ) +- set(Tbb_INCLUDE_DIRS ${Tbb_INCLUDE_DIR}) +- set(Tbb_DEFINITIONS ${PC_Tbb_CFLAGS_OTHER}) +- +- set(Tbb_LIBRARY_DIRS "") +- foreach(LIB ${Tbb_LIB_COMPONENTS}) +- get_filename_component(_TBB_LIBDIR ${LIB} DIRECTORY) +- list(APPEND Tbb_LIBRARY_DIRS ${_TBB_LIBDIR}) +- endforeach() +- list(REMOVE_DUPLICATES Tbb_LIBRARY_DIRS) +- +- # Configure imported targets +- +- foreach(COMPONENT ${TBB_FIND_COMPONENTS}) +- if(NOT TARGET TBB::${COMPONENT}) +- add_library(TBB::${COMPONENT} UNKNOWN IMPORTED) +- set_target_properties(TBB::${COMPONENT} PROPERTIES +- IMPORTED_LOCATION "${Tbb_${COMPONENT}_LIBRARY}" +- INTERFACE_COMPILE_OPTIONS "${Tbb_DEFINITIONS}" +- INTERFACE_INCLUDE_DIRECTORIES "${Tbb_INCLUDE_DIR}" +- ) +- endif() +- endforeach() +-elseif(TBB_FIND_REQUIRED) +- message(FATAL_ERROR "Unable to find TBB") + endif() +diff --git a/openvdb/CMakeLists.txt b/openvdb/CMakeLists.txt +index 89301bd..df27aae 100644 +--- a/openvdb/CMakeLists.txt ++++ b/openvdb/CMakeLists.txt +@@ -78,7 +78,7 @@ else() + endif() + + find_package(TBB ${MINIMUM_TBB_VERSION} REQUIRED COMPONENTS tbb) +-if(${Tbb_VERSION} VERSION_LESS FUTURE_MINIMUM_TBB_VERSION) ++if(${TBB_VERSION} VERSION_LESS FUTURE_MINIMUM_TBB_VERSION) + message(DEPRECATION "Support for TBB versions < ${FUTURE_MINIMUM_TBB_VERSION} " + "is deprecated and will be removed.") + endif() +@@ -185,11 +185,6 @@ if(WIN32) + endif() + endif() + +-# @todo Should be target definitions +-if(WIN32) +- add_definitions(-D_WIN32 -DNOMINMAX -DOPENVDB_DLL) +-endif() +- + ##### Core library configuration + + set(OPENVDB_LIBRARY_SOURCE_FILES +@@ -374,10 +369,16 @@ set(OPENVDB_LIBRARY_UTIL_INCLUDE_FILES + + if(OPENVDB_CORE_SHARED) + add_library(openvdb_shared SHARED ${OPENVDB_LIBRARY_SOURCE_FILES}) ++ if(WIN32) ++ target_compile_definitions(openvdb_shared PUBLIC OPENVDB_DLL) ++ endif() + endif() + + if(OPENVDB_CORE_STATIC) + add_library(openvdb_static STATIC ${OPENVDB_LIBRARY_SOURCE_FILES}) ++ if(WIN32) ++ target_compile_definitions(openvdb_static PUBLIC OPENVDB_STATICLIB) ++ endif() + endif() + + # Alias either the shared or static library to the generic OpenVDB +diff --git a/openvdb/Grid.cc b/openvdb/Grid.cc +index 0015f81..cb6084a 100644 +--- a/openvdb/Grid.cc ++++ b/openvdb/Grid.cc +@@ -35,6 +35,9 @@ + #include + #include + ++// WTF??? Somehow from stdlib.h ++#undef min ++#undef max + + namespace openvdb { + OPENVDB_USE_VERSION_NAMESPACE +diff --git a/openvdb/PlatformConfig.h b/openvdb/PlatformConfig.h +index 20ad9a3..c2dd1ef 100644 +--- a/openvdb/PlatformConfig.h ++++ b/openvdb/PlatformConfig.h +@@ -44,9 +44,12 @@ + + // By default, assume that we're dynamically linking OpenEXR, unless + // OPENVDB_OPENEXR_STATICLIB is defined. +- #if !defined(OPENVDB_OPENEXR_STATICLIB) && !defined(OPENEXR_DLL) +- #define OPENEXR_DLL +- #endif ++ // Meszaros Tamas: Why? OpenEXR and its imported targets have OPENEXR_DLL ++ // in INTERFACE_COMPILE_DEFINITIONS if build with it. ++ // #if !defined(OPENVDB_OPENEXR_STATICLIB) && !defined(OPENEXR_DLL) ++ // #define OPENEXR_DLL ++ // static_assert(false, "This is bad: OPENEXR_DLL"); ++ // #endif + + #endif // _WIN32 + +diff --git a/openvdb/cmd/CMakeLists.txt b/openvdb/cmd/CMakeLists.txt +index 57fbec0..55b3850 100644 +--- a/openvdb/cmd/CMakeLists.txt ++++ b/openvdb/cmd/CMakeLists.txt +@@ -74,8 +74,9 @@ if(WIN32) + endif() + endif() + ++# @todo Should be target definitions + if(WIN32) +- add_definitions(-D_WIN32 -DNOMINMAX -DOPENVDB_DLL) ++ add_definitions(-D_WIN32 -DNOMINMAX) + endif() + + # rpath handling +@@ -88,7 +89,6 @@ if(OPENVDB_ENABLE_RPATH) + ${IlmBase_LIBRARY_DIRS} + ${Log4cplus_LIBRARY_DIRS} + ${Blosc_LIBRARY_DIRS} +- ${Tbb_LIBRARY_DIRS} + ) + if(OPENVDB_BUILD_CORE) + list(APPEND RPATHS ${CMAKE_INSTALL_PREFIX}/lib) +diff --git a/openvdb/unittest/CMakeLists.txt b/openvdb/unittest/CMakeLists.txt +index c9e0c34..7e261c0 100644 +--- a/openvdb/unittest/CMakeLists.txt ++++ b/openvdb/unittest/CMakeLists.txt +@@ -71,8 +71,9 @@ if(WIN32) + link_directories(${Boost_LIBRARY_DIR}) + endif() + ++# @todo Should be target definitions + if(WIN32) +- add_definitions(-D_WIN32 -DNOMINMAX -DOPENVDB_DLL) ++ add_definitions(-D_WIN32 -DNOMINMAX) + endif() + + ##### VDB unit tests +diff --git a/openvdb/unittest/TestFile.cc b/openvdb/unittest/TestFile.cc +index df51830..0ab0c12 100644 +--- a/openvdb/unittest/TestFile.cc ++++ b/openvdb/unittest/TestFile.cc +@@ -2573,7 +2573,7 @@ TestFile::testBlosc() + outdata(new char[decompbufbytes]); + + for (int compcode = 0; compcode <= BLOSC_ZLIB; ++compcode) { +- char* compname = nullptr; ++ const char* compname = nullptr; + if (0 > blosc_compcode_to_compname(compcode, &compname)) continue; + /// @todo This changes the compressor setting globally. + if (blosc_set_compressor(compname) < 0) continue; +-- +2.16.2.windows.1 + diff --git a/deps/qhull-mods.patch b/deps/qhull-mods.patch index 94aeeca2f..70f7be6a7 100644 --- a/deps/qhull-mods.patch +++ b/deps/qhull-mods.patch @@ -1,121 +1,49 @@ -From a31ae4781a4afa60e21c70e5b4ae784bcd447c8a Mon Sep 17 00:00:00 2001 +From 7f55a56b3d112f4dffbf21b1722f400c64bf03b1 Mon Sep 17 00:00:00 2001 From: tamasmeszaros -Date: Thu, 6 Jun 2019 15:41:43 +0200 -Subject: [PATCH] prusa-slicer changes +Date: Mon, 21 Oct 2019 16:52:04 +0200 +Subject: [PATCH] Fix the build on macOS --- - CMakeLists.txt | 44 +++++++++++++++++++++++++++++++++++--- - Config.cmake.in | 2 ++ - src/libqhull_r/qhull_r-exports.def | 2 ++ - src/libqhull_r/user_r.h | 2 +- - 4 files changed, 46 insertions(+), 4 deletions(-) - create mode 100644 Config.cmake.in + CMakeLists.txt | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt -index 59dff41..20c2ec5 100644 +index 07d3da2..14df8e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -61,7 +61,7 @@ - # $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $ +@@ -626,18 +626,18 @@ install(TARGETS ${qhull_TARGETS_INSTALL} EXPORT QhullTargets + include(CMakePackageConfigHelpers) - project(qhull) --cmake_minimum_required(VERSION 2.6) -+cmake_minimum_required(VERSION 3.0) + write_basic_package_version_file( +- "${CMAKE_CURRENT_BINARY_DIR}/Qhull/QhullConfigVersion.cmake" ++ "${CMAKE_CURRENT_BINARY_DIR}/QhullExport/QhullConfigVersion.cmake" + VERSION ${qhull_VERSION} + COMPATIBILITY AnyNewerVersion + ) - # Define qhull_VERSION in CMakeLists.txt, Makefile, qhull-exports.def, qhull_p-exports.def, qhull_r-exports.def, qhull-warn.pri - set(qhull_VERSION2 "2015.2 2016/01/18") # not used, See global.c, global_r.c, rbox.c, rbox_r.c -@@ -610,10 +610,48 @@ add_test(NAME user_eg3 - # Define install - # --------------------------------------- + export(EXPORT QhullTargets +- FILE "${CMAKE_CURRENT_BINARY_DIR}/Qhull/QhullTargets.cmake" ++ FILE "${CMAKE_CURRENT_BINARY_DIR}/QhullExport/QhullTargets.cmake" + NAMESPACE Qhull:: + ) --install(TARGETS ${qhull_TARGETS_INSTALL} -+install(TARGETS ${qhull_TARGETS_INSTALL} EXPORT QhullTargets - RUNTIME DESTINATION ${BIN_INSTALL_DIR} - LIBRARY DESTINATION ${LIB_INSTALL_DIR} -- ARCHIVE DESTINATION ${LIB_INSTALL_DIR}) -+ ARCHIVE DESTINATION ${LIB_INSTALL_DIR} -+ INCLUDES DESTINATION include) -+ -+include(CMakePackageConfigHelpers) -+ -+write_basic_package_version_file( -+ "${CMAKE_CURRENT_BINARY_DIR}/Qhull/QhullConfigVersion.cmake" -+ VERSION ${qhull_VERSION} -+ COMPATIBILITY AnyNewerVersion -+) -+ -+export(EXPORT QhullTargets -+ FILE "${CMAKE_CURRENT_BINARY_DIR}/Qhull/QhullTargets.cmake" -+ NAMESPACE Qhull:: -+) -+ -+configure_file(Config.cmake.in -+ "${CMAKE_CURRENT_BINARY_DIR}/Qhull/QhullConfig.cmake" -+ @ONLY -+) -+ -+set(ConfigPackageLocation lib/cmake/Qhull) -+install(EXPORT QhullTargets -+ FILE -+ QhullTargets.cmake -+ NAMESPACE -+ Qhull:: -+ DESTINATION -+ ${ConfigPackageLocation} -+) -+install( -+ FILES -+ "${CMAKE_CURRENT_BINARY_DIR}/Qhull/QhullConfig.cmake" -+ "${CMAKE_CURRENT_BINARY_DIR}/Qhull/QhullConfigVersion.cmake" -+ DESTINATION -+ ${ConfigPackageLocation} -+ COMPONENT -+ Devel -+) + configure_file(${PROJECT_SOURCE_DIR}/build/config.cmake.in +- "${CMAKE_CURRENT_BINARY_DIR}/Qhull/QhullConfig.cmake" ++ "${CMAKE_CURRENT_BINARY_DIR}/QhullExport/QhullConfig.cmake" + @ONLY + ) - install(FILES ${libqhull_HEADERS} DESTINATION ${INCLUDE_INSTALL_DIR}/libqhull) - install(FILES ${libqhull_DOC} DESTINATION ${INCLUDE_INSTALL_DIR}/libqhull) -diff --git a/Config.cmake.in b/Config.cmake.in -new file mode 100644 -index 0000000..bc92bfe ---- /dev/null -+++ b/Config.cmake.in -@@ -0,0 +1,2 @@ -+include("${CMAKE_CURRENT_LIST_DIR}/QhullTargets.cmake") -+ -diff --git a/src/libqhull_r/qhull_r-exports.def b/src/libqhull_r/qhull_r-exports.def -index 325d57c..72f6ad0 100644 ---- a/src/libqhull_r/qhull_r-exports.def -+++ b/src/libqhull_r/qhull_r-exports.def -@@ -185,6 +185,7 @@ qh_memsetup - qh_memsize - qh_memstatistics - qh_memtotal -+qh_memcheck - qh_merge_degenredundant - qh_merge_nonconvex - qh_mergecycle -@@ -372,6 +373,7 @@ qh_settruncate - qh_setunique - qh_setvoronoi_all - qh_setzero -+qh_setendpointer - qh_sharpnewfacets - qh_skipfacet - qh_skipfilename -diff --git a/src/libqhull_r/user_r.h b/src/libqhull_r/user_r.h -index fc105b9..7cca65a 100644 ---- a/src/libqhull_r/user_r.h -+++ b/src/libqhull_r/user_r.h -@@ -139,7 +139,7 @@ Code flags -- - REALfloat = 1 all numbers are 'float' type - = 0 all numbers are 'double' type - */ --#define REALfloat 0 -+#define REALfloat 1 - - #if (REALfloat == 1) - #define realT float +@@ -652,8 +652,8 @@ install(EXPORT QhullTargets + ) + install( + FILES +- "${CMAKE_CURRENT_BINARY_DIR}/Qhull/QhullConfig.cmake" +- "${CMAKE_CURRENT_BINARY_DIR}/Qhull/QhullConfigVersion.cmake" ++ "${CMAKE_CURRENT_BINARY_DIR}/QhullExport/QhullConfig.cmake" ++ "${CMAKE_CURRENT_BINARY_DIR}/QhullExport/QhullConfigVersion.cmake" + DESTINATION + ${ConfigPackageLocation} + COMPONENT -- -2.16.2.windows.1 +2.17.1 diff --git a/doc/Dependencies.md b/doc/Dependencies.md index b4b0c348c..3f6335cb7 100644 --- a/doc/Dependencies.md +++ b/doc/Dependencies.md @@ -1,6 +1,6 @@ # Dependency report for PrusaSlicer ## Possible dynamic linking on Linux -* zlib: This should not be even mentioned in our cmake scripts but due to a bug in the system libraries of gtk it has to be linked to PrusaSlicer. +* zlib: Strict dependency required from the system, linked dynamically. Many other libs depend on zlib. * wxWidgets: searches for wx-3.1 by default, but with cmake option `SLIC3R_WX_STABLE=ON` it will use wx-3.0 bundled with most distros. * libcurl * tbb @@ -10,13 +10,13 @@ * expat * openssl * nlopt -* gtest +* openvdb: This library depends on other libs, namely boost, zlib, openexr, blosc (not strictly), etc... ## External libraries in source tree * ad-mesh: Lots of customization, have to be bundled in the source tree. * avrdude: Like ad-mesh, many customization, need to be in the source tree. * clipper: An important library we have to have full control over it. We also have some slicer specific modifications. -* glu-libtess: This is an extract of the mesa/glu library not oficially available as a package. +* glu-libtess: This is an extract of the mesa/glu library not officially available as a package. * imgui: no packages for debian, author suggests using in the source tree * miniz: No packages, author suggests using in the source tree * qhull: libqhull-dev does not contain libqhullcpp => link errors. Until it is fixed, we will use the builtin version. https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=925540 @@ -29,5 +29,6 @@ * igl * nanosvg * agg +* catch2: Only Arch has packages for catch2, other distros at most catch (v1.x). Being strictly header only, we bundle this in the source tree. Used for the unit-test suites. diff --git a/doc/How to build - Linux et al.md b/doc/How to build - Linux et al.md index 67556a6f7..958ae83c3 100644 --- a/doc/How to build - Linux et al.md +++ b/doc/How to build - Linux et al.md @@ -2,7 +2,7 @@ # Building Slic3r++ on UNIX/Linux Slic3r++ uses the CMake build system and requires several dependencies. -The dependencies can be listed in `deps/deps-linux.cmake`, although they don't necessarily need to be as recent +The dependencies can be listed in `deps/deps-linux.cmake` and `deps/deps-unix-common.cmake`, although they don't necessarily need to be as recent as the versions listed - generally versions available on conservative Linux distros such as Debian stable or CentOS should suffice. Perl is not required any more. diff --git a/resources/icons/add_gcode.svg b/resources/icons/add_gcode.svg new file mode 100644 index 000000000..e2aa21adf --- /dev/null +++ b/resources/icons/add_gcode.svg @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/resources/icons/bed/ender3.svg b/resources/icons/bed/ender3.svg new file mode 100644 index 000000000..06910afdf --- /dev/null +++ b/resources/icons/bed/ender3.svg @@ -0,0 +1,47 @@ + + ender3_bed_texture + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/bed/mini.svg b/resources/icons/bed/mini.svg new file mode 100644 index 000000000..93c3437bd --- /dev/null +++ b/resources/icons/bed/mini.svg @@ -0,0 +1,70 @@ + + MINI_bed_texture + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/change_extruder.svg b/resources/icons/change_extruder.svg new file mode 100644 index 000000000..61cd4cfd8 --- /dev/null +++ b/resources/icons/change_extruder.svg @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/resources/icons/colorchange_add.svg b/resources/icons/colorchange_add.svg new file mode 100644 index 000000000..b72fddb0e --- /dev/null +++ b/resources/icons/colorchange_add.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + diff --git a/resources/icons/colorchange_add_f.svg b/resources/icons/colorchange_add_f.svg new file mode 100644 index 000000000..a96a62443 --- /dev/null +++ b/resources/icons/colorchange_add_f.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + diff --git a/resources/icons/colorchange_add_m.svg b/resources/icons/colorchange_add_m.svg new file mode 100644 index 000000000..82cd1e5f4 --- /dev/null +++ b/resources/icons/colorchange_add_m.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + diff --git a/resources/icons/colorchange_del.svg b/resources/icons/colorchange_del.svg new file mode 100644 index 000000000..0bd9e0667 --- /dev/null +++ b/resources/icons/colorchange_del.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + diff --git a/resources/icons/colorchange_del_f.svg b/resources/icons/colorchange_del_f.svg new file mode 100644 index 000000000..a54452e1f --- /dev/null +++ b/resources/icons/colorchange_del_f.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + diff --git a/resources/icons/edit_gcode.svg b/resources/icons/edit_gcode.svg new file mode 100644 index 000000000..694e106cc --- /dev/null +++ b/resources/icons/edit_gcode.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/resources/icons/edit_uni.svg b/resources/icons/edit_uni.svg new file mode 100644 index 000000000..f7b1673a6 --- /dev/null +++ b/resources/icons/edit_uni.svg @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/resources/icons/pause_add.png b/resources/icons/pause_add.png new file mode 100644 index 000000000..afe881de8 Binary files /dev/null and b/resources/icons/pause_add.png differ diff --git a/resources/icons/pause_print.svg b/resources/icons/pause_print.svg new file mode 100644 index 000000000..a905b1ea1 --- /dev/null +++ b/resources/icons/pause_print.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + diff --git a/resources/icons/printers/Creality_Ender3.png b/resources/icons/printers/Creality_Ender3.png new file mode 100644 index 000000000..52861197c Binary files /dev/null and b/resources/icons/printers/Creality_Ender3.png differ diff --git a/resources/icons/printers/PrusaResearch_MINI.png b/resources/icons/printers/PrusaResearch_MINI.png new file mode 100644 index 000000000..01780f9ca Binary files /dev/null and b/resources/icons/printers/PrusaResearch_MINI.png differ diff --git a/resources/icons/thumb_down.svg b/resources/icons/thumb_down.svg new file mode 100644 index 000000000..0499cea41 --- /dev/null +++ b/resources/icons/thumb_down.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/resources/icons/thumb_up.svg b/resources/icons/thumb_up.svg new file mode 100644 index 000000000..c9045929b --- /dev/null +++ b/resources/icons/thumb_up.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/resources/localization/PrusaSlicer.pot b/resources/localization/PrusaSlicer.pot index 9a01d213d..1b1e74cba 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-09-09 16:39+0200\n" +"POT-Creation-Date: 2019-12-04 11:00+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:62 +#: src/slic3r/GUI/AboutDialog.cpp:231 src/slic3r/GUI/MainFrame.cpp:63 msgid "Version" msgstr "" @@ -64,31 +64,50 @@ msgid "" "numerous others." msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:92 +#: src/slic3r/GUI/AppConfig.cpp:105 +msgid "" +"Error parsing PrusaSlicer config file, it is probably corrupted. Try to " +"manualy 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:3290 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3365 src/slic3r/GUI/Plater.cpp:135 +msgid "Info" +msgstr "" + +#: 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 "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:93 -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:415 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:111 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:461 msgid "Running post-processing scripts" msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:95 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:113 msgid "G-code file exported to %1%" msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:99 #: src/slic3r/GUI/BackgroundSlicingProcess.cpp:117 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:167 msgid "Slicing complete" msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:113 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:163 msgid "Masked SLA file exported to %1%" msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:155 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:205 #, possible-c-format msgid "" "%s has encountered an error. It was likely caused by running out of memory. " @@ -96,152 +115,158 @@ msgid "" "and we would be glad if you reported it." msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:417 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:463 msgid "Copying of the temporary G-code to the output G-code failed" msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:426 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:488 msgid "Scheduling upload to `%1%`. See Window -> Print Host Upload Queue" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:65 +#: src/slic3r/GUI/BedShapeDialog.cpp:68 src/slic3r/GUI/GUI_ObjectList.cpp:1930 msgid "Shape" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:72 +#: src/slic3r/GUI/BedShapeDialog.cpp:75 msgid "Rectangular" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:76 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:393 src/slic3r/GUI/Plater.cpp:145 -#: src/slic3r/GUI/Tab.cpp:2524 +#: 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 "" -#: src/slic3r/GUI/BedShapeDialog.cpp:77 +#: src/slic3r/GUI/BedShapeDialog.cpp:80 msgid "Size in X and Y of the rectangular plate." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:83 +#: src/slic3r/GUI/BedShapeDialog.cpp:86 msgid "Origin" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:84 +#: src/slic3r/GUI/BedShapeDialog.cpp:87 msgid "" "Distance of the 0,0 G-code coordinate from the front left corner of the " "rectangle." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:88 +#: src/slic3r/GUI/BedShapeDialog.cpp:91 msgid "Circular" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:91 src/slic3r/GUI/ConfigWizard.cpp:123 -#: src/slic3r/GUI/ConfigWizard.cpp:576 src/slic3r/GUI/ConfigWizard.cpp:590 +#: src/slic3r/GUI/BedShapeDialog.cpp:94 src/slic3r/GUI/ConfigWizard.cpp:218 +#: src/slic3r/GUI/ConfigWizard.cpp:934 src/slic3r/GUI/ConfigWizard.cpp:948 #: src/slic3r/GUI/GUI_ObjectLayers.cpp:135 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:390 -#: src/slic3r/GUI/WipeTowerDialog.cpp:84 src/slic3r/GUI/wxExtensions.cpp:509 -#: 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:2510 src/libslic3r/PrintConfig.cpp:2519 -#: src/libslic3r/PrintConfig.cpp:2528 src/libslic3r/PrintConfig.cpp:2538 -#: src/libslic3r/PrintConfig.cpp:2582 src/libslic3r/PrintConfig.cpp:2592 -#: src/libslic3r/PrintConfig.cpp:2604 src/libslic3r/PrintConfig.cpp:2624 -#: src/libslic3r/PrintConfig.cpp:2634 src/libslic3r/PrintConfig.cpp:2644 -#: src/libslic3r/PrintConfig.cpp:2662 src/libslic3r/PrintConfig.cpp:2677 -#: src/libslic3r/PrintConfig.cpp:2691 src/libslic3r/PrintConfig.cpp:2704 -#: src/libslic3r/PrintConfig.cpp:2742 src/libslic3r/PrintConfig.cpp:2752 -#: src/libslic3r/PrintConfig.cpp:2761 src/libslic3r/PrintConfig.cpp:2771 +#: 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 "" -#: src/slic3r/GUI/BedShapeDialog.cpp:92 src/libslic3r/PrintConfig.cpp:692 +#: src/slic3r/GUI/BedShapeDialog.cpp:95 src/libslic3r/PrintConfig.cpp:699 msgid "Diameter" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:93 +#: 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 "" -#: src/slic3r/GUI/BedShapeDialog.cpp:97 src/slic3r/GUI/GUI_Preview.cpp:247 -#: src/libslic3r/GCode/PreviewData.cpp:159 +#: src/slic3r/GUI/BedShapeDialog.cpp:100 src/slic3r/GUI/GUI_Preview.cpp:248 +#: src/libslic3r/ExtrusionEntity.cpp:322 msgid "Custom" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:101 +#: src/slic3r/GUI/BedShapeDialog.cpp:104 msgid "Load shape from STL..." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:154 +#: src/slic3r/GUI/BedShapeDialog.cpp:157 msgid "Settings" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:171 +#: src/slic3r/GUI/BedShapeDialog.cpp:174 msgid "Texture" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:181 src/slic3r/GUI/BedShapeDialog.cpp:249 +#: src/slic3r/GUI/BedShapeDialog.cpp:184 src/slic3r/GUI/BedShapeDialog.cpp:263 msgid "Load..." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:189 src/slic3r/GUI/BedShapeDialog.cpp:257 -#: src/slic3r/GUI/Tab.cpp:3286 +#: src/slic3r/GUI/BedShapeDialog.cpp:192 src/slic3r/GUI/BedShapeDialog.cpp:271 +#: src/slic3r/GUI/Tab.cpp:3073 msgid "Remove" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:239 +#: src/slic3r/GUI/BedShapeDialog.cpp:225 src/slic3r/GUI/BedShapeDialog.cpp:304 +msgid "Not found: " +msgstr "" + +#: src/slic3r/GUI/BedShapeDialog.cpp:253 msgid "Model" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:464 +#: src/slic3r/GUI/BedShapeDialog.cpp:489 msgid "Choose an STL file to import bed shape from:" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:471 src/slic3r/GUI/BedShapeDialog.cpp:520 -#: src/slic3r/GUI/BedShapeDialog.cpp:543 +#: src/slic3r/GUI/BedShapeDialog.cpp:496 src/slic3r/GUI/BedShapeDialog.cpp:545 +#: src/slic3r/GUI/BedShapeDialog.cpp:570 msgid "Invalid file format." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:482 +#: src/slic3r/GUI/BedShapeDialog.cpp:507 msgid "Error! Invalid model" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:490 +#: src/slic3r/GUI/BedShapeDialog.cpp:515 msgid "The selected file contains no geometry." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:494 +#: src/slic3r/GUI/BedShapeDialog.cpp:519 msgid "" "The selected file contains several disjoint areas. This is not supported." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:509 +#: src/slic3r/GUI/BedShapeDialog.cpp:534 msgid "Choose a file to import bed texture from (PNG/SVG):" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:532 +#: src/slic3r/GUI/BedShapeDialog.cpp:559 msgid "Choose an STL file to import bed model from:" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.hpp:59 src/slic3r/GUI/ConfigWizard.cpp:535 +#: src/slic3r/GUI/BedShapeDialog.hpp:59 src/slic3r/GUI/ConfigWizard.cpp:893 msgid "Bed Shape" msgstr "" @@ -287,6 +312,129 @@ msgid "" "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 "" + +#: 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 "" + +#: src/slic3r/GUI/ConfigManipulation.cpp:61 src/libslic3r/PrintConfig.cpp:878 +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" +"- no ensure_vertical_shell_thickness" +msgstr "" + +#: 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 "" + +#: 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 "" + +#: 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 "" + +#: 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 "" + +#: 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 "" + +#: 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:89 +#: src/slic3r/GUI/GUI_ObjectList.cpp:607 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 "" + +#: src/slic3r/GUI/ConfigManipulation.cpp:304 +msgid "Head penetration should not be greater than the head width." +msgstr "" + +#: src/slic3r/GUI/ConfigManipulation.cpp:306 +msgid "Invalid Head penetration" +msgstr "" + +#: src/slic3r/GUI/ConfigManipulation.cpp:317 +msgid "Pinhead diameter should be smaller than the pillar diameter." +msgstr "" + +#: src/slic3r/GUI/ConfigManipulation.cpp:319 +msgid "Invalid pinhead diameter" +msgstr "" + #: src/slic3r/GUI/ConfigSnapshotDialog.cpp:18 msgid "Upgrade" msgstr "" @@ -307,150 +455,163 @@ 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" +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:50 +msgid "PrusaSlicer version" msgstr "" -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:46 src/slic3r/GUI/Preset.cpp:1311 +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:51 src/slic3r/GUI/Preset.cpp:1392 msgid "print" 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:1315 +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:53 src/slic3r/GUI/Preset.cpp:1396 msgid "printer" msgstr "" -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:52 src/slic3r/GUI/Tab.cpp:961 +#: 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 "" - -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:55 -msgid "max slic3r version" -msgstr "" - #: src/slic3r/GUI/ConfigSnapshotDialog.cpp:58 +msgid "min PrusaSlicer version" +msgstr "" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:60 +msgid "max PrusaSlicer version" +msgstr "" + +#: 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 #, possible-c-format msgid "Incompatible with this %s" msgstr "" -#: 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:123 +#: src/slic3r/GUI/ConfigWizard.cpp:218 msgid "nozzle" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:127 +#: src/slic3r/GUI/ConfigWizard.cpp:222 msgid "Alternate nozzles:" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:193 +#: src/slic3r/GUI/ConfigWizard.cpp:289 msgid "All standard" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:194 src/slic3r/GUI/Tab.cpp:3336 +#: src/slic3r/GUI/ConfigWizard.cpp:289 +msgid "Standard" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:290 src/slic3r/GUI/ConfigWizard.cpp:545 +#: src/slic3r/GUI/Tab.cpp:3123 msgid "All" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:195 src/slic3r/GUI/Plater.cpp:469 -#: src/slic3r/GUI/Plater.cpp:607 src/libslic3r/GCode/PreviewData.cpp:146 +#: 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 "" -#: src/slic3r/GUI/ConfigWizard.cpp:301 +#: src/slic3r/GUI/ConfigWizard.cpp:412 #, possible-c-format msgid "Welcome to the %s Configuration Assistant" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:303 +#: src/slic3r/GUI/ConfigWizard.cpp:414 #, possible-c-format msgid "Welcome to the %s Configuration Wizard" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:305 +#: src/slic3r/GUI/ConfigWizard.cpp:416 msgid "Welcome" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:309 src/slic3r/GUI/GUI_App.cpp:793 -#, possible-c-format -msgid "Run %s" -msgstr "" - -#: src/slic3r/GUI/ConfigWizard.cpp:311 +#: src/slic3r/GUI/ConfigWizard.cpp:418 #, 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:316 +#: src/slic3r/GUI/ConfigWizard.cpp:423 msgid "" "Remove user profiles - install from scratch (a snapshot will be taken " "beforehand)" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:347 +#: src/slic3r/GUI/ConfigWizard.cpp:466 #, possible-c-format msgid "%s Family" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:384 +#: src/slic3r/GUI/ConfigWizard.cpp:537 +msgid "Vendor:" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:538 +msgid "Profile:" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:575 src/slic3r/GUI/ConfigWizard.cpp:603 +msgid "(All)" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:712 msgid "Custom Printer Setup" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:384 +#: src/slic3r/GUI/ConfigWizard.cpp:712 msgid "Custom Printer" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:386 +#: src/slic3r/GUI/ConfigWizard.cpp:714 msgid "Define a custom printer profile" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:388 +#: src/slic3r/GUI/ConfigWizard.cpp:716 msgid "Custom profile name:" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:412 +#: src/slic3r/GUI/ConfigWizard.cpp:740 msgid "Automatic updates" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:412 +#: src/slic3r/GUI/ConfigWizard.cpp:740 msgid "Updates" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:420 src/slic3r/GUI/Preferences.cpp:69 +#: src/slic3r/GUI/ConfigWizard.cpp:748 src/slic3r/GUI/Preferences.cpp:69 msgid "Check for application updates" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:424 +#: src/slic3r/GUI/ConfigWizard.cpp:752 #, possible-c-format msgid "" "If enabled, %s checks for new application versions online. When a new " @@ -459,11 +620,11 @@ msgid "" "notification mechanisms, no automatic installation is done." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:430 src/slic3r/GUI/Preferences.cpp:77 +#: src/slic3r/GUI/ConfigWizard.cpp:758 src/slic3r/GUI/Preferences.cpp:77 msgid "Update built-in Presets automatically" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:434 +#: src/slic3r/GUI/ConfigWizard.cpp:762 #, possible-c-format msgid "" "If enabled, %s downloads updates of built-in system presets in the " @@ -472,189 +633,254 @@ msgid "" "startup." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:437 +#: src/slic3r/GUI/ConfigWizard.cpp:765 msgid "" "Updates are never applied without user's consent and never overwrite user's " "customized settings." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:442 +#: src/slic3r/GUI/ConfigWizard.cpp:770 msgid "" "Additionally a backup snapshot of the whole configuration is created before " "an update is applied." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:449 +#: src/slic3r/GUI/ConfigWizard.cpp:777 +msgid "View mode" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:779 +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 progressivly more sophisticated " +"fine-tuning, they are suitable for advanced and expert usiser, respectively." +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:784 +msgid "Simple mode" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:785 +msgid "Advanced mode" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:786 +msgid "Expert mode" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:820 msgid "Other Vendors" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:451 +#: src/slic3r/GUI/ConfigWizard.cpp:824 #, possible-c-format -msgid "Pick another vendor supported by %s:" +msgid "Pick another vendor supported by %s: (FIXME: this text)" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:497 +#: src/slic3r/GUI/ConfigWizard.cpp:855 msgid "Firmware Type" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:497 src/slic3r/GUI/Tab.cpp:2149 +#: src/slic3r/GUI/ConfigWizard.cpp:855 src/slic3r/GUI/Tab.cpp:1917 msgid "Firmware" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:501 +#: src/slic3r/GUI/ConfigWizard.cpp:859 msgid "Choose the type of firmware used by your printer." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:535 +#: src/slic3r/GUI/ConfigWizard.cpp:893 msgid "Bed Shape and Size" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:538 +#: src/slic3r/GUI/ConfigWizard.cpp:896 msgid "Set the shape of your printer's bed." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:558 +#: src/slic3r/GUI/ConfigWizard.cpp:916 msgid "Filament and Nozzle Diameters" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:558 +#: src/slic3r/GUI/ConfigWizard.cpp:916 msgid "Print Diameters" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:572 +#: src/slic3r/GUI/ConfigWizard.cpp:930 msgid "Enter the diameter of your printer's hot end nozzle." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:575 +#: src/slic3r/GUI/ConfigWizard.cpp:933 msgid "Nozzle Diameter:" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:585 +#: src/slic3r/GUI/ConfigWizard.cpp:943 msgid "Enter the diameter of your filament." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:586 +#: src/slic3r/GUI/ConfigWizard.cpp:944 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:589 +#: src/slic3r/GUI/ConfigWizard.cpp:947 msgid "Filament Diameter:" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:623 +#: src/slic3r/GUI/ConfigWizard.cpp:981 msgid "Extruder and Bed Temperatures" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:623 +#: src/slic3r/GUI/ConfigWizard.cpp:981 msgid "Temperatures" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:639 +#: src/slic3r/GUI/ConfigWizard.cpp:997 msgid "Enter the temperature needed for extruding your filament." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:640 +#: src/slic3r/GUI/ConfigWizard.cpp:998 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:643 +#: src/slic3r/GUI/ConfigWizard.cpp:1001 msgid "Extrusion Temperature:" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:644 src/slic3r/GUI/ConfigWizard.cpp:658 +#: src/slic3r/GUI/ConfigWizard.cpp:1002 src/slic3r/GUI/ConfigWizard.cpp:1016 msgid "°C" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:653 +#: src/slic3r/GUI/ConfigWizard.cpp:1011 msgid "" "Enter the bed temperature needed for getting your filament to stick to your " "heated bed." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:654 +#: src/slic3r/GUI/ConfigWizard.cpp:1012 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:657 +#: src/slic3r/GUI/ConfigWizard.cpp:1015 msgid "Bed Temperature:" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1138 +#: src/slic3r/GUI/ConfigWizard.cpp:1434 src/slic3r/GUI/ConfigWizard.cpp:1874 +msgid "Filaments" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:1434 src/slic3r/GUI/ConfigWizard.cpp:1876 +msgid "SLA Materials" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:1488 +msgid "FFF Technology Printers" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:1493 +msgid "SLA Technology Printers" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:1637 +msgid "You have to select at least one filament for selected printers" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:1643 +msgid "You have to select at least one material for selected printers" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:1843 msgid "Select all standard printers" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1141 +#: src/slic3r/GUI/ConfigWizard.cpp:1846 msgid "< &Back" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1142 +#: src/slic3r/GUI/ConfigWizard.cpp:1847 msgid "&Next >" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1143 +#: src/slic3r/GUI/ConfigWizard.cpp:1848 msgid "&Finish" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1144 src/slic3r/GUI/FirmwareDialog.cpp:151 +#: src/slic3r/GUI/ConfigWizard.cpp:1849 src/slic3r/GUI/FirmwareDialog.cpp:151 #: src/slic3r/GUI/ProgressStatusBar.cpp:27 msgid "Cancel" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1158 +#: src/slic3r/GUI/ConfigWizard.cpp:1862 msgid "Prusa FFF Technology Printers" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1161 +#: src/slic3r/GUI/ConfigWizard.cpp:1865 msgid "Prusa MSLA Technology Printers" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1230 +#: src/slic3r/GUI/ConfigWizard.cpp:1874 +msgid "Filament Profiles Selection" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:1874 src/slic3r/GUI/GUI_ObjectList.cpp:3413 +msgid "Type:" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:1876 +msgid "SLA Material Profiles Selection" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:1876 +msgid "Layer height:" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:1966 msgid "Configuration Assistant" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1231 +#: src/slic3r/GUI/ConfigWizard.cpp:1967 msgid "Configuration &Assistant" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1233 +#: src/slic3r/GUI/ConfigWizard.cpp:1969 msgid "Configuration Wizard" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1234 +#: src/slic3r/GUI/ConfigWizard.cpp:1970 msgid "Configuration &Wizard" msgstr "" -#: src/slic3r/GUI/Field.cpp:125 +#: 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/OptionsGroup.cpp:569 +#: src/slic3r/GUI/Field.cpp:145 src/slic3r/GUI/OptionsGroup.cpp:570 msgid "N/A" msgstr "" -#: src/slic3r/GUI/Field.cpp:158 +#: src/slic3r/GUI/Field.cpp:170 #, possible-c-format msgid "%s doesn't support percentage" msgstr "" -#: src/slic3r/GUI/Field.cpp:174 src/slic3r/GUI/Field.cpp:197 -#: src/slic3r/GUI/GUI_ObjectLayers.cpp:337 +#: 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 "" -#: src/slic3r/GUI/Field.cpp:206 +#: src/slic3r/GUI/Field.cpp:235 #, possible-c-format msgid "" "Do you mean %s%% instead of %s %s?\n" @@ -662,7 +888,7 @@ msgid "" "or NO if you are sure that %s %s is a correct value." msgstr "" -#: src/slic3r/GUI/Field.cpp:209 +#: src/slic3r/GUI/Field.cpp:238 msgid "Parameter validation" msgstr "" @@ -738,8 +964,8 @@ msgstr "" msgid "Firmware image:" msgstr "" -#: src/slic3r/GUI/FirmwareDialog.cpp:805 src/slic3r/GUI/Tab.cpp:1870 -#: src/slic3r/GUI/Tab.cpp:1926 +#: src/slic3r/GUI/FirmwareDialog.cpp:805 src/slic3r/GUI/Tab.cpp:1635 +#: src/slic3r/GUI/Tab.cpp:1691 msgid "Browse" msgstr "" @@ -776,179 +1002,295 @@ msgstr "" 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!" msgstr "" -#: 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:534 -msgid "Layers heights" +#: src/slic3r/GUI/GLCanvas3D.cpp:240 +msgid "Layer height profile" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:631 +#: src/slic3r/GUI/GLCanvas3D.cpp:243 +msgid "Left mouse button:" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:246 +msgid "Add detail" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:249 +msgid "Right mouse button:" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:252 +msgid "Remove detail" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:255 +msgid "Shift + Left mouse button:" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:258 +msgid "Reset to base" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:261 +msgid "Shift + Right mouse button:" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:264 +msgid "Smoothing" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:267 +msgid "Mouse wheel:" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:270 +msgid "Increase/decrease edit area" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:273 +msgid "Adaptive" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:278 +msgid "Cusp (mm)" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:286 +msgid "Smooth" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:291 src/libslic3r/PrintConfig.cpp:500 +msgid "Radius" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:300 +msgid "Keep min" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:307 +msgid "Reset" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:695 +msgid "Layer height profile-Manual edit" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:794 msgid "An object outside the print area was detected" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:632 +#: src/slic3r/GUI/GLCanvas3D.cpp:795 msgid "A toolpath outside the print area was detected" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:633 +#: src/slic3r/GUI/GLCanvas3D.cpp:796 msgid "SLA supports outside the print area were detected" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:634 +#: src/slic3r/GUI/GLCanvas3D.cpp:797 msgid "Some objects are not visible when editing supports" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:636 +#: src/slic3r/GUI/GLCanvas3D.cpp:799 msgid "" "An object outside the print area was detected\n" "Resolve the current problem to continue slicing" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:1733 +#: src/slic3r/GUI/GLCanvas3D.cpp:1014 src/slic3r/GUI/GLCanvas3D.cpp:1042 +msgid "Default print color" +msgstr "" + +#: 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 "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1064 +#, possible-c-format +msgid "up to %.2f mm" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1068 +#, possible-c-format +msgid "above %.2f mm" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1072 +#, 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 +#, possible-c-format +msgid "Color change for Extruder %d at %.2f mm" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1673 +msgid "Layer height profile-Reset" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1681 +msgid "Layer height profile-Adaptive" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1689 +msgid "Layer height profile-Smooth all" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:2026 msgid "Mirror Object" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:2970 +#: src/slic3r/GUI/GLCanvas3D.cpp:3297 msgid "Move Object" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3506 +#: src/slic3r/GUI/GLCanvas3D.cpp:3843 msgid "Undo History" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3506 +#: src/slic3r/GUI/GLCanvas3D.cpp:3843 msgid "Redo History" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3524 +#: src/slic3r/GUI/GLCanvas3D.cpp:3861 #, possible-c-format msgid "Undo %1$d Action" msgid_plural "Undo %1$d Actions" msgstr[0] "" msgstr[1] "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3524 +#: src/slic3r/GUI/GLCanvas3D.cpp:3861 #, possible-c-format msgid "Redo %1$d Action" msgid_plural "Redo %1$d Actions" msgstr[0] "" msgstr[1] "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3571 +#: src/slic3r/GUI/GLCanvas3D.cpp:4259 msgid "Add..." msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3579 src/slic3r/GUI/GUI_ObjectList.cpp:1501 -#: src/slic3r/GUI/Plater.cpp:3520 src/slic3r/GUI/Plater.cpp:3539 -#: src/slic3r/GUI/Tab.cpp:3286 +#: src/slic3r/GUI/GLCanvas3D.cpp:4267 src/slic3r/GUI/GUI_ObjectList.cpp:1590 +#: src/slic3r/GUI/Plater.cpp:3712 src/slic3r/GUI/Plater.cpp:3734 +#: src/slic3r/GUI/Tab.cpp:3073 msgid "Delete" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3588 src/slic3r/GUI/Plater.cpp:4172 +#: src/slic3r/GUI/GLCanvas3D.cpp:4276 src/slic3r/GUI/Plater.cpp:4410 msgid "Delete all" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3597 src/slic3r/GUI/KBShortcutsDialog.cpp:137 -#: src/slic3r/GUI/Plater.cpp:2681 +#: src/slic3r/GUI/GLCanvas3D.cpp:4285 src/slic3r/GUI/KBShortcutsDialog.cpp:137 +#: src/slic3r/GUI/Plater.cpp:2758 msgid "Arrange" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3597 src/slic3r/GUI/KBShortcutsDialog.cpp:138 +#: src/slic3r/GUI/GLCanvas3D.cpp:4285 src/slic3r/GUI/KBShortcutsDialog.cpp:138 msgid "Arrange selection" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3609 +#: src/slic3r/GUI/GLCanvas3D.cpp:4297 msgid "Copy" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3618 +#: src/slic3r/GUI/GLCanvas3D.cpp:4306 msgid "Paste" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3630 src/slic3r/GUI/Plater.cpp:3400 -#: src/slic3r/GUI/Plater.cpp:3412 src/slic3r/GUI/Plater.cpp:3526 +#: 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 "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3641 src/slic3r/GUI/Plater.cpp:3528 +#: src/slic3r/GUI/GLCanvas3D.cpp:4329 src/slic3r/GUI/Plater.cpp:3723 msgid "Remove instance" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3654 +#: src/slic3r/GUI/GLCanvas3D.cpp:4342 msgid "Split to objects" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3664 src/slic3r/GUI/GUI_ObjectList.cpp:1340 +#: src/slic3r/GUI/GLCanvas3D.cpp:4352 src/slic3r/GUI/GUI_ObjectList.cpp:1422 msgid "Split to parts" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3677 src/slic3r/GUI/GUI_ObjectList.cpp:2203 +#: src/slic3r/GUI/GLCanvas3D.cpp:4365 src/slic3r/GUI/GUI_ObjectList.cpp:2372 msgid "Height ranges" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3728 src/slic3r/GUI/MainFrame.cpp:570 +#: src/slic3r/GUI/GLCanvas3D.cpp:4416 src/slic3r/GUI/MainFrame.cpp:571 msgid "Undo" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3728 src/slic3r/GUI/GLCanvas3D.cpp:3761 +#: src/slic3r/GUI/GLCanvas3D.cpp:4416 src/slic3r/GUI/GLCanvas3D.cpp:4449 msgid "Click right mouse button to open History" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3745 +#: src/slic3r/GUI/GLCanvas3D.cpp:4433 msgid "Next Undo action: %1%" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3761 src/slic3r/GUI/MainFrame.cpp:573 +#: src/slic3r/GUI/GLCanvas3D.cpp:4449 src/slic3r/GUI/MainFrame.cpp:574 msgid "Redo" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3777 +#: src/slic3r/GUI/GLCanvas3D.cpp:4465 msgid "Next Redo action: %1%" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:5555 +#: src/slic3r/GUI/GLCanvas3D.cpp:6380 msgid "Selection-Add from rectangle" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:5574 +#: src/slic3r/GUI/GLCanvas3D.cpp:6399 msgid "Selection-Remove from rectangle" msgstr "" -#: src/slic3r/GUI/GLCanvas3DManager.cpp:273 +#: src/slic3r/GUI/GLCanvas3DManager.cpp:283 #, 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:276 +#: src/slic3r/GUI/GLCanvas3DManager.cpp:286 msgid "You may need to update your graphics card driver." msgstr "" -#: src/slic3r/GUI/GLCanvas3DManager.cpp:279 +#: 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 "" -#: src/slic3r/GUI/GLCanvas3DManager.cpp:281 +#: src/slic3r/GUI/GLCanvas3DManager.cpp:291 msgid "Unsupported OpenGL version" msgstr "" #: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:40 -#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:145 src/libslic3r/PrintConfig.cpp:3212 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:145 src/libslic3r/PrintConfig.cpp:3329 msgid "Cut" msgstr "" @@ -985,10 +1327,10 @@ msgid "Displacement (mm)" msgstr "" #: src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp:449 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:477 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:496 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:514 -#: src/libslic3r/PrintConfig.cpp:3261 +#: 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 "" @@ -997,10 +1339,10 @@ msgid "Rotation (deg)" msgstr "" #: src/slic3r/GUI/Gizmos/GLGizmoScale.cpp:47 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:392 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:497 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:515 -#: src/libslic3r/PrintConfig.cpp:3276 +#: 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 "" @@ -1008,225 +1350,214 @@ msgstr "" 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 "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:46 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1449 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:50 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1286 msgid "Remove selected points" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:47 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:51 msgid "Remove all points" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:48 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1452 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:52 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1289 msgid "Apply changes" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:49 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1453 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:53 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1290 msgid "Discard changes" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:50 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:54 msgid "Minimal points distance" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:51 -#: src/libslic3r/PrintConfig.cpp:2651 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:55 +#: src/libslic3r/PrintConfig.cpp:2732 msgid "Support points density" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:52 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1455 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:56 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1292 msgid "Auto-generate points" 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 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:58 msgid "Clipping of view" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:55 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:59 msgid "Reset direction" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:531 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:442 msgid "Add support point" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:719 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:578 msgid "Delete support point" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:920 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:754 msgid "Change point head diameter" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:986 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:820 msgid "Support parameter change" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1094 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:929 msgid "SLA Support Points" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1115 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:950 msgid "SLA gizmo turned on" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1137 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:972 msgid "Do you want to save your manually edited support points?" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1138 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:973 msgid "Save changes?" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1150 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:985 msgid "SLA gizmo turned off" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1187 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1022 msgid "Move support point" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1286 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1121 msgid "Support points edit" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1355 -msgid "" -"Autogeneration will erase all manually edited points.\n" -"\n" -"Are you sure you want to do it?\n" +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1190 +msgid "Autogeneration will erase all manually edited points." msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1357 src/slic3r/GUI/GUI.cpp:289 -#: src/slic3r/GUI/WipeTowerDialog.cpp:44 src/slic3r/GUI/WipeTowerDialog.cpp:328 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1191 +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:3033 src/slic3r/GUI/WipeTowerDialog.cpp:45 +#: src/slic3r/GUI/WipeTowerDialog.cpp:366 msgid "Warning" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1360 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1195 msgid "Autogenerate support points" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1412 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1249 msgid "SLA gizmo keyboard shortcuts" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1423 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1260 msgid "Note: some shortcuts work in (non)editing mode only." msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1441 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1444 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1445 +#: 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 "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1441 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1278 msgid "Add point" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1442 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1279 msgid "Right click" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1442 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1279 msgid "Remove point" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1443 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1446 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1447 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1280 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1283 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1284 msgid "Drag" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1443 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1280 msgid "Move point" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1444 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1281 msgid "Add point to selection" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1445 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1282 msgid "Remove point from selection" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1446 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1283 msgid "Select by rectangle" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1447 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1284 msgid "Deselect by rectangle" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1448 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1285 msgid "Select all points" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1450 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1287 msgid "Mouse wheel" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1450 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1287 msgid "Move clipping plane" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1451 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1288 msgid "Reset clipping plane" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1454 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1291 msgid "Switch to editing mode" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:569 +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:477 msgid "Gizmo-Place on Face" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:641 +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:550 msgid "Gizmo-Move" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:646 +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:552 msgid "Gizmo-Scale" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:651 +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:554 msgid "Gizmo-Rotate" msgstr "" -#: src/slic3r/GUI/GUI.cpp:141 src/slic3r/GUI/Tab.cpp:3145 -msgid "It's impossible to print multi-part object(s) with SLA technology." -msgstr "" - -#: src/slic3r/GUI/GUI.cpp:142 -msgid "Please check and fix your object list." -msgstr "" - -#: src/slic3r/GUI/GUI.cpp:143 src/slic3r/GUI/Plater.cpp:2246 -#: src/slic3r/GUI/Tab.cpp:3147 -msgid "Attention!" -msgstr "" - -#: src/slic3r/GUI/GUI.cpp:283 +#: src/slic3r/GUI/GUI.cpp:240 msgid "Notice" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:132 +#: src/slic3r/GUI/GUI_App.cpp:137 #, possible-c-format msgid "" "%s has encountered an error. It was likely caused by running out of memory. " @@ -1236,156 +1567,178 @@ msgid "" "The application will now terminate." msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:135 +#: src/slic3r/GUI/GUI_App.cpp:140 msgid "Fatal error" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:442 +#: src/slic3r/GUI/GUI_App.cpp:448 msgid "Changing of an application language" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:450 src/slic3r/GUI/GUI_App.cpp:459 +#: src/slic3r/GUI/GUI_App.cpp:456 src/slic3r/GUI/GUI_App.cpp:465 msgid "Recreating" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:463 +#: src/slic3r/GUI/GUI_App.cpp:469 msgid "Loading of current presets" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:471 +#: src/slic3r/GUI/GUI_App.cpp:477 msgid "Loading of a mode view" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:551 +#: src/slic3r/GUI/GUI_App.cpp:558 msgid "Choose one file (3MF/AMF):" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:563 +#: src/slic3r/GUI/GUI_App.cpp:570 msgid "Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:625 +#: src/slic3r/GUI/GUI_App.cpp:632 msgid "Select the language" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:625 +#: src/slic3r/GUI/GUI_App.cpp:632 msgid "Language" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:796 +#: src/slic3r/GUI/GUI_App.cpp:800 +#, possible-c-format +msgid "Run %s" +msgstr "" + +#: src/slic3r/GUI/GUI_App.cpp:803 msgid "&Configuration Snapshots" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:796 +#: src/slic3r/GUI/GUI_App.cpp:803 msgid "Inspect / activate configuration snapshots" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:797 +#: src/slic3r/GUI/GUI_App.cpp:804 msgid "Take Configuration &Snapshot" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:797 +#: src/slic3r/GUI/GUI_App.cpp:804 msgid "Capture a configuration snapshot" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:800 +#: src/slic3r/GUI/GUI_App.cpp:807 msgid "&Preferences" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:806 +#: src/slic3r/GUI/GUI_App.cpp:813 msgid "Application preferences" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:809 src/slic3r/GUI/wxExtensions.cpp:3043 +#: src/slic3r/GUI/GUI_App.cpp:816 src/slic3r/GUI/wxExtensions.cpp:3824 msgid "Simple" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:809 +#: src/slic3r/GUI/GUI_App.cpp:816 msgid "Simple View Mode" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:810 src/slic3r/GUI/GUI_ObjectList.cpp:97 -#: src/slic3r/GUI/GUI_ObjectList.cpp:620 src/slic3r/GUI/Tab.cpp:1061 -#: src/slic3r/GUI/Tab.cpp:1076 src/slic3r/GUI/Tab.cpp:1174 -#: src/slic3r/GUI/Tab.cpp:1177 src/slic3r/GUI/Tab.cpp:1685 -#: src/slic3r/GUI/Tab.cpp:2169 src/slic3r/GUI/Tab.cpp:3785 -#: src/slic3r/GUI/wxExtensions.cpp:3044 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:817 src/slic3r/GUI/GUI_ObjectList.cpp:97 +#: src/slic3r/GUI/GUI_ObjectList.cpp:615 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:3607 +#: 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 "" -#: src/slic3r/GUI/GUI_App.cpp:810 +#: src/slic3r/GUI/GUI_App.cpp:817 msgid "Advanced View Mode" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:811 src/slic3r/GUI/wxExtensions.cpp:3045 +#: src/slic3r/GUI/GUI_App.cpp:818 src/slic3r/GUI/wxExtensions.cpp:3826 msgid "Expert" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:811 +#: src/slic3r/GUI/GUI_App.cpp:818 msgid "Expert View Mode" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:816 +#: src/slic3r/GUI/GUI_App.cpp:823 msgid "Mode" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:816 +#: src/slic3r/GUI/GUI_App.cpp:823 #, possible-c-format msgid "%s View Mode" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:818 +#: src/slic3r/GUI/GUI_App.cpp:825 msgid "Change Application &Language" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:820 +#: src/slic3r/GUI/GUI_App.cpp:827 msgid "Flash printer &firmware" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:820 +#: src/slic3r/GUI/GUI_App.cpp:827 msgid "Upload a firmware image into an Arduino based printer" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:832 +#: src/slic3r/GUI/GUI_App.cpp:839 msgid "Taking configuration snapshot" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:832 +#: src/slic3r/GUI/GUI_App.cpp:839 msgid "Snapshot name" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:875 +#: 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:877 +#: src/slic3r/GUI/GUI_App.cpp:884 msgid "Do you want to proceed?" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:878 +#: src/slic3r/GUI/GUI_App.cpp:885 msgid "Language selection" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:901 +#: src/slic3r/GUI/GUI_App.cpp:908 msgid "&Configuration" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:923 +#: src/slic3r/GUI/GUI_App.cpp:932 msgid "The presets on the following tabs were modified" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:923 src/slic3r/GUI/Tab.cpp:3133 +#: src/slic3r/GUI/GUI_App.cpp:932 src/slic3r/GUI/Tab.cpp:2901 msgid "Discard changes and continue anyway?" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:926 +#: src/slic3r/GUI/GUI_App.cpp:935 msgid "Unsaved Presets" msgstr "" +#: src/slic3r/GUI/GUI_App.cpp:1081 src/slic3r/GUI/Tab.cpp:2913 +msgid "It's impossible to print multi-part object(s) with SLA technology." +msgstr "" + +#: src/slic3r/GUI/GUI_App.cpp:1082 +msgid "Please check and fix your object list." +msgstr "" + +#: src/slic3r/GUI/GUI_App.cpp:1083 src/slic3r/GUI/Plater.cpp:2317 +#: src/slic3r/GUI/Tab.cpp:2915 +msgid "Attention!" +msgstr "" + +#: src/slic3r/GUI/GUI_App.cpp:1100 +msgid "Select a gcode file:" +msgstr "" + #: src/slic3r/GUI/GUI_ObjectLayers.cpp:27 msgid "Start at height" msgstr "" @@ -1394,11 +1747,6 @@ msgstr "" msgid "Stop at height" msgstr "" -#: src/slic3r/GUI/GUI_ObjectLayers.cpp:27 src/slic3r/GUI/Tab.cpp:1033 -#: src/libslic3r/PrintConfig.cpp:66 -msgid "Layer height" -msgstr "" - #: src/slic3r/GUI/GUI_ObjectLayers.cpp:153 msgid "Remove layer range" msgstr "" @@ -1408,49 +1756,36 @@ 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:611 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_ObjectList.cpp:606 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 "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:35 src/slic3r/GUI/GUI_ObjectList.cpp:89 -#: src/slic3r/GUI/GUI_ObjectList.cpp:612 src/slic3r/GUI/Plater.cpp:497 -#: src/slic3r/GUI/Tab.cpp:1065 src/slic3r/GUI/Tab.cpp:1066 -#: 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:36 src/slic3r/GUI/GUI_ObjectList.cpp:90 -#: src/slic3r/GUI/GUI_ObjectList.cpp:613 src/slic3r/GUI/GUI_Preview.cpp:244 -#: src/slic3r/GUI/Tab.cpp:1094 src/slic3r/GUI/Tab.cpp:1095 -#: 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:156 +#: src/slic3r/GUI/GUI_ObjectList.cpp:608 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 "" #: src/slic3r/GUI/GUI_ObjectList.cpp:39 src/slic3r/GUI/GUI_ObjectList.cpp:94 -#: src/slic3r/GUI/GUI_ObjectList.cpp:617 src/libslic3r/PrintConfig.cpp:2202 -#: src/libslic3r/PrintConfig.cpp:2210 +#: src/slic3r/GUI/GUI_ObjectList.cpp:612 src/libslic3r/PrintConfig.cpp:2229 +#: src/libslic3r/PrintConfig.cpp:2237 msgid "Wipe options" msgstr "" @@ -1474,483 +1809,493 @@ msgstr "" msgid "Add support blocker" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:91 src/slic3r/GUI/GUI_ObjectList.cpp:614 -#: src/slic3r/GUI/GUI_Preview.cpp:223 src/slic3r/GUI/Tab.cpp:1119 -#: 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:91 src/slic3r/GUI/GUI_ObjectList.cpp:609 +#: 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 "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:92 src/slic3r/GUI/GUI_ObjectList.cpp:615 -#: src/slic3r/GUI/Tab.cpp:1154 src/slic3r/GUI/Tab.cpp:2043 -#: 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:92 src/slic3r/GUI/GUI_ObjectList.cpp:610 +#: 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 "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:93 src/slic3r/GUI/GUI_ObjectList.cpp:616 -#: 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:93 src/slic3r/GUI/GUI_ObjectList.cpp:611 +#: 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 "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:99 src/slic3r/GUI/GUI_ObjectList.cpp:622 -#: src/slic3r/GUI/Plater.cpp:465 src/slic3r/GUI/Tab.cpp:3737 -#: src/slic3r/GUI/Tab.cpp:3738 src/libslic3r/PrintConfig.cpp:2501 -#: src/libslic3r/PrintConfig.cpp:2508 src/libslic3r/PrintConfig.cpp:2517 -#: src/libslic3r/PrintConfig.cpp:2526 src/libslic3r/PrintConfig.cpp:2536 -#: src/libslic3r/PrintConfig.cpp:2562 src/libslic3r/PrintConfig.cpp:2569 -#: src/libslic3r/PrintConfig.cpp:2580 src/libslic3r/PrintConfig.cpp:2590 -#: src/libslic3r/PrintConfig.cpp:2599 src/libslic3r/PrintConfig.cpp:2612 -#: src/libslic3r/PrintConfig.cpp:2622 src/libslic3r/PrintConfig.cpp:2631 -#: src/libslic3r/PrintConfig.cpp:2641 src/libslic3r/PrintConfig.cpp:2652 -#: src/libslic3r/PrintConfig.cpp:2660 +#: src/slic3r/GUI/GUI_ObjectList.cpp:99 src/slic3r/GUI/GUI_ObjectList.cpp:617 +#: src/slic3r/GUI/Plater.cpp:484 src/slic3r/GUI/Tab.cpp:3557 +#: src/slic3r/GUI/Tab.cpp:3558 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 "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:100 src/slic3r/GUI/GUI_ObjectList.cpp:623 -#: src/slic3r/GUI/Plater.cpp:603 src/slic3r/GUI/Tab.cpp:3769 -#: src/slic3r/GUI/Tab.cpp:3770 src/libslic3r/PrintConfig.cpp:2668 -#: src/libslic3r/PrintConfig.cpp:2675 src/libslic3r/PrintConfig.cpp:2689 -#: src/libslic3r/PrintConfig.cpp:2699 src/libslic3r/PrintConfig.cpp:2721 -#: src/libslic3r/PrintConfig.cpp:2732 src/libslic3r/PrintConfig.cpp:2739 -#: src/libslic3r/PrintConfig.cpp:2750 src/libslic3r/PrintConfig.cpp:2759 -#: src/libslic3r/PrintConfig.cpp:2768 +#: src/slic3r/GUI/GUI_ObjectList.cpp:100 src/slic3r/GUI/GUI_ObjectList.cpp:618 +#: src/slic3r/GUI/Plater.cpp:624 src/slic3r/GUI/Tab.cpp:3589 +#: src/slic3r/GUI/Tab.cpp:3590 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 "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:260 +#: src/slic3r/GUI/GUI_ObjectList.cpp:262 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:153 msgid "Name" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:271 src/slic3r/GUI/GUI_ObjectList.cpp:373 -msgid "Editing" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:318 -#, possible-c-format -msgid "Auto-repaired (%d errors):\n" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:325 -msgid "degenerate facets" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:326 -msgid "edges fixed" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:327 -msgid "facets removed" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:328 -msgid "facets added" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:329 -msgid "facets reversed" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:330 -msgid "backwards edges" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:338 -msgid "Right button click the icon to fix STL through Netfabb" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:375 -msgid "Right button click the icon to change the object settings" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:377 -msgid "Click the icon to change the object settings" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:381 -msgid "Right button click the icon to change the object printable property" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:383 -msgid "Click the icon to change the object printable property" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:428 src/slic3r/GUI/GUI_ObjectList.cpp:449 -#: src/slic3r/GUI/GUI_ObjectList.cpp:461 src/slic3r/GUI/GUI_ObjectList.cpp:3642 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3652 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3684 src/slic3r/GUI/wxExtensions.cpp:603 -#: src/slic3r/GUI/wxExtensions.cpp:660 src/slic3r/GUI/wxExtensions.cpp:685 -#: src/slic3r/GUI/wxExtensions.cpp:893 -msgid "default" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:433 src/slic3r/GUI/Tab.cpp:1649 -#: src/libslic3r/PrintConfig.cpp:470 +#: 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 "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:546 +#: src/slic3r/GUI/GUI_ObjectList.cpp:274 src/slic3r/GUI/GUI_ObjectList.cpp:386 +msgid "Editing" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:331 +#, possible-c-format +msgid "Auto-repaired (%d errors):" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:338 +msgid "degenerate facets" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:339 +msgid "edges fixed" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:340 +msgid "facets removed" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:341 +msgid "facets added" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:342 +msgid "facets reversed" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:343 +msgid "backwards edges" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:351 +msgid "Right button click the icon to fix STL through Netfabb" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:388 +msgid "Right button click the icon to change the object settings" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:390 +msgid "Click the icon to change the object settings" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:394 +msgid "Right button click the icon to change the object printable property" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:396 +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:905 src/slic3r/GUI/GUI_ObjectList.cpp:3820 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3830 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3862 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 "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:541 msgid "Rename Object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:546 +#: src/slic3r/GUI/GUI_ObjectList.cpp:541 msgid "Rename Sub-object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:987 src/slic3r/GUI/GUI_ObjectList.cpp:3464 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1066 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3641 msgid "Instances to Separated Objects" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1005 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1084 msgid "Volumes in Object reordered" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1005 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1084 msgid "Object reordered" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1060 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1376 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1382 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1623 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1139 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1458 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1464 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1721 #, possible-c-format msgid "Quick Add Settings (%s)" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1137 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1216 msgid "Select showing settings" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1186 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1265 msgid "Add Settings for Layers" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1187 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1266 msgid "Add Settings for Sub-object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1188 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1267 msgid "Add Settings for Object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1249 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1328 msgid "Add Settings Bundle for Height range" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1250 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1329 msgid "Add Settings Bundle for Sub-object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1251 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1330 msgid "Add Settings Bundle for Object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1290 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1369 msgid "Load" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1295 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1320 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1323 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1374 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1402 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1405 msgid "Box" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1295 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1374 msgid "Cylinder" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1295 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1374 msgid "Sphere" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1295 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1374 msgid "Slab" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1347 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1429 msgid "Height range Modifier" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1355 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1437 msgid "Add settings" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1422 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1505 msgid "Change type" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1429 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1577 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1512 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1669 msgid "Set as a Separated Object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1435 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1518 msgid "Printable" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1442 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1525 msgid "Rename" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1453 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1536 msgid "Fix through the Netfabb" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1463 src/slic3r/GUI/Plater.cpp:3552 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1546 src/slic3r/GUI/Plater.cpp:3747 msgid "Export as STL" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1470 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1553 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:1553 src/slic3r/GUI/Plater.cpp:3715 +msgid "Reload the selected volumes from disk" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1559 src/slic3r/GUI/wxExtensions.cpp:3176 +#: src/slic3r/GUI/wxExtensions.cpp:3432 msgid "Change extruder" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1489 src/libslic3r/PrintConfig.cpp:309 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1578 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:1495 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1584 msgid "Select new extruder for the object/part" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1507 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1596 msgid "Scale to print volume" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1507 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1596 msgid "Scale the selected object to fit the print volume" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1577 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1669 msgid "Set as a Separated Objects" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1652 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1676 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1922 +msgid "Add Shape" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1750 msgid "Load Part" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1687 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1789 msgid "Error!" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1732 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1864 msgid "Add Generic Subobject" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1739 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1893 msgid "Generic" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1843 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1945 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2011 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2113 msgid "Last instance of an object cannot be deleted." msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1855 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2023 msgid "Delete Settings" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1879 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2047 msgid "Delete All Instances from Object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1895 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2063 msgid "Delete Height Range" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1926 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2094 msgid "From Object List You can't delete the last solid part from object." msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1930 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2098 msgid "Delete Subobject" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1949 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2117 msgid "Delete Instance" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1973 src/slic3r/GUI/Plater.cpp:2838 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2141 src/slic3r/GUI/Plater.cpp:2914 msgid "" "The selected object couldn't be split because it contains only one part." msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1977 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2145 msgid "Split to Parts" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2025 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2193 msgid "Add Layers" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2150 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2319 msgid "Group manipulation" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2162 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2331 msgid "Object manipulation" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2175 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2344 msgid "Object Settings to modify" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2179 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2348 msgid "Part Settings to modify" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2184 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2353 msgid "Layer range Settings to modify" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2190 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2359 msgid "Part manipulation" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2196 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2365 msgid "Instance manipulation" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2203 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2372 msgid "Settings for height range" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2388 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2558 msgid "Delete Selected Item" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2525 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2695 msgid "Delete Selected" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2584 -#: src/slic3r/GUI/GUI_ObjectList.cpp:2613 -#: src/slic3r/GUI/GUI_ObjectList.cpp:2631 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2761 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2790 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2808 msgid "Add Height Range" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2690 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2867 msgid "Edit Height Range" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2974 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3151 msgid "Selection-Remove from list" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2982 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3159 msgid "Selection-Add from list" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3100 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3277 msgid "Object or Instance" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3101 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3234 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3278 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3411 msgid "Part" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3101 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3278 msgid "Layer" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3103 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3280 msgid "Unsupported selection" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3104 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3281 #, possible-c-format msgid "You started your selection with %s Item." msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3105 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3282 #, possible-c-format msgid "In this mode you can select only other %s Items%s" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3108 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3285 msgid "of a current Object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3113 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3188 src/slic3r/GUI/Plater.cpp:126 -msgid "Info" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:3229 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3406 msgid "You can't change a type of the last solid part of the object." msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3234 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3411 msgid "Modifier" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3234 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3411 msgid "Support Enforcer" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3234 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3411 msgid "Support Blocker" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3236 -msgid "Type:" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:3236 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3413 msgid "Select type of part" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3241 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3418 msgid "Change Part Type" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3486 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3663 msgid "Enter new name" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3486 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3663 msgid "Renaming" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3502 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3608 src/slic3r/GUI/Tab.cpp:3618 -#: src/slic3r/GUI/Tab.cpp:3622 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3679 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3786 src/slic3r/GUI/Tab.cpp:3405 +#: src/slic3r/GUI/Tab.cpp:3409 msgid "The supplied name is not valid;" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3503 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3609 src/slic3r/GUI/Tab.cpp:3619 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3680 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3787 src/slic3r/GUI/Tab.cpp:3406 msgid "the following characters are not allowed:" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3632 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3810 msgid "Set extruder for selected items" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3633 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3811 msgid "Select extruder number for selected objects and/or parts" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3646 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3824 msgid "Select extruder number:" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3647 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3825 msgid "This extruder will be set for selected items" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3759 src/slic3r/GUI/Selection.cpp:1473 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3937 src/slic3r/GUI/Selection.cpp:1473 msgid "Set Printable" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3759 src/slic3r/GUI/Selection.cpp:1473 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3937 src/slic3r/GUI/Selection.cpp:1473 msgid "Set Unprintable" msgstr "" @@ -1968,77 +2313,73 @@ 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:178 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:155 msgid "Object name" msgstr "" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:214 +#: 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 +msgid "Rotation" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:263 #, possible-c-format msgid "Toggle %c axis mirroring" msgstr "" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:247 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:297 msgid "Set Mirror" msgstr "" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:287 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:292 -msgid "Reset scale" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:305 -msgid "Reset rotation" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:330 -msgid "Reset Rotation" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:342 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:357 +#: 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:390 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:454 -msgid "Position" +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:363 +msgid "Reset rotation" msgstr "" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:391 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:455 -msgid "Rotation" +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:385 +msgid "Reset Rotation" msgstr "" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:456 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:397 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:399 +msgid "Reset scale" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:459 msgid "Scale factors" msgstr "" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:513 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:516 msgid "Translate" msgstr "" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:565 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:578 msgid "" "You cannot use non-uniform scaling mode for multiple objects/parts selection" msgstr "" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:735 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:750 msgid "Set Position" msgstr "" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:766 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:781 msgid "Set Orientation" msgstr "" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:831 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:846 msgid "Set Scale" msgstr "" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:915 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:875 msgid "" "The currently manipulated object is tilted (rotation angles are not " "multiples of 90°).\n" @@ -2047,7 +2388,7 @@ msgid "" "once the rotation is embedded into the object coordinates." msgstr "" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:918 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:878 msgid "" "This operation is irreversible.\n" "Do you want to proceed?" @@ -2066,7 +2407,7 @@ msgstr "" msgid "Delete Option %s" msgstr "" -#: src/slic3r/GUI/GUI_ObjectSettings.cpp:146 +#: src/slic3r/GUI/GUI_ObjectSettings.cpp:152 #, possible-c-format msgid "Change Option %s" msgstr "" @@ -2075,113 +2416,117 @@ msgstr "" msgid "View" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:220 src/slic3r/GUI/GUI_Preview.cpp:569 -#: src/libslic3r/GCode/PreviewData.cpp:378 +#: src/slic3r/GUI/GUI_Preview.cpp:220 src/slic3r/GUI/GUI_Preview.cpp:577 +#: src/libslic3r/GCode/PreviewData.cpp:360 msgid "Feature type" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:221 src/libslic3r/PrintConfig.cpp:483 +#: src/slic3r/GUI/GUI_Preview.cpp:221 src/libslic3r/PrintConfig.cpp:489 msgid "Height" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:222 src/libslic3r/PrintConfig.cpp:2188 +#: src/slic3r/GUI/GUI_Preview.cpp:222 src/libslic3r/PrintConfig.cpp:2215 msgid "Width" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:224 +#: src/slic3r/GUI/GUI_Preview.cpp:224 src/slic3r/GUI/Tab.cpp:1437 +msgid "Fan speed" +msgstr "" + +#: src/slic3r/GUI/GUI_Preview.cpp:225 msgid "Volumetric flow rate" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:225 src/slic3r/GUI/GUI_Preview.cpp:333 -#: src/slic3r/GUI/GUI_Preview.cpp:515 src/slic3r/GUI/GUI_Preview.cpp:568 -#: src/slic3r/GUI/GUI_Preview.cpp:774 src/libslic3r/GCode/PreviewData.cpp:388 +#: 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 "" -#: src/slic3r/GUI/GUI_Preview.cpp:226 src/slic3r/GUI/GUI_Preview.cpp:566 -#: src/libslic3r/GCode/PreviewData.cpp:390 +#: src/slic3r/GUI/GUI_Preview.cpp:227 src/slic3r/GUI/GUI_Preview.cpp:574 +#: src/libslic3r/GCode/PreviewData.cpp:374 msgid "Color Print" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:229 +#: src/slic3r/GUI/GUI_Preview.cpp:230 msgid "Show" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:232 src/slic3r/GUI/GUI_Preview.cpp:233 +#: src/slic3r/GUI/GUI_Preview.cpp:233 src/slic3r/GUI/GUI_Preview.cpp:234 msgid "Feature types" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:235 src/libslic3r/GCode/PreviewData.cpp:147 +#: src/slic3r/GUI/GUI_Preview.cpp:236 src/libslic3r/ExtrusionEntity.cpp:310 msgid "Perimeter" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:236 src/libslic3r/GCode/PreviewData.cpp:148 +#: src/slic3r/GUI/GUI_Preview.cpp:237 src/libslic3r/ExtrusionEntity.cpp:311 msgid "External perimeter" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:237 src/libslic3r/GCode/PreviewData.cpp:149 +#: src/slic3r/GUI/GUI_Preview.cpp:238 src/libslic3r/ExtrusionEntity.cpp:312 msgid "Overhang perimeter" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:238 src/libslic3r/GCode/PreviewData.cpp:150 +#: src/slic3r/GUI/GUI_Preview.cpp:239 src/libslic3r/ExtrusionEntity.cpp:313 msgid "Internal infill" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:239 src/libslic3r/PrintConfig.cpp:1736 -#: src/libslic3r/PrintConfig.cpp:1746 src/libslic3r/GCode/PreviewData.cpp:151 +#: 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 "" -#: src/slic3r/GUI/GUI_Preview.cpp:240 src/libslic3r/PrintConfig.cpp:2068 -#: src/libslic3r/PrintConfig.cpp:2079 src/libslic3r/GCode/PreviewData.cpp:152 +#: 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 "" -#: src/slic3r/GUI/GUI_Preview.cpp:241 src/libslic3r/GCode/PreviewData.cpp:153 +#: src/slic3r/GUI/GUI_Preview.cpp:242 src/libslic3r/ExtrusionEntity.cpp:316 msgid "Bridge infill" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:242 src/libslic3r/PrintConfig.cpp:895 -#: src/libslic3r/GCode/PreviewData.cpp:154 +#: src/slic3r/GUI/GUI_Preview.cpp:243 src/libslic3r/ExtrusionEntity.cpp:317 +#: src/libslic3r/PrintConfig.cpp:907 msgid "Gap fill" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:243 src/slic3r/GUI/Tab.cpp:1085 -#: src/libslic3r/GCode/PreviewData.cpp:155 +#: src/slic3r/GUI/GUI_Preview.cpp:244 src/slic3r/GUI/Tab.cpp:1091 +#: src/libslic3r/ExtrusionEntity.cpp:318 msgid "Skirt" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:245 src/libslic3r/PrintConfig.cpp:1954 -#: src/libslic3r/GCode/PreviewData.cpp:157 +#: src/slic3r/GUI/GUI_Preview.cpp:246 src/libslic3r/ExtrusionEntity.cpp:320 +#: src/libslic3r/PrintConfig.cpp:1980 msgid "Support material interface" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:246 src/slic3r/GUI/Tab.cpp:1165 -#: src/libslic3r/GCode/PreviewData.cpp:158 +#: src/slic3r/GUI/GUI_Preview.cpp:247 src/slic3r/GUI/Tab.cpp:1171 +#: src/libslic3r/ExtrusionEntity.cpp:321 msgid "Wipe tower" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:251 src/libslic3r/PrintConfig.cpp:2102 +#: src/slic3r/GUI/GUI_Preview.cpp:252 src/libslic3r/PrintConfig.cpp:2129 msgid "Travel" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:252 +#: src/slic3r/GUI/GUI_Preview.cpp:253 msgid "Retractions" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:253 +#: src/slic3r/GUI/GUI_Preview.cpp:254 msgid "Unretractions" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:254 +#: src/slic3r/GUI/GUI_Preview.cpp:255 msgid "Shells" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:255 +#: src/slic3r/GUI/GUI_Preview.cpp:256 msgid "Legend" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:14 src/slic3r/GUI/MainFrame.cpp:683 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:14 src/slic3r/GUI/MainFrame.cpp:684 msgid "Keyboard Shortcuts" msgstr "" @@ -2197,8 +2542,8 @@ msgstr "" msgid "Load Config from .ini/amf/3mf/gcode" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:110 src/slic3r/GUI/Plater.cpp:837 -#: src/slic3r/GUI/Plater.cpp:4822 src/libslic3r/PrintConfig.cpp:3163 +#: 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 "" @@ -2316,7 +2661,7 @@ msgid "Gizmo SLA support points" msgstr "" #: src/slic3r/GUI/KBShortcutsDialog.cpp:150 -#, possible-c-format +#, no-c-format msgid "" "Press to activate selection rectangle\n" "or to snap by 5% in Gizmo scale\n" @@ -2365,647 +2710,693 @@ msgid "Zoom out" msgstr "" #: src/slic3r/GUI/KBShortcutsDialog.cpp:160 +msgid "Show/Hide 3Dconnexion devices settings dialog" +msgstr "" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:161 msgid "Unselect gizmo / Clear selection" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:166 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:167 msgid "Plater Shortcuts" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:181 -#: src/slic3r/GUI/KBShortcutsDialog.cpp:193 -msgid "Arrow Up" -msgstr "" - -#: src/slic3r/GUI/KBShortcutsDialog.cpp:181 -#: src/slic3r/GUI/KBShortcutsDialog.cpp:183 -msgid "Upper Layer" -msgstr "" - #: src/slic3r/GUI/KBShortcutsDialog.cpp:182 #: src/slic3r/GUI/KBShortcutsDialog.cpp:194 -msgid "Arrow Down" +msgid "Arrow Up" msgstr "" #: src/slic3r/GUI/KBShortcutsDialog.cpp:182 #: src/slic3r/GUI/KBShortcutsDialog.cpp:184 +msgid "Upper Layer" +msgstr "" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:183 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:195 +msgid "Arrow Down" +msgstr "" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:183 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:185 msgid "Lower Layer" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:185 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:186 msgid "Show/Hide (L)egend" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:187 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:188 msgid "Preview Shortcuts" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:193 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:194 msgid "Move current slider thumb Up" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:194 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:195 msgid "Move current slider thumb Down" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:195 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:196 msgid "Arrow Left" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:195 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:196 msgid "Set upper thumb to current slider thumb" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:196 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:197 msgid "Arrow Right" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:196 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:197 msgid "Set lower thumb to current slider thumb" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:197 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:198 msgid "Add color change marker for current layer" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:198 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:199 msgid "Delete color change marker for current layer" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:200 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:201 msgid "Layers Slider Shortcuts" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:64 +#: src/slic3r/GUI/MainFrame.cpp:65 msgid "" " - Remember to check for updates at http://github.com/prusa3d/PrusaSlicer/" "releases" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:159 +#: src/slic3r/GUI/MainFrame.cpp:160 msgid "based on Slic3r" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:189 +#: src/slic3r/GUI/MainFrame.cpp:190 msgid "Plater" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:400 +#: src/slic3r/GUI/MainFrame.cpp:401 msgid "&New Project" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:400 +#: src/slic3r/GUI/MainFrame.cpp:401 msgid "Start a new project" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:403 +#: src/slic3r/GUI/MainFrame.cpp:404 msgid "&Open Project" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:403 +#: src/slic3r/GUI/MainFrame.cpp:404 msgid "Open a project file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:408 +#: src/slic3r/GUI/MainFrame.cpp:409 msgid "Recent projects" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:417 +#: src/slic3r/GUI/MainFrame.cpp:418 msgid "The selected project is no more available" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:417 src/slic3r/GUI/MainFrame.cpp:755 +#: src/slic3r/GUI/MainFrame.cpp:418 src/slic3r/GUI/MainFrame.cpp:761 #: src/slic3r/GUI/PrintHostDialogs.cpp:231 msgid "Error" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:441 +#: src/slic3r/GUI/MainFrame.cpp:442 msgid "&Save Project" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:441 +#: src/slic3r/GUI/MainFrame.cpp:442 msgid "Save current project file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:445 src/slic3r/GUI/MainFrame.cpp:447 +#: src/slic3r/GUI/MainFrame.cpp:446 src/slic3r/GUI/MainFrame.cpp:448 msgid "Save Project &as" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:445 src/slic3r/GUI/MainFrame.cpp:447 +#: src/slic3r/GUI/MainFrame.cpp:446 src/slic3r/GUI/MainFrame.cpp:448 msgid "Save current project file as" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:455 +#: src/slic3r/GUI/MainFrame.cpp:456 msgid "Import STL/OBJ/AM&F/3MF" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:455 +#: src/slic3r/GUI/MainFrame.cpp:456 msgid "Load a model" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:459 +#: src/slic3r/GUI/MainFrame.cpp:460 msgid "Import &Config" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:459 +#: src/slic3r/GUI/MainFrame.cpp:460 msgid "Load exported configuration file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:461 +#: src/slic3r/GUI/MainFrame.cpp:462 msgid "Import Config from &project" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:461 +#: src/slic3r/GUI/MainFrame.cpp:462 msgid "Load configuration from project file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:464 +#: src/slic3r/GUI/MainFrame.cpp:465 msgid "Import Config &Bundle" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:464 +#: src/slic3r/GUI/MainFrame.cpp:465 msgid "Load presets from a bundle" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:466 +#: src/slic3r/GUI/MainFrame.cpp:467 msgid "&Import" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:469 src/slic3r/GUI/MainFrame.cpp:719 +#: src/slic3r/GUI/MainFrame.cpp:470 src/slic3r/GUI/MainFrame.cpp:725 msgid "Export &G-code" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:469 +#: src/slic3r/GUI/MainFrame.cpp:470 msgid "Export current plate as G-code" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:473 src/slic3r/GUI/MainFrame.cpp:720 +#: src/slic3r/GUI/MainFrame.cpp:474 src/slic3r/GUI/MainFrame.cpp:726 msgid "S&end G-code" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:473 +#: src/slic3r/GUI/MainFrame.cpp:474 msgid "Send to print current plate as G-code" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:478 +#: src/slic3r/GUI/MainFrame.cpp:479 msgid "Export plate as &STL" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:478 +#: src/slic3r/GUI/MainFrame.cpp:479 msgid "Export current plate as STL" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:481 +#: src/slic3r/GUI/MainFrame.cpp:482 msgid "Export plate as STL &including supports" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:481 +#: src/slic3r/GUI/MainFrame.cpp:482 msgid "Export current plate as STL including supports" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:484 +#: src/slic3r/GUI/MainFrame.cpp:485 msgid "Export plate as &AMF" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:484 +#: src/slic3r/GUI/MainFrame.cpp:485 msgid "Export current plate as AMF" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:488 +#: src/slic3r/GUI/MainFrame.cpp:489 msgid "Export &toolpaths as OBJ" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:488 +#: src/slic3r/GUI/MainFrame.cpp:489 msgid "Export toolpaths as OBJ" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:492 +#: src/slic3r/GUI/MainFrame.cpp:493 msgid "Export &Config" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:492 +#: src/slic3r/GUI/MainFrame.cpp:493 msgid "Export current configuration to file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:494 +#: src/slic3r/GUI/MainFrame.cpp:495 msgid "Export Config &Bundle" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:494 +#: src/slic3r/GUI/MainFrame.cpp:495 msgid "Export all presets to file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:496 +#: src/slic3r/GUI/MainFrame.cpp:497 msgid "&Export" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:502 +#: src/slic3r/GUI/MainFrame.cpp:503 msgid "Quick Slice" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:502 +#: src/slic3r/GUI/MainFrame.cpp:503 msgid "Slice a file into a G-code" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:508 +#: src/slic3r/GUI/MainFrame.cpp:509 msgid "Quick Slice and Save As" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:508 +#: src/slic3r/GUI/MainFrame.cpp:509 msgid "Slice a file into a G-code, save as" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:514 +#: src/slic3r/GUI/MainFrame.cpp:515 msgid "Repeat Last Quick Slice" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:514 +#: src/slic3r/GUI/MainFrame.cpp:515 msgid "Repeat last quick slice" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:522 +#: src/slic3r/GUI/MainFrame.cpp:523 msgid "(Re)Slice No&w" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:522 +#: src/slic3r/GUI/MainFrame.cpp:523 msgid "Start new slicing process" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:526 +#: src/slic3r/GUI/MainFrame.cpp:527 msgid "&Repair STL file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:526 +#: src/slic3r/GUI/MainFrame.cpp:527 msgid "Automatically repair an STL file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:529 +#: src/slic3r/GUI/MainFrame.cpp:530 msgid "&Quit" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:529 +#: src/slic3r/GUI/MainFrame.cpp:530 #, possible-c-format msgid "Quit %s" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:554 +#: src/slic3r/GUI/MainFrame.cpp:555 msgid "&Select all" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:555 +#: src/slic3r/GUI/MainFrame.cpp:556 msgid "Selects all objects" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:557 +#: src/slic3r/GUI/MainFrame.cpp:558 msgid "D&eselect all" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:558 +#: src/slic3r/GUI/MainFrame.cpp:559 msgid "Deselects all objects" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:561 +#: src/slic3r/GUI/MainFrame.cpp:562 msgid "&Delete selected" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:562 +#: src/slic3r/GUI/MainFrame.cpp:563 msgid "Deletes the current selection" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:564 +#: src/slic3r/GUI/MainFrame.cpp:565 msgid "Delete &all" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:565 +#: src/slic3r/GUI/MainFrame.cpp:566 msgid "Deletes all objects" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:569 +#: src/slic3r/GUI/MainFrame.cpp:570 msgid "&Undo" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:572 +#: src/slic3r/GUI/MainFrame.cpp:573 msgid "&Redo" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:577 +#: src/slic3r/GUI/MainFrame.cpp:578 msgid "&Copy" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:578 +#: src/slic3r/GUI/MainFrame.cpp:579 msgid "Copy selection to clipboard" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:580 +#: src/slic3r/GUI/MainFrame.cpp:581 msgid "&Paste" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:581 +#: src/slic3r/GUI/MainFrame.cpp:582 msgid "Paste clipboard" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:590 +#: src/slic3r/GUI/MainFrame.cpp:591 msgid "&Plater Tab" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:590 +#: src/slic3r/GUI/MainFrame.cpp:591 msgid "Show the plater" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:597 +#: src/slic3r/GUI/MainFrame.cpp:598 msgid "P&rint Settings Tab" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:597 +#: src/slic3r/GUI/MainFrame.cpp:598 msgid "Show the print settings" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:599 src/slic3r/GUI/MainFrame.cpp:722 +#: src/slic3r/GUI/MainFrame.cpp:600 src/slic3r/GUI/MainFrame.cpp:728 msgid "&Filament Settings Tab" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:599 +#: src/slic3r/GUI/MainFrame.cpp:600 msgid "Show the filament settings" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:602 +#: src/slic3r/GUI/MainFrame.cpp:603 msgid "Print&er Settings Tab" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:602 +#: src/slic3r/GUI/MainFrame.cpp:603 msgid "Show the printer settings" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:606 +#: src/slic3r/GUI/MainFrame.cpp:607 msgid "3&D" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:606 +#: src/slic3r/GUI/MainFrame.cpp:607 msgid "Show the 3D editing view" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:609 +#: src/slic3r/GUI/MainFrame.cpp:610 msgid "Pre&view" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:609 +#: src/slic3r/GUI/MainFrame.cpp:610 msgid "Show the 3D slices preview" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:628 +#: src/slic3r/GUI/MainFrame.cpp:629 msgid "Print &Host Upload Queue" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:628 +#: src/slic3r/GUI/MainFrame.cpp:629 msgid "Display the Print Host Upload Queue window" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:637 +#: src/slic3r/GUI/MainFrame.cpp:638 msgid "Iso" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:637 +#: src/slic3r/GUI/MainFrame.cpp:638 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:641 src/libslic3r/PrintConfig.cpp:2094 +#: src/slic3r/GUI/MainFrame.cpp:642 src/libslic3r/PrintConfig.cpp:2121 msgid "Top" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:641 +#: src/slic3r/GUI/MainFrame.cpp:642 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:644 src/libslic3r/PrintConfig.cpp:159 +#: src/slic3r/GUI/MainFrame.cpp:645 src/libslic3r/PrintConfig.cpp:164 msgid "Bottom" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:644 +#: src/slic3r/GUI/MainFrame.cpp:645 msgid "Bottom View" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:646 +#: src/slic3r/GUI/MainFrame.cpp:647 msgid "Front" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:646 +#: src/slic3r/GUI/MainFrame.cpp:647 msgid "Front View" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:648 src/libslic3r/PrintConfig.cpp:1611 +#: src/slic3r/GUI/MainFrame.cpp:649 src/libslic3r/PrintConfig.cpp:1627 msgid "Rear" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:648 +#: src/slic3r/GUI/MainFrame.cpp:649 msgid "Rear View" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:650 +#: src/slic3r/GUI/MainFrame.cpp:651 msgid "Left" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:650 +#: src/slic3r/GUI/MainFrame.cpp:651 msgid "Left View" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:652 +#: src/slic3r/GUI/MainFrame.cpp:653 msgid "Right" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:652 +#: src/slic3r/GUI/MainFrame.cpp:653 msgid "Right View" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:659 +#: src/slic3r/GUI/MainFrame.cpp:660 msgid "Prusa 3D &Drivers" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:659 +#: src/slic3r/GUI/MainFrame.cpp:660 msgid "Open the Prusa3D drivers download page in your browser" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:661 +#: src/slic3r/GUI/MainFrame.cpp:662 msgid "Software &Releases" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:661 +#: src/slic3r/GUI/MainFrame.cpp:662 msgid "Open the software releases page in your browser" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:667 -#, possible-c-format -msgid "%s &Website" -msgstr "" - #: src/slic3r/GUI/MainFrame.cpp:668 #, possible-c-format +msgid "%s &Website" +msgstr "" + +#: src/slic3r/GUI/MainFrame.cpp:669 +#, possible-c-format msgid "Open the %s website in your browser" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:674 +#: src/slic3r/GUI/MainFrame.cpp:675 msgid "System &Info" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:674 +#: src/slic3r/GUI/MainFrame.cpp:675 msgid "Show system information" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:676 +#: src/slic3r/GUI/MainFrame.cpp:677 msgid "Show &Configuration Folder" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:676 +#: src/slic3r/GUI/MainFrame.cpp:677 msgid "Show user configuration folder (datadir)" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:678 +#: src/slic3r/GUI/MainFrame.cpp:679 msgid "Report an I&ssue" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:678 +#: src/slic3r/GUI/MainFrame.cpp:679 #, possible-c-format msgid "Report an issue on %s" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:680 +#: src/slic3r/GUI/MainFrame.cpp:681 #, possible-c-format msgid "&About %s" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:680 +#: src/slic3r/GUI/MainFrame.cpp:681 msgid "Show about dialog" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:683 +#: src/slic3r/GUI/MainFrame.cpp:684 msgid "Show the list of the keyboard shortcuts" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:691 -msgid "&File" +#: src/slic3r/GUI/MainFrame.cpp:688 +msgid "DEBUG gcode thumbnails" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:692 -msgid "&Edit" -msgstr "" - -#: src/slic3r/GUI/MainFrame.cpp:693 -msgid "&Window" -msgstr "" - -#: src/slic3r/GUI/MainFrame.cpp:694 -msgid "&View" +#: src/slic3r/GUI/MainFrame.cpp:688 +msgid "" +"DEBUG ONLY - read the selected gcode file and generates png for the " +"contained thumbnails" msgstr "" #: src/slic3r/GUI/MainFrame.cpp:697 +msgid "&File" +msgstr "" + +#: src/slic3r/GUI/MainFrame.cpp:698 +msgid "&Edit" +msgstr "" + +#: src/slic3r/GUI/MainFrame.cpp:699 +msgid "&Window" +msgstr "" + +#: src/slic3r/GUI/MainFrame.cpp:700 +msgid "&View" +msgstr "" + +#: src/slic3r/GUI/MainFrame.cpp:703 msgid "&Help" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:719 +#: src/slic3r/GUI/MainFrame.cpp:725 msgid "E&xport" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:720 +#: src/slic3r/GUI/MainFrame.cpp:726 msgid "S&end to print" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:722 +#: src/slic3r/GUI/MainFrame.cpp:728 msgid "Mate&rial Settings Tab" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:743 +#: src/slic3r/GUI/MainFrame.cpp:749 msgid "Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:754 +#: src/slic3r/GUI/MainFrame.cpp:760 msgid "No previously sliced file." msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:760 +#: src/slic3r/GUI/MainFrame.cpp:766 msgid "Previously sliced file (" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:760 +#: src/slic3r/GUI/MainFrame.cpp:766 msgid ") not found." msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:761 +#: src/slic3r/GUI/MainFrame.cpp:767 msgid "File Not Found" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:796 +#: src/slic3r/GUI/MainFrame.cpp:802 #, possible-c-format msgid "Save %s file as:" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:796 +#: src/slic3r/GUI/MainFrame.cpp:802 msgid "SVG" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:796 +#: src/slic3r/GUI/MainFrame.cpp:802 msgid "G-code" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:808 +#: src/slic3r/GUI/MainFrame.cpp:814 msgid "Save zip file as:" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:817 src/slic3r/GUI/Plater.cpp:2981 -#: src/slic3r/GUI/Plater.cpp:4533 src/slic3r/GUI/Tab.cpp:1194 -#: src/slic3r/GUI/Tab.cpp:3786 +#: 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:3608 msgid "Slicing" msgstr "" #. TRN "Processing input_file_basename" -#: src/slic3r/GUI/MainFrame.cpp:819 +#: src/slic3r/GUI/MainFrame.cpp:825 #, possible-c-format msgid "Processing %s" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:842 +#: src/slic3r/GUI/MainFrame.cpp:848 msgid " was successfully sliced." msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:844 +#: src/slic3r/GUI/MainFrame.cpp:850 msgid "Slicing Done!" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:859 +#: src/slic3r/GUI/MainFrame.cpp:865 msgid "Select the STL file to repair:" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:869 +#: src/slic3r/GUI/MainFrame.cpp:875 msgid "Save OBJ file (less prone to coordinate errors than STL) as:" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:881 +#: src/slic3r/GUI/MainFrame.cpp:887 msgid "Your file was repaired." msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:881 src/libslic3r/PrintConfig.cpp:3257 +#: src/slic3r/GUI/MainFrame.cpp:887 src/libslic3r/PrintConfig.cpp:3374 msgid "Repair" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:895 +#: src/slic3r/GUI/MainFrame.cpp:901 msgid "Save configuration as:" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:914 src/slic3r/GUI/MainFrame.cpp:976 +#: src/slic3r/GUI/MainFrame.cpp:920 src/slic3r/GUI/MainFrame.cpp:982 msgid "Select configuration to load:" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:950 +#: src/slic3r/GUI/MainFrame.cpp:956 msgid "Save presets bundle as:" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:997 +#: src/slic3r/GUI/MainFrame.cpp:1003 #, possible-c-format msgid "%d presets successfully imported." msgstr "" +#: src/slic3r/GUI/Mouse3DController.cpp:255 +msgid "3Dconnexion settings" +msgstr "" + +#: src/slic3r/GUI/Mouse3DController.cpp:259 +msgid "Device:" +msgstr "" + +#: src/slic3r/GUI/Mouse3DController.cpp:266 +msgid "Speed:" +msgstr "" + +#: src/slic3r/GUI/Mouse3DController.cpp:270 +msgid "Translation##1" +msgstr "" + +#: src/slic3r/GUI/Mouse3DController.cpp:274 +msgid "Rotation##1" +msgstr "" + +#: src/slic3r/GUI/Mouse3DController.cpp:279 +msgid "Deadzone:" +msgstr "" + +#: src/slic3r/GUI/Mouse3DController.cpp:283 +msgid "Translation##2" +msgstr "" + +#: src/slic3r/GUI/Mouse3DController.cpp:287 +msgid "Rotation##2" +msgstr "" + #: src/slic3r/GUI/MsgDialog.cpp:73 #, possible-c-format msgid "%s error" @@ -3026,564 +3417,564 @@ msgctxt "Layers" msgid "Bottom" msgstr "" -#: src/slic3r/GUI/Plater.cpp:146 +#: src/slic3r/GUI/Plater.cpp:155 msgid "Volume" msgstr "" -#: src/slic3r/GUI/Plater.cpp:147 +#: src/slic3r/GUI/Plater.cpp:156 msgid "Facets" msgstr "" -#: src/slic3r/GUI/Plater.cpp:148 +#: src/slic3r/GUI/Plater.cpp:157 msgid "Materials" msgstr "" -#: src/slic3r/GUI/Plater.cpp:151 +#: src/slic3r/GUI/Plater.cpp:160 msgid "Manifold" msgstr "" -#: src/slic3r/GUI/Plater.cpp:201 +#: src/slic3r/GUI/Plater.cpp:210 msgid "Sliced Info" msgstr "" -#: src/slic3r/GUI/Plater.cpp:220 src/slic3r/GUI/Plater.cpp:1150 +#: src/slic3r/GUI/Plater.cpp:229 src/slic3r/GUI/Plater.cpp:1179 msgid "Used Filament (m)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:221 +#: src/slic3r/GUI/Plater.cpp:230 msgid "Used Filament (mm³)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:222 +#: src/slic3r/GUI/Plater.cpp:231 msgid "Used Filament (g)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:223 +#: src/slic3r/GUI/Plater.cpp:232 msgid "Used Material (unit)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:224 src/slic3r/GUI/Plater.cpp:1165 -#: src/libslic3r/PrintConfig.cpp:742 -msgid "Cost" +#: src/slic3r/GUI/Plater.cpp:233 +msgid "Cost (money)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:225 src/slic3r/GUI/Plater.cpp:1137 -#: src/slic3r/GUI/Plater.cpp:1179 +#: src/slic3r/GUI/Plater.cpp:234 src/slic3r/GUI/Plater.cpp:1166 +#: src/slic3r/GUI/Plater.cpp:1208 msgid "Estimated printing time" msgstr "" -#: src/slic3r/GUI/Plater.cpp:226 +#: src/slic3r/GUI/Plater.cpp:235 msgid "Number of tool changes" msgstr "" -#: src/slic3r/GUI/Plater.cpp:316 +#: src/slic3r/GUI/Plater.cpp:332 msgid "Click to edit preset" msgstr "" -#: src/slic3r/GUI/Plater.cpp:468 +#: src/slic3r/GUI/Plater.cpp:487 msgid "Select what kind of support do you need" msgstr "" -#: src/slic3r/GUI/Plater.cpp:470 src/libslic3r/PrintConfig.cpp:1865 -#: src/libslic3r/PrintConfig.cpp:2561 +#: src/slic3r/GUI/Plater.cpp:489 src/libslic3r/PrintConfig.cpp:1890 +#: src/libslic3r/PrintConfig.cpp:2642 msgid "Support on build plate only" msgstr "" -#: src/slic3r/GUI/Plater.cpp:471 src/slic3r/GUI/Plater.cpp:592 +#: src/slic3r/GUI/Plater.cpp:490 src/slic3r/GUI/Plater.cpp:613 msgid "For support enforcers only" msgstr "" -#: src/slic3r/GUI/Plater.cpp:472 +#: src/slic3r/GUI/Plater.cpp:491 msgid "Everywhere" msgstr "" -#: src/slic3r/GUI/Plater.cpp:504 src/slic3r/GUI/Tab.cpp:1091 +#: src/slic3r/GUI/Plater.cpp:523 src/slic3r/GUI/Tab.cpp:1097 msgid "Brim" msgstr "" -#: src/slic3r/GUI/Plater.cpp:506 +#: src/slic3r/GUI/Plater.cpp:525 msgid "" "This flag enables the brim that will be printed around each object on the " "first layer." msgstr "" -#: src/slic3r/GUI/Plater.cpp:514 +#: src/slic3r/GUI/Plater.cpp:533 msgid "Purging volumes" msgstr "" -#: src/slic3r/GUI/Plater.cpp:606 +#: src/slic3r/GUI/Plater.cpp:627 msgid "Select what kind of pad do you need" msgstr "" -#: src/slic3r/GUI/Plater.cpp:608 +#: src/slic3r/GUI/Plater.cpp:629 msgid "Below object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:609 +#: src/slic3r/GUI/Plater.cpp:630 msgid "Around object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:781 +#: src/slic3r/GUI/Plater.cpp:802 msgid "Print settings" msgstr "" -#: src/slic3r/GUI/Plater.cpp:782 src/slic3r/GUI/Tab.cpp:1640 -#: src/slic3r/GUI/Tab.cpp:1641 +#: src/slic3r/GUI/Plater.cpp:803 src/slic3r/GUI/Tab.cpp:1405 +#: src/slic3r/GUI/Tab.cpp:1406 msgid "Filament" msgstr "" -#: src/slic3r/GUI/Plater.cpp:783 +#: src/slic3r/GUI/Plater.cpp:804 msgid "SLA print settings" msgstr "" -#: src/slic3r/GUI/Plater.cpp:784 src/slic3r/GUI/Preset.cpp:1314 +#: src/slic3r/GUI/Plater.cpp:805 src/slic3r/GUI/Preset.cpp:1395 msgid "SLA material" msgstr "" -#: src/slic3r/GUI/Plater.cpp:785 +#: src/slic3r/GUI/Plater.cpp:806 msgid "Printer" msgstr "" -#: src/slic3r/GUI/Plater.cpp:835 src/slic3r/GUI/Plater.cpp:4823 +#: src/slic3r/GUI/Plater.cpp:856 src/slic3r/GUI/Plater.cpp:5143 msgid "Send to printer" msgstr "" -#: src/slic3r/GUI/Plater.cpp:838 src/slic3r/GUI/Plater.cpp:2981 -#: src/slic3r/GUI/Plater.cpp:4536 +#: src/slic3r/GUI/Plater.cpp:859 src/slic3r/GUI/Plater.cpp:3058 +#: src/slic3r/GUI/Plater.cpp:4784 msgid "Slice now" msgstr "" -#: src/slic3r/GUI/Plater.cpp:978 +#: src/slic3r/GUI/Plater.cpp:999 msgid "Hold Shift to Slice & Export G-code" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1083 +#: src/slic3r/GUI/Plater.cpp:1102 #, possible-c-format msgid "%d (%d shells)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1088 +#: src/slic3r/GUI/Plater.cpp:1107 #, possible-c-format msgid "Auto-repaired (%d errors)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1091 +#: src/slic3r/GUI/Plater.cpp:1110 #, 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:1101 +#: src/slic3r/GUI/Plater.cpp:1120 msgid "Yes" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1124 +#: src/slic3r/GUI/Plater.cpp:1141 msgid "Used Material (ml)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1127 +#: src/slic3r/GUI/Plater.cpp:1144 msgid "object(s)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1127 +#: src/slic3r/GUI/Plater.cpp:1144 msgid "supports and pad" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1152 src/slic3r/GUI/Plater.cpp:1167 +#: src/slic3r/GUI/Plater.cpp:1181 src/slic3r/GUI/Plater.cpp:1196 msgid "objects" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1152 src/slic3r/GUI/Plater.cpp:1167 +#: src/slic3r/GUI/Plater.cpp:1181 src/slic3r/GUI/Plater.cpp:1196 msgid "wipe tower" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1182 +#: src/slic3r/GUI/Plater.cpp:1194 src/libslic3r/PrintConfig.cpp:749 +#: src/libslic3r/PrintConfig.cpp:2478 src/libslic3r/PrintConfig.cpp:2479 +msgid "Cost" +msgstr "" + +#: src/slic3r/GUI/Plater.cpp:1211 msgid "normal mode" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1186 src/slic3r/GUI/Plater.cpp:1195 -#: src/libslic3r/PrintConfig.cpp:565 +#: src/slic3r/GUI/Plater.cpp:1215 src/slic3r/GUI/Plater.cpp:1224 +#: src/libslic3r/PrintConfig.cpp:572 msgid "Color" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1191 +#: src/slic3r/GUI/Plater.cpp:1220 msgid "stealth mode" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1286 +#: src/slic3r/GUI/Plater.cpp:1324 msgid "Load File" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1290 +#: src/slic3r/GUI/Plater.cpp:1328 msgid "Load Files" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1519 +#: src/slic3r/GUI/Plater.cpp:1561 msgid "ERROR: not enough resources to execute a new job." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2089 +#: src/slic3r/GUI/Plater.cpp:2158 msgid "New Project" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2206 +#: src/slic3r/GUI/Plater.cpp:2277 msgid "Loading" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2216 +#: src/slic3r/GUI/Plater.cpp:2287 #, possible-c-format -msgid "Processing input file %s\n" +msgid "Processing input file %s" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2244 +#: 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 "" -#: src/slic3r/GUI/Plater.cpp:2245 src/slic3r/GUI/Tab.cpp:3146 +#: src/slic3r/GUI/Plater.cpp:2316 src/slic3r/GUI/Tab.cpp:2914 msgid "Please check your object list before preset changing." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2288 +#: src/slic3r/GUI/Plater.cpp:2361 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 "" -#: src/slic3r/GUI/Plater.cpp:2291 src/slic3r/GUI/Plater.cpp:2343 +#: src/slic3r/GUI/Plater.cpp:2364 src/slic3r/GUI/Plater.cpp:2417 msgid "Multi-part object detected" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2298 +#: 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?\n" +"advanced mode?" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2299 +#: src/slic3r/GUI/Plater.cpp:2372 msgid "Detected advanced data" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2320 +#: src/slic3r/GUI/Plater.cpp:2394 #, 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:2340 +#: 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?\n" +"these files to represent a single object having multiple parts?" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2356 +#: src/slic3r/GUI/Plater.cpp:2430 msgid "Loaded" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2458 +#: 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 "" -#: src/slic3r/GUI/Plater.cpp:2459 +#: src/slic3r/GUI/Plater.cpp:2533 msgid "Object too large?" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2517 +#: src/slic3r/GUI/Plater.cpp:2595 msgid "Export STL file:" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2524 +#: src/slic3r/GUI/Plater.cpp:2602 msgid "Export AMF file:" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2530 +#: src/slic3r/GUI/Plater.cpp:2608 msgid "Save file as:" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2536 +#: src/slic3r/GUI/Plater.cpp:2614 msgid "Export OBJ file:" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2638 +#: src/slic3r/GUI/Plater.cpp:2716 msgid "Delete Object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2649 +#: src/slic3r/GUI/Plater.cpp:2727 msgid "Reset Project" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2688 +#: src/slic3r/GUI/Plater.cpp:2765 msgid "Optimize Rotation" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2734 +#: src/slic3r/GUI/Plater.cpp:2811 msgid "Arranging" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2757 +#: src/slic3r/GUI/Plater.cpp:2833 msgid "Could not arrange model objects! Some geometries may be invalid." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2763 +#: src/slic3r/GUI/Plater.cpp:2839 msgid "Arranging canceled." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2764 +#: src/slic3r/GUI/Plater.cpp:2840 msgid "Arranging done." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2780 +#: src/slic3r/GUI/Plater.cpp:2856 msgid "Searching for optimal orientation" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2813 +#: src/slic3r/GUI/Plater.cpp:2889 msgid "Orientation search canceled." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2814 +#: src/slic3r/GUI/Plater.cpp:2890 msgid "Orientation found." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2830 +#: src/slic3r/GUI/Plater.cpp:2906 msgid "" "The selected object can't be split because it contains more than one volume/" "material." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2841 +#: src/slic3r/GUI/Plater.cpp:2917 msgid "Split to Objects" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2966 +#: src/slic3r/GUI/Plater.cpp:3043 msgid "Invalid data" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2975 +#: src/slic3r/GUI/Plater.cpp:3052 msgid "Ready to slice" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3013 src/slic3r/GUI/PrintHostDialogs.cpp:232 +#: src/slic3r/GUI/Plater.cpp:3090 src/slic3r/GUI/PrintHostDialogs.cpp:232 msgid "Cancelling" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3030 +#: src/slic3r/GUI/Plater.cpp:3107 msgid "Another export job is currently running." msgstr "" -#: src/slic3r/GUI/Plater.cpp:3084 src/slic3r/GUI/Plater.cpp:3549 -msgid "Reload from Disk" -msgstr "" - -#: src/slic3r/GUI/Plater.cpp:3120 +#: src/slic3r/GUI/Plater.cpp:3276 msgid "Fix Throught NetFabb" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3307 +#: src/slic3r/GUI/Plater.cpp:3467 msgid "Export failed" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3312 src/slic3r/GUI/PrintHostDialogs.cpp:233 +#: src/slic3r/GUI/Plater.cpp:3472 src/slic3r/GUI/PrintHostDialogs.cpp:233 msgid "Cancelled" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3520 src/slic3r/GUI/Plater.cpp:3539 +#: src/slic3r/GUI/Plater.cpp:3712 src/slic3r/GUI/Plater.cpp:3734 msgid "Remove the selected object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3526 +#: src/slic3r/GUI/Plater.cpp:3721 msgid "Add one more instance of the selected object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3528 +#: src/slic3r/GUI/Plater.cpp:3723 msgid "Remove one instance of the selected object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3530 +#: src/slic3r/GUI/Plater.cpp:3725 msgid "Set number of instances" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3530 +#: src/slic3r/GUI/Plater.cpp:3725 msgid "Change the number of instances of the selected object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3549 -msgid "Reload the selected file from Disk" +#: src/slic3r/GUI/Plater.cpp:3744 +msgid "Reload the selected object from disk" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3552 +#: src/slic3r/GUI/Plater.cpp:3747 msgid "Export the selected object as STL file" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3577 +#: src/slic3r/GUI/Plater.cpp:3772 msgid "Along X axis" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3577 +#: src/slic3r/GUI/Plater.cpp:3772 msgid "Mirror the selected object along the X axis" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3579 +#: src/slic3r/GUI/Plater.cpp:3774 msgid "Along Y axis" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3579 +#: src/slic3r/GUI/Plater.cpp:3774 msgid "Mirror the selected object along the Y axis" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3581 +#: src/slic3r/GUI/Plater.cpp:3776 msgid "Along Z axis" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3581 +#: src/slic3r/GUI/Plater.cpp:3776 msgid "Mirror the selected object along the Z axis" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3584 +#: src/slic3r/GUI/Plater.cpp:3779 msgid "Mirror" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3584 +#: src/slic3r/GUI/Plater.cpp:3779 msgid "Mirror the selected object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3596 +#: src/slic3r/GUI/Plater.cpp:3791 msgid "To objects" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3596 src/slic3r/GUI/Plater.cpp:3616 +#: src/slic3r/GUI/Plater.cpp:3791 src/slic3r/GUI/Plater.cpp:3811 msgid "Split the selected object into individual objects" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3598 +#: src/slic3r/GUI/Plater.cpp:3793 msgid "To parts" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3598 src/slic3r/GUI/Plater.cpp:3630 +#: src/slic3r/GUI/Plater.cpp:3793 src/slic3r/GUI/Plater.cpp:3825 msgid "Split the selected object into individual sub-parts" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3601 src/slic3r/GUI/Plater.cpp:3616 -#: src/slic3r/GUI/Plater.cpp:3630 src/libslic3r/PrintConfig.cpp:3281 +#: 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 "" -#: src/slic3r/GUI/Plater.cpp:3601 +#: src/slic3r/GUI/Plater.cpp:3796 msgid "Split the selected object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3622 +#: src/slic3r/GUI/Plater.cpp:3817 msgid "Optimize orientation" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3622 +#: src/slic3r/GUI/Plater.cpp:3817 msgid "Optimize the rotation of the object for better print results." msgstr "" -#: src/slic3r/GUI/Plater.cpp:3662 +#: src/slic3r/GUI/Plater.cpp:3857 msgid "3D editor view" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3670 src/slic3r/GUI/Tab.cpp:2590 +#: src/slic3r/GUI/Plater.cpp:3865 src/slic3r/GUI/Tab.cpp:2358 msgid "Preview" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3907 +#: 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 "" -#: src/slic3r/GUI/Plater.cpp:4081 +#: src/slic3r/GUI/Plater.cpp:4319 msgid "Load Project" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4109 +#: src/slic3r/GUI/Plater.cpp:4347 msgid "Import Object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4113 +#: src/slic3r/GUI/Plater.cpp:4351 msgid "Import Objects" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4172 +#: src/slic3r/GUI/Plater.cpp:4410 msgid "All objects will be removed, continue ?" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4180 +#: src/slic3r/GUI/Plater.cpp:4418 msgid "Delete Selected Objects" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4188 +#: src/slic3r/GUI/Plater.cpp:4426 msgid "Increase Instances" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4224 +#: src/slic3r/GUI/Plater.cpp:4461 msgid "Decrease Instances" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4260 +#: src/slic3r/GUI/Plater.cpp:4497 #, possible-c-format msgid "Set numbers of copies to %d" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4290 +#: src/slic3r/GUI/Plater.cpp:4527 msgid "Cut by Plane" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4322 +#: src/slic3r/GUI/Plater.cpp:4559 msgid "Save G-code file as:" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4322 +#: src/slic3r/GUI/Plater.cpp:4559 msgid "Save SL1 file as:" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4434 +#: src/slic3r/GUI/Plater.cpp:4671 #, possible-c-format msgid "STL file exported to %s" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4450 +#: src/slic3r/GUI/Plater.cpp:4687 #, possible-c-format msgid "AMF file exported to %s" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4453 +#: src/slic3r/GUI/Plater.cpp:4690 #, possible-c-format msgid "Error exporting AMF file %s" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4479 +#: src/slic3r/GUI/Plater.cpp:4722 #, possible-c-format msgid "3MF file exported to %s" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4484 +#: src/slic3r/GUI/Plater.cpp:4727 #, possible-c-format msgid "Error exporting 3MF file %s" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4822 +#: src/slic3r/GUI/Plater.cpp:5142 msgid "Export" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4823 +#: src/slic3r/GUI/Plater.cpp:5143 msgid "Send G-code" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4907 +#: src/slic3r/GUI/Plater.cpp:5227 msgid "Paste From Clipboard" msgstr "" -#: src/slic3r/GUI/Preferences.cpp:22 src/slic3r/GUI/Tab.cpp:2001 -#: src/slic3r/GUI/Tab.cpp:2242 +#: src/slic3r/GUI/Preferences.cpp:22 src/slic3r/GUI/Tab.cpp:1766 +#: src/slic3r/GUI/Tab.cpp:2010 msgid "General" msgstr "" @@ -3693,33 +4084,41 @@ msgstr "" msgid "Select toolbar icon size in respect to the default one." msgstr "" -#: src/slic3r/GUI/Preset.cpp:212 +#: src/slic3r/GUI/Preset.cpp:237 msgid "modified" msgstr "" -#: src/slic3r/GUI/Preset.cpp:967 src/slic3r/GUI/Preset.cpp:1007 -#: src/slic3r/GUI/Preset.cpp:1072 src/slic3r/GUI/Preset.cpp:1104 -#: src/slic3r/GUI/PresetBundle.cpp:1484 src/slic3r/GUI/PresetBundle.cpp:1559 +#: src/slic3r/GUI/Preset.cpp:1018 src/slic3r/GUI/Preset.cpp:1065 +#: src/slic3r/GUI/Preset.cpp:1141 src/slic3r/GUI/Preset.cpp:1175 +#: src/slic3r/GUI/PresetBundle.cpp:1583 src/slic3r/GUI/PresetBundle.cpp:1667 msgid "System presets" msgstr "" -#: src/slic3r/GUI/Preset.cpp:1011 src/slic3r/GUI/Preset.cpp:1108 -#: src/slic3r/GUI/PresetBundle.cpp:1564 +#: src/slic3r/GUI/Preset.cpp:1069 src/slic3r/GUI/Preset.cpp:1179 +#: src/slic3r/GUI/PresetBundle.cpp:1672 msgid "User presets" msgstr "" -#: src/slic3r/GUI/Preset.cpp:1040 src/slic3r/GUI/Tab.cpp:243 +#: src/slic3r/GUI/Preset.cpp:1100 src/slic3r/GUI/Tab.cpp:243 msgid "Add a new printer" msgstr "" -#: src/slic3r/GUI/Preset.cpp:1312 +#: src/slic3r/GUI/Preset.cpp:1102 +msgid "Add/Remove materials" +msgstr "" + +#: src/slic3r/GUI/Preset.cpp:1393 msgid "filament" msgstr "" -#: src/slic3r/GUI/Preset.cpp:1313 +#: src/slic3r/GUI/Preset.cpp:1394 msgid "SLA print" msgstr "" +#: src/slic3r/GUI/PresetBundle.cpp:1683 +msgid "Add/Remove filaments" +msgstr "" + #: src/slic3r/GUI/PresetHints.cpp:28 msgid "" "If estimated layer time is below ~%1%s, fan will run at %2%%% and print " @@ -3729,15 +4128,12 @@ msgstr "" #: src/slic3r/GUI/PresetHints.cpp:35 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 "" #: src/slic3r/GUI/PresetHints.cpp:39 -msgid "" -"\n" -"During the other layers, fan" +msgid "During the other layers, fan" msgstr "" #: src/slic3r/GUI/PresetHints.cpp:41 @@ -3910,13 +4306,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:2426 src/libslic3r/PrintConfig.cpp:2434 -#: src/libslic3r/PrintConfig.cpp:2441 src/libslic3r/PrintConfig.cpp:2449 -#: src/libslic3r/PrintConfig.cpp:2457 +#: 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 "" @@ -3924,8 +4320,8 @@ msgstr "" msgid "Volumetric speed" msgstr "" -#: 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:591 +#: src/libslic3r/PrintConfig.cpp:1247 msgid "mm³/s" msgstr "" @@ -3981,7 +4377,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:244 msgid "Compatible printers" msgstr "" @@ -3989,7 +4385,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:259 msgid "Compatible print profiles" msgstr "" @@ -4069,229 +4465,233 @@ msgstr "" msgid "default SLA print profile" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1032 src/slic3r/GUI/Tab.cpp:3731 +#: 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:3551 msgid "Layers and perimeters" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1037 +#: src/slic3r/GUI/Tab.cpp:1043 msgid "Vertical shells" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1048 +#: src/slic3r/GUI/Tab.cpp:1054 msgid "Horizontal shells" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1049 src/libslic3r/PrintConfig.cpp:1759 +#: src/slic3r/GUI/Tab.cpp:1055 src/libslic3r/PrintConfig.cpp:1776 msgid "Solid layers" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1054 +#: src/slic3r/GUI/Tab.cpp:1060 msgid "Quality (slower slicing)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1072 +#: src/slic3r/GUI/Tab.cpp:1078 msgid "Reducing printing time" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1084 +#: src/slic3r/GUI/Tab.cpp:1090 msgid "Skirt and brim" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1101 +#: src/slic3r/GUI/Tab.cpp:1107 msgid "Raft" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1105 +#: src/slic3r/GUI/Tab.cpp:1111 msgid "Options for support material and raft" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1120 +#: src/slic3r/GUI/Tab.cpp:1126 msgid "Speed for print moves" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1132 +#: src/slic3r/GUI/Tab.cpp:1138 msgid "Speed for non-print moves" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1135 +#: src/slic3r/GUI/Tab.cpp:1141 msgid "Modifiers" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1138 +#: src/slic3r/GUI/Tab.cpp:1144 msgid "Acceleration control (advanced)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1145 +#: src/slic3r/GUI/Tab.cpp:1151 msgid "Autospeed (advanced)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1153 +#: src/slic3r/GUI/Tab.cpp:1159 msgid "Multiple Extruders" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1161 +#: src/slic3r/GUI/Tab.cpp:1167 msgid "Ooze prevention" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1178 +#: src/slic3r/GUI/Tab.cpp:1185 msgid "Extrusion width" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1188 +#: src/slic3r/GUI/Tab.cpp:1195 msgid "Overlap" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1191 +#: src/slic3r/GUI/Tab.cpp:1198 msgid "Flow" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1200 +#: src/slic3r/GUI/Tab.cpp:1207 msgid "Other" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1203 src/slic3r/GUI/Tab.cpp:3789 +#: src/slic3r/GUI/Tab.cpp:1210 src/slic3r/GUI/Tab.cpp:3611 msgid "Output options" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1204 +#: src/slic3r/GUI/Tab.cpp:1211 msgid "Sequential printing" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1206 +#: src/slic3r/GUI/Tab.cpp:1213 msgid "Extruder clearance (mm)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1215 src/slic3r/GUI/Tab.cpp:3790 +#: src/slic3r/GUI/Tab.cpp:1222 src/slic3r/GUI/Tab.cpp:3612 msgid "Output file" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1222 src/libslic3r/PrintConfig.cpp:1432 +#: src/slic3r/GUI/Tab.cpp:1229 src/libslic3r/PrintConfig.cpp:1448 msgid "Post-processing scripts" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1228 src/slic3r/GUI/Tab.cpp:1229 -#: src/slic3r/GUI/Tab.cpp:1752 src/slic3r/GUI/Tab.cpp:1753 -#: src/slic3r/GUI/Tab.cpp:2214 src/slic3r/GUI/Tab.cpp:2215 -#: src/slic3r/GUI/Tab.cpp:2328 src/slic3r/GUI/Tab.cpp:2329 -#: src/slic3r/GUI/Tab.cpp:3668 src/slic3r/GUI/Tab.cpp:3669 +#: 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:3488 src/slic3r/GUI/Tab.cpp:3489 msgid "Notes" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1235 src/slic3r/GUI/Tab.cpp:1760 -#: src/slic3r/GUI/Tab.cpp:2221 src/slic3r/GUI/Tab.cpp:2335 -#: src/slic3r/GUI/Tab.cpp:3676 src/slic3r/GUI/Tab.cpp:3795 +#: 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:3496 src/slic3r/GUI/Tab.cpp:3617 msgid "Dependencies" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1236 src/slic3r/GUI/Tab.cpp:1761 -#: src/slic3r/GUI/Tab.cpp:2222 src/slic3r/GUI/Tab.cpp:2336 -#: src/slic3r/GUI/Tab.cpp:3677 src/slic3r/GUI/Tab.cpp:3796 +#: 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:3497 src/slic3r/GUI/Tab.cpp:3618 msgid "Profile dependencies" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1538 src/slic3r/GUI/Tab.cpp:1593 +#: src/slic3r/GUI/Tab.cpp:1303 src/slic3r/GUI/Tab.cpp:1358 msgid "Filament Overrides" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1539 src/slic3r/GUI/Tab.cpp:1598 -#: src/slic3r/GUI/Tab.cpp:2570 +#: src/slic3r/GUI/Tab.cpp:1304 src/slic3r/GUI/Tab.cpp:1363 +#: src/slic3r/GUI/Tab.cpp:2338 msgid "Retraction" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1648 src/libslic3r/PrintConfig.cpp:2030 +#: src/slic3r/GUI/Tab.cpp:1413 src/libslic3r/PrintConfig.cpp:2056 msgid "Temperature" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1654 +#: src/slic3r/GUI/Tab.cpp:1419 msgid "Bed" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1659 +#: src/slic3r/GUI/Tab.cpp:1424 msgid "Cooling" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1660 src/libslic3r/PrintConfig.cpp:1335 -#: src/libslic3r/PrintConfig.cpp:2150 +#: src/slic3r/GUI/Tab.cpp:1425 src/libslic3r/PrintConfig.cpp:1350 +#: src/libslic3r/PrintConfig.cpp:2177 msgid "Enable" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1671 +#: src/slic3r/GUI/Tab.cpp:1436 msgid "Fan settings" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1672 -msgid "Fan speed" -msgstr "" - -#: src/slic3r/GUI/Tab.cpp:1680 +#: src/slic3r/GUI/Tab.cpp:1445 msgid "Cooling thresholds" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1686 +#: src/slic3r/GUI/Tab.cpp:1451 msgid "Filament properties" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1690 +#: src/slic3r/GUI/Tab.cpp:1455 msgid "Print speed override" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1700 +#: src/slic3r/GUI/Tab.cpp:1465 msgid "Wipe tower parameters" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1703 +#: src/slic3r/GUI/Tab.cpp:1468 msgid "Toolchange parameters with single extruder MM printers" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1717 +#: src/slic3r/GUI/Tab.cpp:1482 msgid "Ramming settings" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1739 src/slic3r/GUI/Tab.cpp:2177 +#: src/slic3r/GUI/Tab.cpp:1504 src/slic3r/GUI/Tab.cpp:1945 msgid "Custom G-code" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1740 src/slic3r/GUI/Tab.cpp:2178 -#: src/libslic3r/PrintConfig.cpp:1785 src/libslic3r/PrintConfig.cpp:1800 +#: 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 "" -#: src/slic3r/GUI/Tab.cpp:1746 src/slic3r/GUI/Tab.cpp:2184 -#: src/libslic3r/PrintConfig.cpp:369 src/libslic3r/PrintConfig.cpp:379 +#: 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 "" -#: src/slic3r/GUI/Tab.cpp:1803 +#: src/slic3r/GUI/Tab.cpp:1568 msgid "Volumetric flow hints not available" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1889 src/slic3r/GUI/Tab.cpp:2117 +#: src/slic3r/GUI/Tab.cpp:1654 src/slic3r/GUI/Tab.cpp:1885 msgid "Test" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1899 +#: src/slic3r/GUI/Tab.cpp:1664 msgid "Could not get a valid Printer Host reference" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1905 src/slic3r/GUI/Tab.cpp:2130 +#: src/slic3r/GUI/Tab.cpp:1670 src/slic3r/GUI/Tab.cpp:1898 msgid "Success!" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1920 +#: 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 "" -#: src/slic3r/GUI/Tab.cpp:1933 +#: src/slic3r/GUI/Tab.cpp:1698 msgid "Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1934 +#: src/slic3r/GUI/Tab.cpp:1699 msgid "Open CA certificate file" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1962 +#: src/slic3r/GUI/Tab.cpp:1727 #, possible-c-format msgid "" "HTTPS CA File:\n" @@ -4301,24 +4701,24 @@ msgid "" "Store / Keychain." msgstr "" -#: src/slic3r/GUI/Tab.cpp:2002 src/slic3r/GUI/Tab.cpp:2243 +#: src/slic3r/GUI/Tab.cpp:1767 src/slic3r/GUI/Tab.cpp:2011 msgid "Size and coordinates" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2007 src/slic3r/GUI/Tab.cpp:2248 -#: src/slic3r/GUI/Tab.cpp:3338 +#: src/slic3r/GUI/Tab.cpp:1772 src/slic3r/GUI/Tab.cpp:2016 +#: src/slic3r/GUI/Tab.cpp:3125 msgid "Set" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2039 +#: src/slic3r/GUI/Tab.cpp:1804 msgid "Capabilities" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2044 +#: src/slic3r/GUI/Tab.cpp:1809 msgid "Number of extruders of the printer." msgstr "" -#: src/slic3r/GUI/Tab.cpp:2069 +#: src/slic3r/GUI/Tab.cpp:1837 msgid "" "Single Extruder Multi Material is selected, \n" "and all extruders must have the same diameter.\n" @@ -4326,244 +4726,248 @@ msgid "" "nozzle diameter value?" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2072 src/slic3r/GUI/Tab.cpp:2540 -#: src/libslic3r/PrintConfig.cpp:1310 +#: src/slic3r/GUI/Tab.cpp:1840 src/slic3r/GUI/Tab.cpp:2308 +#: src/libslic3r/PrintConfig.cpp:1323 msgid "Nozzle diameter" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2102 +#: src/slic3r/GUI/Tab.cpp:1870 msgid "USB/Serial connection" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2103 src/libslic3r/PrintConfig.cpp:1640 +#: src/slic3r/GUI/Tab.cpp:1871 src/libslic3r/PrintConfig.cpp:1656 msgid "Serial port" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2108 +#: src/slic3r/GUI/Tab.cpp:1876 msgid "Rescan serial ports" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2130 +#: src/slic3r/GUI/Tab.cpp:1898 msgid "Connection to printer works correctly." msgstr "" -#: src/slic3r/GUI/Tab.cpp:2133 +#: src/slic3r/GUI/Tab.cpp:1901 msgid "Connection failed." msgstr "" -#: src/slic3r/GUI/Tab.cpp:2146 src/slic3r/GUI/Tab.cpp:2323 +#: src/slic3r/GUI/Tab.cpp:1914 src/slic3r/GUI/Tab.cpp:2091 msgid "Print Host upload" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2190 src/libslic3r/PrintConfig.cpp:138 +#: src/slic3r/GUI/Tab.cpp:1958 src/libslic3r/PrintConfig.cpp:143 msgid "Before layer change G-code" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2196 src/libslic3r/PrintConfig.cpp:1056 +#: src/slic3r/GUI/Tab.cpp:1964 src/libslic3r/PrintConfig.cpp:1069 msgid "After layer change G-code" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2202 src/libslic3r/PrintConfig.cpp:2056 +#: src/slic3r/GUI/Tab.cpp:1970 src/libslic3r/PrintConfig.cpp:2082 msgid "Tool change G-code" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2208 +#: src/slic3r/GUI/Tab.cpp:1976 msgid "Between objects G-code (for sequential printing)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2280 +#: src/slic3r/GUI/Tab.cpp:2048 msgid "Display" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2295 +#: src/slic3r/GUI/Tab.cpp:2063 msgid "Tilt" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2296 +#: src/slic3r/GUI/Tab.cpp:2064 msgid "Tilt time" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2302 src/slic3r/GUI/Tab.cpp:3650 +#: src/slic3r/GUI/Tab.cpp:2070 src/slic3r/GUI/Tab.cpp:3470 msgid "Corrections" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2317 src/slic3r/GUI/Tab.cpp:3646 +#: src/slic3r/GUI/Tab.cpp:2085 src/slic3r/GUI/Tab.cpp:3466 msgid "Exposure" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2388 src/slic3r/GUI/Tab.cpp:2473 -#: 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: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 "" -#: src/slic3r/GUI/Tab.cpp:2402 +#: src/slic3r/GUI/Tab.cpp:2170 msgid "Values in this column are for Normal mode" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2403 +#: src/slic3r/GUI/Tab.cpp:2171 msgid "Normal" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2408 +#: src/slic3r/GUI/Tab.cpp:2176 msgid "Values in this column are for Stealth mode" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2409 +#: src/slic3r/GUI/Tab.cpp:2177 msgid "Stealth" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2417 +#: src/slic3r/GUI/Tab.cpp:2185 msgid "Maximum feedrates" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2422 +#: src/slic3r/GUI/Tab.cpp:2190 msgid "Maximum accelerations" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2429 +#: src/slic3r/GUI/Tab.cpp:2197 msgid "Jerk limits" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2434 +#: src/slic3r/GUI/Tab.cpp:2202 msgid "Minimum feedrates" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2498 src/slic3r/GUI/Tab.cpp:2506 +#: src/slic3r/GUI/Tab.cpp:2266 src/slic3r/GUI/Tab.cpp:2274 msgid "Single extruder MM setup" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2507 +#: src/slic3r/GUI/Tab.cpp:2275 msgid "Single extruder multimaterial parameters" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2520 src/libslic3r/GCode/PreviewData.cpp:461 -#, possible-c-format -msgid "Extruder %d" -msgstr "" - -#: src/slic3r/GUI/Tab.cpp:2538 +#: 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 "" -#: src/slic3r/GUI/Tab.cpp:2562 +#: src/slic3r/GUI/Tab.cpp:2330 msgid "Layer height limits" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2567 +#: src/slic3r/GUI/Tab.cpp:2335 msgid "Position (for multi-extruder printers)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2573 +#: src/slic3r/GUI/Tab.cpp:2341 msgid "Only lift Z" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2586 +#: src/slic3r/GUI/Tab.cpp:2354 msgid "" "Retraction when tool is disabled (advanced settings for multi-extruder " "setups)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2594 +#: src/slic3r/GUI/Tab.cpp:2362 msgid "Reset to Filament Color" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2775 +#: 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 "" -#: src/slic3r/GUI/Tab.cpp:2777 +#: src/slic3r/GUI/Tab.cpp:2545 msgid "Firmware Retraction" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3106 +#: src/slic3r/GUI/Tab.cpp:2874 #, possible-c-format msgid "Default preset (%s)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3107 +#: src/slic3r/GUI/Tab.cpp:2875 #, possible-c-format msgid "Preset (%s)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3124 +#: src/slic3r/GUI/Tab.cpp:2892 msgid "has the following unsaved changes:" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3127 +#: src/slic3r/GUI/Tab.cpp:2895 msgid "is not compatible with printer" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3128 +#: src/slic3r/GUI/Tab.cpp:2896 msgid "is not compatible with print profile" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3130 +#: src/slic3r/GUI/Tab.cpp:2898 msgid "and it has the following unsaved changes:" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3134 +#: src/slic3r/GUI/Tab.cpp:2902 msgid "Unsaved Changes" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3225 +#: src/slic3r/GUI/Tab.cpp:2994 +msgctxt "PresetName" msgid "%1% - Copy" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3248 +#: src/slic3r/GUI/Tab.cpp:3017 msgid "The supplied name is empty. It can't be saved." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3253 +#: src/slic3r/GUI/Tab.cpp:3022 msgid "Cannot overwrite a system profile." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3257 +#: src/slic3r/GUI/Tab.cpp:3026 msgid "Cannot overwrite an external profile." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3283 +#: src/slic3r/GUI/Tab.cpp:3031 +msgid "Preset with name \"%1%\" already exist." +msgstr "" + +#: src/slic3r/GUI/Tab.cpp:3032 +msgid "Replace?" +msgstr "" + +#: src/slic3r/GUI/Tab.cpp:3070 msgid "remove" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3283 +#: src/slic3r/GUI/Tab.cpp:3070 msgid "delete" msgstr "" #. TRN remove/delete -#: src/slic3r/GUI/Tab.cpp:3285 +#: src/slic3r/GUI/Tab.cpp:3072 msgid "Are you sure you want to %1% the selected preset?" msgstr "" #. TRN Remove/Delete -#: src/slic3r/GUI/Tab.cpp:3288 +#: src/slic3r/GUI/Tab.cpp:3075 msgid "%1% Preset" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3414 +#: src/slic3r/GUI/Tab.cpp:3201 msgid "LOCKED LOCK" msgstr "" #. TRN Description for "LOCKED LOCK" -#: src/slic3r/GUI/Tab.cpp:3416 +#: src/slic3r/GUI/Tab.cpp:3203 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:3418 +#: src/slic3r/GUI/Tab.cpp:3205 msgid "UNLOCKED LOCK" msgstr "" #. TRN Description for "UNLOCKED LOCK" -#: src/slic3r/GUI/Tab.cpp:3420 +#: src/slic3r/GUI/Tab.cpp:3207 msgid "" "indicates that some settings were changed and are not equal to the system " "(or default) values for the current option group.\n" @@ -4571,23 +4975,23 @@ msgid "" "to the system (or default) values." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3425 +#: src/slic3r/GUI/Tab.cpp:3212 msgid "WHITE BULLET" msgstr "" #. TRN Description for "WHITE BULLET" -#: src/slic3r/GUI/Tab.cpp:3427 +#: src/slic3r/GUI/Tab.cpp:3214 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:3430 +#: src/slic3r/GUI/Tab.cpp:3217 msgid "BACK ARROW" msgstr "" #. TRN Description for "BACK ARROW" -#: src/slic3r/GUI/Tab.cpp:3432 +#: src/slic3r/GUI/Tab.cpp:3219 msgid "" "indicates that the settings were changed and are not equal to the last saved " "preset for the current option group.\n" @@ -4595,13 +4999,13 @@ msgid "" "to the last saved preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3442 +#: src/slic3r/GUI/Tab.cpp:3229 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:3444 +#: src/slic3r/GUI/Tab.cpp:3231 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" @@ -4609,17 +5013,17 @@ msgid "" "default) values." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3447 +#: src/slic3r/GUI/Tab.cpp:3234 msgid "WHITE BULLET icon indicates a non system (or non default) preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3450 +#: src/slic3r/GUI/Tab.cpp:3237 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:3452 +#: src/slic3r/GUI/Tab.cpp:3239 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" @@ -4627,26 +5031,26 @@ msgid "" "preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3458 +#: src/slic3r/GUI/Tab.cpp:3245 msgid "" "LOCKED LOCK icon indicates that the value is the same as the system (or " "default) value." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3459 +#: src/slic3r/GUI/Tab.cpp:3246 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:3465 +#: src/slic3r/GUI/Tab.cpp:3252 msgid "" "WHITE BULLET icon indicates that the value is the same as in the last saved " "preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3466 +#: src/slic3r/GUI/Tab.cpp:3253 msgid "" "BACK ARROW icon indicates that the value was changed and is not equal to the " "last saved preset.\n" @@ -4654,61 +5058,61 @@ msgid "" msgstr "" #. TRN Preset -#: src/slic3r/GUI/Tab.cpp:3579 +#: src/slic3r/GUI/Tab.cpp:3366 #, possible-c-format msgid "Save %s as:" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3623 +#: src/slic3r/GUI/Tab.cpp:3410 msgid "the following suffix is not allowed:" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3627 +#: src/slic3r/GUI/Tab.cpp:3414 msgid "The supplied name is not available." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3640 +#: src/slic3r/GUI/Tab.cpp:3427 src/slic3r/GUI/Tab.cpp:3429 msgid "Material" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3642 src/slic3r/GUI/Tab.cpp:3733 -#: src/slic3r/GUI/wxExtensions.cpp:482 +#: src/slic3r/GUI/Tab.cpp:3463 src/slic3r/GUI/Tab.cpp:3553 +#: src/slic3r/GUI/wxExtensions.cpp:601 msgid "Layers" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3741 +#: src/slic3r/GUI/Tab.cpp:3561 msgid "Support head" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3746 +#: src/slic3r/GUI/Tab.cpp:3566 msgid "Support pillar" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3760 +#: src/slic3r/GUI/Tab.cpp:3580 msgid "Connection of the support sticks and junctions" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3765 +#: src/slic3r/GUI/Tab.cpp:3585 msgid "Automatic generation" msgstr "" -#: src/slic3r/GUI/Tab.hpp:328 src/slic3r/GUI/Tab.hpp:428 +#: src/slic3r/GUI/Tab.hpp:327 src/slic3r/GUI/Tab.hpp:427 msgid "Print Settings" msgstr "" -#: src/slic3r/GUI/Tab.hpp:353 +#: src/slic3r/GUI/Tab.hpp:352 msgid "Filament Settings" msgstr "" -#: src/slic3r/GUI/Tab.hpp:389 +#: src/slic3r/GUI/Tab.hpp:388 msgid "Printer Settings" msgstr "" -#: src/slic3r/GUI/Tab.hpp:413 +#: src/slic3r/GUI/Tab.hpp:412 msgid "Material Settings" msgstr "" -#: src/slic3r/GUI/Tab.hpp:440 +#: src/slic3r/GUI/Tab.hpp:439 msgid "Save preset" msgstr "" @@ -4721,39 +5125,39 @@ msgstr "" msgid "New version of %s is available" msgstr "" -#: 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:127 +#: src/slic3r/GUI/UpdateDialogs.cpp:60 src/slic3r/GUI/UpdateDialogs.cpp:125 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:207 +#: src/slic3r/GUI/UpdateDialogs.cpp:89 src/slic3r/GUI/UpdateDialogs.cpp:205 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" @@ -4763,21 +5167,21 @@ msgid "" "Updated configuration bundles:" msgstr "" -#: src/slic3r/GUI/UpdateDialogs.cpp:115 +#: src/slic3r/GUI/UpdateDialogs.cpp:113 msgid "Comment:" msgstr "" -#: src/slic3r/GUI/UpdateDialogs.cpp:151 +#: src/slic3r/GUI/UpdateDialogs.cpp:149 #, possible-c-format msgid "%s incompatibility" msgstr "" -#: src/slic3r/GUI/UpdateDialogs.cpp:152 +#: src/slic3r/GUI/UpdateDialogs.cpp:150 #, possible-c-format msgid "%s configuration is incompatible" msgstr "" -#: src/slic3r/GUI/UpdateDialogs.cpp:157 +#: src/slic3r/GUI/UpdateDialogs.cpp:155 #, possible-c-format msgid "" "This version of %s is not compatible with currently installed configuration " @@ -4787,28 +5191,28 @@ 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 "" -#: src/slic3r/GUI/UpdateDialogs.cpp:166 +#: src/slic3r/GUI/UpdateDialogs.cpp:164 #, possible-c-format msgid "This %s version: %s" msgstr "" -#: src/slic3r/GUI/UpdateDialogs.cpp:171 +#: src/slic3r/GUI/UpdateDialogs.cpp:169 msgid "Incompatible bundles:" msgstr "" -#: src/slic3r/GUI/UpdateDialogs.cpp:187 +#: src/slic3r/GUI/UpdateDialogs.cpp:185 #, possible-c-format msgid "Exit %s" msgstr "" -#: src/slic3r/GUI/UpdateDialogs.cpp:190 +#: src/slic3r/GUI/UpdateDialogs.cpp:188 msgid "Re-configure" msgstr "" -#: src/slic3r/GUI/UpdateDialogs.cpp:211 +#: src/slic3r/GUI/UpdateDialogs.cpp:209 #, possible-c-format msgid "" "%s now uses an updated configuration structure.\n" @@ -4824,15 +5228,15 @@ msgid "" "choose whether to enable automatic preset updates." msgstr "" -#: src/slic3r/GUI/UpdateDialogs.cpp:227 +#: src/slic3r/GUI/UpdateDialogs.cpp:225 msgid "For more information please visit our wiki page:" msgstr "" -#: src/slic3r/GUI/WipeTowerDialog.cpp:14 +#: src/slic3r/GUI/WipeTowerDialog.cpp:15 msgid "Ramming customization" msgstr "" -#: 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 " @@ -4845,63 +5249,63 @@ msgid "" "jams, extruder wheel grinding into filament etc." 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 "" -#: 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 "" -#: src/slic3r/GUI/WipeTowerDialog.cpp:235 +#: src/slic3r/GUI/WipeTowerDialog.cpp:264 msgid "loaded" msgstr "" -#: src/slic3r/GUI/WipeTowerDialog.cpp:240 +#: src/slic3r/GUI/WipeTowerDialog.cpp:276 msgid "Tool #" msgstr "" -#: 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." msgstr "" -#: src/slic3r/GUI/WipeTowerDialog.cpp:248 +#: src/slic3r/GUI/WipeTowerDialog.cpp:286 msgid "Volume to purge (mm³) when the filament is being" msgstr "" -#: src/slic3r/GUI/WipeTowerDialog.cpp:262 +#: src/slic3r/GUI/WipeTowerDialog.cpp:300 msgid "From" msgstr "" -#: 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" @@ -4909,49 +5313,173 @@ msgid "" "Do you want to proceed?" msgstr "" -#: 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:471 +#: src/slic3r/GUI/wxExtensions.cpp:590 msgid "Instances" msgstr "" -#: src/slic3r/GUI/wxExtensions.cpp:475 src/slic3r/GUI/wxExtensions.cpp:619 +#: src/slic3r/GUI/wxExtensions.cpp:594 src/slic3r/GUI/wxExtensions.cpp:750 #, possible-c-format msgid "Instance %d" msgstr "" -#: src/slic3r/GUI/wxExtensions.cpp:509 +#: src/slic3r/GUI/wxExtensions.cpp:628 msgid "Range" msgstr "" -#: src/slic3r/GUI/wxExtensions.cpp:2731 +#: 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:2732 -msgid "Add/Del color change" +#: src/slic3r/GUI/wxExtensions.cpp:3078 +msgid "Discard all custom changes" msgstr "" -#: src/slic3r/GUI/wxExtensions.cpp:2733 -msgid "Discard all color changes" +#: src/slic3r/GUI/wxExtensions.cpp:3080 +msgid "Set extruder sequence for whole print" msgstr "" -#: src/slic3r/GUI/wxExtensions.cpp:2993 +#: 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 #, possible-c-format msgid "Switch to the %s mode" msgstr "" -#: src/slic3r/GUI/wxExtensions.cpp:2994 +#: src/slic3r/GUI/wxExtensions.cpp:3775 #, 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 "" @@ -4961,6 +5489,7 @@ 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 "" @@ -4997,17 +5526,35 @@ msgstr "" msgid "Could not connect to Prusa SLA" msgstr "" -#: src/slic3r/Utils/PresetUpdater.cpp:614 +#: src/slic3r/Utils/FlashAir.cpp:60 +msgid "Upload not enabled on FlashAir card." +msgstr "" + +#: src/slic3r/Utils/FlashAir.cpp:70 +msgid "Connection to FlashAir works correctly and upload is enabled." +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:624 #, possible-c-format msgid "requires min. %s and max. %s" msgstr "" -#: src/slic3r/Utils/PresetUpdater.cpp:619 +#: src/slic3r/Utils/PresetUpdater.cpp:629 #, possible-c-format msgid "requires min. %s" msgstr "" -#: src/slic3r/Utils/PresetUpdater.cpp:621 +#: src/slic3r/Utils/PresetUpdater.cpp:631 #, possible-c-format msgid "requires max. %s" msgstr "" @@ -5090,7 +5637,11 @@ msgid "Model Repair by the Netfabb service" msgstr "" #: src/slic3r/Utils/FixModelByWin10.cpp:426 -msgid "Model repair failed: \n" +msgid "Model repair failed:" +msgstr "" + +#: src/libslic3r/SLA/SLAPad.cpp:690 +msgid "Pad brim size is too small for the current configuration." msgstr "" #: src/libslic3r/Zipper.cpp:32 @@ -5221,99 +5772,134 @@ msgstr "" msgid "Error with zip archive" msgstr "" -#: src/libslic3r/Print.cpp:1112 +#: src/libslic3r/GCode.cpp:628 +msgid "Empty layers detected, the output would not be printable." +msgstr "" + +#: src/libslic3r/GCode.cpp:629 +msgid "Object name: " +msgstr "" + +#: src/libslic3r/GCode.cpp:629 +msgid "Print z: " +msgstr "" + +#: src/libslic3r/GCode.cpp:630 +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/Format/3mf.cpp:1517 +msgid "The selected 3mf file has been saved with a newer version of " +msgstr "" + +#: src/libslic3r/Format/AMF.cpp:915 +msgid "The selected amf file has been saved with a newer version of " +msgstr "" + +#: src/libslic3r/Print.cpp:1168 msgid "All objects are outside of the print volume." msgstr "" -#: src/libslic3r/Print.cpp:1139 +#: src/libslic3r/Print.cpp:1171 +msgid "The supplied settings will cause an empty print." +msgstr "" + +#: src/libslic3r/Print.cpp:1198 msgid "Some objects are too close; your extruder will collide with them." msgstr "" -#: src/libslic3r/Print.cpp:1154 +#: src/libslic3r/Print.cpp:1213 msgid "" "Some objects are too tall and cannot be printed without extruder collisions." msgstr "" -#: src/libslic3r/Print.cpp:1164 +#: src/libslic3r/Print.cpp:1223 msgid "The Spiral Vase option can only be used when printing a single object." msgstr "" -#: src/libslic3r/Print.cpp:1171 +#: src/libslic3r/Print.cpp:1230 msgid "" "The Spiral Vase option can only be used when printing single material " "objects." msgstr "" -#: src/libslic3r/Print.cpp:1184 +#: 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 "" -#: src/libslic3r/Print.cpp:1189 +#: src/libslic3r/Print.cpp:1248 msgid "" "The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter " "and Repetier G-code flavors." msgstr "" -#: src/libslic3r/Print.cpp:1191 +#: src/libslic3r/Print.cpp:1250 msgid "" "The Wipe Tower is currently only supported with the relative extruder " "addressing (use_relative_e_distances=1)." msgstr "" -#: src/libslic3r/Print.cpp:1193 +#: src/libslic3r/Print.cpp:1252 msgid "Ooze prevention is currently not supported with the wipe tower enabled." msgstr "" -#: src/libslic3r/Print.cpp:1214 +#: src/libslic3r/Print.cpp:1254 +msgid "" +"The Wipe Tower currently does not support volumetric E (use_volumetric_e=0)." +msgstr "" + +#: src/libslic3r/Print.cpp:1275 msgid "" "The Wipe Tower is only supported for multiple objects if they have equal " "layer heights" msgstr "" -#: src/libslic3r/Print.cpp:1216 +#: 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 "" -#: src/libslic3r/Print.cpp:1218 +#: 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 "" -#: src/libslic3r/Print.cpp:1220 +#: src/libslic3r/Print.cpp:1281 msgid "" "The Wipe Tower is only supported for multiple objects if they are sliced " "equally." msgstr "" -#: src/libslic3r/Print.cpp:1248 +#: src/libslic3r/Print.cpp:1323 msgid "" "The Wipe tower is only supported if all objects have the same layer height " "profile" msgstr "" -#: src/libslic3r/Print.cpp:1258 -msgid "The supplied settings will cause an empty print." -msgstr "" - -#: src/libslic3r/Print.cpp:1275 +#: src/libslic3r/Print.cpp:1349 msgid "" "One or more object were assigned an extruder that the printer does not have." msgstr "" -#: src/libslic3r/Print.cpp:1284 +#: src/libslic3r/Print.cpp:1358 msgid "%1%=%2% mm is too low to be printable at a layer height %3% mm" msgstr "" -#: src/libslic3r/Print.cpp:1287 +#: src/libslic3r/Print.cpp:1361 msgid "Excessive %1%=%2% mm to be printable with a nozzle diameter %3% mm" msgstr "" -#: src/libslic3r/Print.cpp:1298 +#: 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 " @@ -5321,13 +5907,13 @@ msgid "" "same diameter." msgstr "" -#: src/libslic3r/Print.cpp:1306 +#: 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 "" -#: src/libslic3r/Print.cpp:1310 +#: 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 " @@ -5335,100 +5921,104 @@ msgid "" "set to 0)." msgstr "" -#: src/libslic3r/Print.cpp:1332 +#: src/libslic3r/Print.cpp:1406 msgid "First layer height can't be greater than nozzle diameter" msgstr "" -#: src/libslic3r/Print.cpp:1337 +#: src/libslic3r/Print.cpp:1411 msgid "Layer height can't be greater than nozzle diameter" msgstr "" -#: src/libslic3r/Print.cpp:1492 +#: src/libslic3r/Print.cpp:1566 msgid "Infilling layers" msgstr "" -#: src/libslic3r/Print.cpp:1500 +#: src/libslic3r/Print.cpp:1582 msgid "Generating skirt" msgstr "" -#: src/libslic3r/Print.cpp:1508 +#: src/libslic3r/Print.cpp:1590 msgid "Generating brim" msgstr "" -#: src/libslic3r/Print.cpp:1536 +#: src/libslic3r/Print.cpp:1614 msgid "Exporting G-code" msgstr "" -#: src/libslic3r/Print.cpp:1540 +#: src/libslic3r/Print.cpp:1618 msgid "Generating G-code" msgstr "" -#: src/libslic3r/SLAPrint.cpp:64 +#: src/libslic3r/SLAPrint.cpp:66 msgid "Slicing model" msgstr "" -#: src/libslic3r/SLAPrint.cpp:65 src/libslic3r/SLAPrint.cpp:899 +#: src/libslic3r/SLAPrint.cpp:67 src/libslic3r/SLAPrint.cpp:905 msgid "Generating support points" msgstr "" -#: src/libslic3r/SLAPrint.cpp:66 +#: src/libslic3r/SLAPrint.cpp:68 msgid "Generating support tree" msgstr "" -#: src/libslic3r/SLAPrint.cpp:67 +#: src/libslic3r/SLAPrint.cpp:69 msgid "Generating pad" msgstr "" -#: src/libslic3r/SLAPrint.cpp:68 +#: src/libslic3r/SLAPrint.cpp:70 msgid "Slicing supports" msgstr "" -#: src/libslic3r/SLAPrint.cpp:85 +#: src/libslic3r/SLAPrint.cpp:87 msgid "Merging slices and calculating statistics" msgstr "" -#: src/libslic3r/SLAPrint.cpp:86 +#: src/libslic3r/SLAPrint.cpp:88 msgid "Rasterizing layers" msgstr "" -#: src/libslic3r/SLAPrint.cpp:661 +#: src/libslic3r/SLAPrint.cpp:670 msgid "" "Cannot proceed without support points! Add support points or disable support " "generation." msgstr "" -#: src/libslic3r/SLAPrint.cpp:678 +#: 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 "" -#: src/libslic3r/SLAPrint.cpp:684 +#: 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 "" -#: src/libslic3r/SLAPrint.cpp:696 +#: src/libslic3r/SLAPrint.cpp:703 msgid "Exposition time is out of printer profile bounds." msgstr "" -#: src/libslic3r/SLAPrint.cpp:703 +#: src/libslic3r/SLAPrint.cpp:710 msgid "Initial exposition time is out of printer profile bounds." msgstr "" -#: src/libslic3r/SLAPrint.cpp:787 +#: src/libslic3r/SLAPrint.cpp:794 msgid "" "Slicing had to be stopped due to an internal error: Inconsistent slice index." msgstr "" -#: src/libslic3r/SLAPrint.cpp:982 src/libslic3r/SLAPrint.cpp:992 -#: src/libslic3r/SLAPrint.cpp:1033 +#: src/libslic3r/SLAPrint.cpp:964 src/libslic3r/SLAPrint.cpp:973 +#: src/libslic3r/SLAPrint.cpp:1018 msgid "Visualizing supports" msgstr "" -#: src/libslic3r/SLAPrint.cpp:1566 +#: src/libslic3r/SLAPrint.cpp:1009 +msgid "No pad can be generated for this model with the current configuration" +msgstr "" + +#: src/libslic3r/SLAPrint.cpp:1554 msgid "Slicing done" msgstr "" @@ -5452,101 +6042,105 @@ msgstr "" msgid "Bed custom model" msgstr "" -#: src/libslic3r/PrintConfig.cpp:68 +#: src/libslic3r/PrintConfig.cpp:66 +msgid "Picture sizes to be stored into a .gcode and .sl1 files" +msgstr "" + +#: 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 "" -#: 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 " "resolution, therefore it is advisable to keep the value reasonably low." msgstr "" -#: src/libslic3r/PrintConfig.cpp:92 +#: src/libslic3r/PrintConfig.cpp:97 msgid "Hostname, IP or URL" msgstr "" -#: 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." msgstr "" -#: src/libslic3r/PrintConfig.cpp:99 +#: src/libslic3r/PrintConfig.cpp:104 msgid "API Key / Password" msgstr "" -#: 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." msgstr "" -#: src/libslic3r/PrintConfig.cpp:106 +#: src/libslic3r/PrintConfig.cpp:111 msgid "HTTPS CA File" msgstr "" -#: 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 " "is used." msgstr "" -#: src/libslic3r/PrintConfig.cpp:121 +#: src/libslic3r/PrintConfig.cpp:126 msgid "Avoid crossing perimeters" 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 "" -#: src/libslic3r/PrintConfig.cpp:129 src/libslic3r/PrintConfig.cpp:2027 +#: src/libslic3r/PrintConfig.cpp:134 src/libslic3r/PrintConfig.cpp:2053 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." msgstr "" -#: 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 " "as [layer_num] and [layer_z]." msgstr "" -#: src/libslic3r/PrintConfig.cpp:149 +#: src/libslic3r/PrintConfig.cpp:154 msgid "Between objects G-code" msgstr "" -#: 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; " @@ -5556,70 +6150,70 @@ msgid "" "S[first_layer_temperature]\" command wherever you want." msgstr "" -#: 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:172 msgid "Bridge" msgstr "" -#: src/libslic3r/PrintConfig.cpp:168 +#: 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 "" -#: 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: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 "" -#: src/libslic3r/PrintConfig.cpp:176 +#: src/libslic3r/PrintConfig.cpp:181 msgid "Bridging angle" msgstr "" -#: src/libslic3r/PrintConfig.cpp:178 +#: 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 "" -#: 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:2614 -#: src/libslic3r/PrintConfig.cpp:2724 +#: 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:187 +#: src/libslic3r/PrintConfig.cpp:192 msgid "Bridges fan speed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:188 +#: src/libslic3r/PrintConfig.cpp:193 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:2654 +#: 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:196 +#: src/libslic3r/PrintConfig.cpp:201 msgid "Bridge flow ratio" msgstr "" -#: src/libslic3r/PrintConfig.cpp:198 +#: 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 " @@ -5627,83 +6221,83 @@ msgid "" "before tweaking this." msgstr "" -#: src/libslic3r/PrintConfig.cpp:208 +#: src/libslic3r/PrintConfig.cpp:213 msgid "Bridges" msgstr "" -#: src/libslic3r/PrintConfig.cpp:210 +#: src/libslic3r/PrintConfig.cpp:215 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: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 "" -#: src/libslic3r/PrintConfig.cpp:218 +#: src/libslic3r/PrintConfig.cpp:223 msgid "Brim width" msgstr "" -#: src/libslic3r/PrintConfig.cpp:219 +#: src/libslic3r/PrintConfig.cpp:224 msgid "" "Horizontal width of the brim that will be printed around each object on the " "first layer." msgstr "" -#: src/libslic3r/PrintConfig.cpp:226 +#: src/libslic3r/PrintConfig.cpp:231 msgid "Clip multi-part objects" msgstr "" -#: src/libslic3r/PrintConfig.cpp:227 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:234 +#: src/libslic3r/PrintConfig.cpp:239 msgid "Colorprint height" msgstr "" -#: src/libslic3r/PrintConfig.cpp:235 +#: src/libslic3r/PrintConfig.cpp:240 msgid "Heights at which a filament change is to occur." msgstr "" -#: src/libslic3r/PrintConfig.cpp:245 +#: src/libslic3r/PrintConfig.cpp:250 msgid "Compatible printers condition" msgstr "" -#: src/libslic3r/PrintConfig.cpp:246 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:260 +#: src/libslic3r/PrintConfig.cpp:265 msgid "Compatible print profiles condition" msgstr "" -#: src/libslic3r/PrintConfig.cpp:261 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:278 +#: src/libslic3r/PrintConfig.cpp:283 msgid "Complete individual objects" msgstr "" -#: src/libslic3r/PrintConfig.cpp:279 +#: 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). " @@ -5711,114 +6305,107 @@ msgid "" "warn and prevent you from extruder collisions, but beware." msgstr "" -#: src/libslic3r/PrintConfig.cpp:287 +#: src/libslic3r/PrintConfig.cpp:292 msgid "Enable auto cooling" msgstr "" -#: src/libslic3r/PrintConfig.cpp:288 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:293 +#: src/libslic3r/PrintConfig.cpp:298 msgid "Cooling tube position" msgstr "" -#: src/libslic3r/PrintConfig.cpp:294 +#: src/libslic3r/PrintConfig.cpp:299 msgid "Distance of the center-point of the cooling tube from the extruder tip." msgstr "" -#: src/libslic3r/PrintConfig.cpp:301 +#: src/libslic3r/PrintConfig.cpp:306 msgid "Cooling tube length" msgstr "" -#: src/libslic3r/PrintConfig.cpp:302 +#: src/libslic3r/PrintConfig.cpp:307 msgid "Length of the cooling tube to limit space for cooling moves inside it." msgstr "" -#: src/libslic3r/PrintConfig.cpp:310 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:319 +#: src/libslic3r/PrintConfig.cpp:324 msgid "Default filament profile" msgstr "" -#: src/libslic3r/PrintConfig.cpp:320 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:326 +#: src/libslic3r/PrintConfig.cpp:331 msgid "Default print profile" msgstr "" -#: src/libslic3r/PrintConfig.cpp:327 src/libslic3r/PrintConfig.cpp:2479 -#: src/libslic3r/PrintConfig.cpp:2490 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:333 +#: src/libslic3r/PrintConfig.cpp:338 msgid "Disable fan for the first" msgstr "" -#: src/libslic3r/PrintConfig.cpp:334 +#: 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 "" -#: 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:348 msgid "Don't support bridges" msgstr "" -#: src/libslic3r/PrintConfig.cpp:345 +#: src/libslic3r/PrintConfig.cpp:350 msgid "" "Experimental option for preventing support material from being generated " "under bridged areas." msgstr "" -#: src/libslic3r/PrintConfig.cpp:351 +#: src/libslic3r/PrintConfig.cpp:356 msgid "Distance between copies" msgstr "" -#: src/libslic3r/PrintConfig.cpp:352 +#: src/libslic3r/PrintConfig.cpp:357 msgid "Distance used for the auto-arrange feature of the plater." msgstr "" -#: src/libslic3r/PrintConfig.cpp:359 +#: src/libslic3r/PrintConfig.cpp:364 msgid "Elephant foot compensation" msgstr "" -#: src/libslic3r/PrintConfig.cpp:361 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:370 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:380 +#: 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 " @@ -5827,62 +6414,62 @@ msgid "" "in extruder order." msgstr "" -#: src/libslic3r/PrintConfig.cpp:391 +#: src/libslic3r/PrintConfig.cpp:396 msgid "Ensure vertical shell thickness" msgstr "" -#: src/libslic3r/PrintConfig.cpp:393 +#: src/libslic3r/PrintConfig.cpp:398 msgid "" "Add solid infill near sloping surfaces to guarantee the vertical shell " "thickness (top+bottom solid layers)." msgstr "" -#: src/libslic3r/PrintConfig.cpp:399 +#: src/libslic3r/PrintConfig.cpp:404 msgid "Top fill pattern" msgstr "" -#: src/libslic3r/PrintConfig.cpp:401 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:409 src/libslic3r/PrintConfig.cpp:821 -#: src/libslic3r/PrintConfig.cpp:1972 +#: src/libslic3r/PrintConfig.cpp:414 src/libslic3r/PrintConfig.cpp:832 +#: src/libslic3r/PrintConfig.cpp:1998 msgid "Rectilinear" msgstr "" -#: src/libslic3r/PrintConfig.cpp:410 src/libslic3r/PrintConfig.cpp:827 +#: src/libslic3r/PrintConfig.cpp:415 src/libslic3r/PrintConfig.cpp:838 msgid "Concentric" msgstr "" -#: src/libslic3r/PrintConfig.cpp:411 src/libslic3r/PrintConfig.cpp:831 +#: src/libslic3r/PrintConfig.cpp:416 src/libslic3r/PrintConfig.cpp:842 msgid "Hilbert Curve" msgstr "" -#: src/libslic3r/PrintConfig.cpp:412 src/libslic3r/PrintConfig.cpp:832 +#: src/libslic3r/PrintConfig.cpp:417 src/libslic3r/PrintConfig.cpp:843 msgid "Archimedean Chords" msgstr "" -#: src/libslic3r/PrintConfig.cpp:413 src/libslic3r/PrintConfig.cpp:833 +#: src/libslic3r/PrintConfig.cpp:418 src/libslic3r/PrintConfig.cpp:844 msgid "Octagram Spiral" msgstr "" -#: src/libslic3r/PrintConfig.cpp:419 +#: src/libslic3r/PrintConfig.cpp:424 msgid "Bottom fill pattern" msgstr "" -#: src/libslic3r/PrintConfig.cpp:421 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:430 src/libslic3r/PrintConfig.cpp:440 +#: src/libslic3r/PrintConfig.cpp:435 src/libslic3r/PrintConfig.cpp:446 msgid "External perimeters" msgstr "" -#: src/libslic3r/PrintConfig.cpp:432 +#: 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, " @@ -5890,58 +6477,58 @@ msgid "" "(for example 200%), it will be computed over layer height." msgstr "" -#: 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: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 "" -#: src/libslic3r/PrintConfig.cpp:442 +#: 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 "" -#: 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: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 "" -#: src/libslic3r/PrintConfig.cpp:452 +#: src/libslic3r/PrintConfig.cpp:458 msgid "External perimeters first" msgstr "" -#: src/libslic3r/PrintConfig.cpp:454 +#: src/libslic3r/PrintConfig.cpp:460 msgid "" "Print contour perimeters from the outermost one to the innermost one instead " "of the default inverse order." msgstr "" -#: src/libslic3r/PrintConfig.cpp:460 +#: src/libslic3r/PrintConfig.cpp:466 msgid "Extra perimeters if needed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:462 -#, possible-c-format +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:472 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:484 +#: 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 " @@ -5949,30 +6536,26 @@ msgid "" "extruder can peek before colliding with other printed objects." msgstr "" -#: src/libslic3r/PrintConfig.cpp:494 -msgid "Radius" -msgstr "" - -#: src/libslic3r/PrintConfig.cpp:495 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:505 +#: src/libslic3r/PrintConfig.cpp:511 msgid "Extruder Color" msgstr "" -#: src/libslic3r/PrintConfig.cpp:506 src/libslic3r/PrintConfig.cpp:566 +#: src/libslic3r/PrintConfig.cpp:512 src/libslic3r/PrintConfig.cpp:573 msgid "This is only used in the Slic3r interface as a visual help." msgstr "" -#: src/libslic3r/PrintConfig.cpp:512 +#: src/libslic3r/PrintConfig.cpp:518 msgid "Extruder offset" msgstr "" -#: src/libslic3r/PrintConfig.cpp:513 +#: 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 " @@ -5980,21 +6563,21 @@ msgid "" "coordinates (they will be subtracted from the XY coordinate)." msgstr "" -#: src/libslic3r/PrintConfig.cpp:522 +#: src/libslic3r/PrintConfig.cpp:528 msgid "Extrusion axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:523 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:528 +#: src/libslic3r/PrintConfig.cpp:534 msgid "Extrusion multiplier" msgstr "" -#: src/libslic3r/PrintConfig.cpp:529 +#: 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. " @@ -6002,11 +6585,11 @@ msgid "" "more, check filament diameter and your firmware E steps." msgstr "" -#: src/libslic3r/PrintConfig.cpp:537 +#: src/libslic3r/PrintConfig.cpp:543 msgid "Default extrusion width" msgstr "" -#: src/libslic3r/PrintConfig.cpp:539 +#: 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 " @@ -6015,119 +6598,119 @@ msgid "" "height." msgstr "" -#: src/libslic3r/PrintConfig.cpp:548 +#: src/libslic3r/PrintConfig.cpp:555 msgid "Keep fan always on" msgstr "" -#: src/libslic3r/PrintConfig.cpp:549 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:554 +#: src/libslic3r/PrintConfig.cpp:561 msgid "Enable fan if layer print time is below" msgstr "" -#: src/libslic3r/PrintConfig.cpp:555 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:557 src/libslic3r/PrintConfig.cpp:1687 +#: src/libslic3r/PrintConfig.cpp:564 src/libslic3r/PrintConfig.cpp:1703 msgid "approximate seconds" msgstr "" -#: src/libslic3r/PrintConfig.cpp:571 +#: src/libslic3r/PrintConfig.cpp:578 msgid "Filament notes" msgstr "" -#: src/libslic3r/PrintConfig.cpp:572 +#: src/libslic3r/PrintConfig.cpp:579 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:587 src/libslic3r/PrintConfig.cpp:1244 msgid "Max volumetric speed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:581 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:590 +#: src/libslic3r/PrintConfig.cpp:597 msgid "Loading speed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:591 +#: src/libslic3r/PrintConfig.cpp:598 msgid "Speed used for loading the filament on the wipe tower." msgstr "" -#: src/libslic3r/PrintConfig.cpp:598 +#: src/libslic3r/PrintConfig.cpp:605 msgid "Loading speed at the start" msgstr "" -#: src/libslic3r/PrintConfig.cpp:599 +#: src/libslic3r/PrintConfig.cpp:606 msgid "Speed used at the very beginning of loading phase." msgstr "" -#: src/libslic3r/PrintConfig.cpp:606 +#: src/libslic3r/PrintConfig.cpp:613 msgid "Unloading speed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:607 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:615 +#: src/libslic3r/PrintConfig.cpp:622 msgid "Unloading speed at the start" msgstr "" -#: src/libslic3r/PrintConfig.cpp:616 +#: src/libslic3r/PrintConfig.cpp:623 msgid "" "Speed used for unloading the tip of the filament immediately after ramming." msgstr "" -#: src/libslic3r/PrintConfig.cpp:623 +#: src/libslic3r/PrintConfig.cpp:630 msgid "Delay after unloading" msgstr "" -#: src/libslic3r/PrintConfig.cpp:624 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:633 +#: src/libslic3r/PrintConfig.cpp:640 msgid "Number of cooling moves" msgstr "" -#: src/libslic3r/PrintConfig.cpp:634 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:642 +#: src/libslic3r/PrintConfig.cpp:649 msgid "Speed of the first cooling move" msgstr "" -#: src/libslic3r/PrintConfig.cpp:643 +#: src/libslic3r/PrintConfig.cpp:650 msgid "Cooling moves are gradually accelerating beginning at this speed." msgstr "" -#: src/libslic3r/PrintConfig.cpp:650 +#: src/libslic3r/PrintConfig.cpp:657 msgid "Minimal purge on wipe tower" msgstr "" -#: src/libslic3r/PrintConfig.cpp:651 +#: 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 " @@ -6136,62 +6719,63 @@ msgid "" "to produce successive infill or sacrificial object extrusions reliably." msgstr "" -#: src/libslic3r/PrintConfig.cpp:655 +#: src/libslic3r/PrintConfig.cpp:662 msgid "mm³" msgstr "" -#: src/libslic3r/PrintConfig.cpp:661 +#: src/libslic3r/PrintConfig.cpp:668 msgid "Speed of the last cooling move" msgstr "" -#: src/libslic3r/PrintConfig.cpp:662 +#: src/libslic3r/PrintConfig.cpp:669 msgid "Cooling moves are gradually accelerating towards this speed." msgstr "" -#: src/libslic3r/PrintConfig.cpp:669 +#: src/libslic3r/PrintConfig.cpp:676 msgid "Filament load time" msgstr "" -#: src/libslic3r/PrintConfig.cpp:670 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:677 +#: src/libslic3r/PrintConfig.cpp:684 msgid "Ramming parameters" msgstr "" -#: src/libslic3r/PrintConfig.cpp:678 +#: src/libslic3r/PrintConfig.cpp:685 msgid "" "This string is edited by RammingDialog and contains ramming specific " "parameters." msgstr "" -#: src/libslic3r/PrintConfig.cpp:684 +#: src/libslic3r/PrintConfig.cpp:691 msgid "Filament unload time" msgstr "" -#: src/libslic3r/PrintConfig.cpp:685 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:693 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:700 +#: src/libslic3r/PrintConfig.cpp:707 src/libslic3r/PrintConfig.cpp:2471 +#: src/libslic3r/PrintConfig.cpp:2472 msgid "Density" msgstr "" -#: src/libslic3r/PrintConfig.cpp:701 +#: 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 " @@ -6199,113 +6783,117 @@ msgid "" "displacement." msgstr "" -#: src/libslic3r/PrintConfig.cpp:704 +#: src/libslic3r/PrintConfig.cpp:711 msgid "g/cm³" msgstr "" -#: src/libslic3r/PrintConfig.cpp:709 +#: src/libslic3r/PrintConfig.cpp:716 msgid "Filament type" msgstr "" -#: src/libslic3r/PrintConfig.cpp:710 +#: src/libslic3r/PrintConfig.cpp:717 msgid "The filament material type for use in custom G-codes." msgstr "" -#: src/libslic3r/PrintConfig.cpp:736 +#: src/libslic3r/PrintConfig.cpp:743 msgid "Soluble material" msgstr "" -#: src/libslic3r/PrintConfig.cpp:737 +#: src/libslic3r/PrintConfig.cpp:744 msgid "Soluble material is most likely used for a soluble support." msgstr "" -#: src/libslic3r/PrintConfig.cpp:743 +#: src/libslic3r/PrintConfig.cpp:750 msgid "" "Enter your filament cost per kg here. This is only for statistical " "information." msgstr "" -#: src/libslic3r/PrintConfig.cpp:744 +#: src/libslic3r/PrintConfig.cpp:751 msgid "money/kg" msgstr "" -#: src/libslic3r/PrintConfig.cpp:753 +#: src/libslic3r/PrintConfig.cpp:760 src/libslic3r/PrintConfig.cpp:2555 +msgid "(Unknown)" +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:764 msgid "Fill angle" msgstr "" -#: src/libslic3r/PrintConfig.cpp:755 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:767 +#: src/libslic3r/PrintConfig.cpp:778 msgid "Fill density" msgstr "" -#: src/libslic3r/PrintConfig.cpp:769 +#: src/libslic3r/PrintConfig.cpp:780 msgid "Density of internal infill, expressed in the range 0% - 100%." msgstr "" -#: src/libslic3r/PrintConfig.cpp:804 +#: src/libslic3r/PrintConfig.cpp:815 msgid "Fill pattern" msgstr "" -#: src/libslic3r/PrintConfig.cpp:806 +#: src/libslic3r/PrintConfig.cpp:817 msgid "Fill pattern for general low-density infill." msgstr "" -#: src/libslic3r/PrintConfig.cpp:822 +#: src/libslic3r/PrintConfig.cpp:833 msgid "Grid" msgstr "" -#: src/libslic3r/PrintConfig.cpp:823 +#: src/libslic3r/PrintConfig.cpp:834 msgid "Triangles" msgstr "" -#: src/libslic3r/PrintConfig.cpp:824 +#: src/libslic3r/PrintConfig.cpp:835 msgid "Stars" msgstr "" -#: src/libslic3r/PrintConfig.cpp:825 +#: src/libslic3r/PrintConfig.cpp:836 msgid "Cubic" msgstr "" -#: src/libslic3r/PrintConfig.cpp:826 +#: src/libslic3r/PrintConfig.cpp:837 msgid "Line" msgstr "" -#: src/libslic3r/PrintConfig.cpp:828 src/libslic3r/PrintConfig.cpp:1974 +#: src/libslic3r/PrintConfig.cpp:839 src/libslic3r/PrintConfig.cpp:2000 msgid "Honeycomb" msgstr "" -#: src/libslic3r/PrintConfig.cpp:829 +#: src/libslic3r/PrintConfig.cpp:840 msgid "3D Honeycomb" msgstr "" -#: src/libslic3r/PrintConfig.cpp:830 +#: src/libslic3r/PrintConfig.cpp:841 msgid "Gyroid" msgstr "" -#: src/libslic3r/PrintConfig.cpp:837 src/libslic3r/PrintConfig.cpp:846 -#: src/libslic3r/PrintConfig.cpp:854 src/libslic3r/PrintConfig.cpp:887 +#: src/libslic3r/PrintConfig.cpp:848 src/libslic3r/PrintConfig.cpp:857 +#: src/libslic3r/PrintConfig.cpp:865 src/libslic3r/PrintConfig.cpp:899 msgid "First layer" msgstr "" -#: src/libslic3r/PrintConfig.cpp:838 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:847 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:856 +#: 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 " @@ -6313,11 +6901,7 @@ msgid "" "layer height. If set to zero, it will use the default extrusion width." msgstr "" -#: src/libslic3r/PrintConfig.cpp:866 -msgid "First layer height" -msgstr "" - -#: src/libslic3r/PrintConfig.cpp:868 +#: 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 " @@ -6325,47 +6909,47 @@ msgid "" "example: 150%) over the default layer height." msgstr "" -#: src/libslic3r/PrintConfig.cpp:877 +#: src/libslic3r/PrintConfig.cpp:889 msgid "First layer speed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:878 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:888 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:897 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:905 +#: src/libslic3r/PrintConfig.cpp:917 msgid "Verbose G-code" msgstr "" -#: src/libslic3r/PrintConfig.cpp:906 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:913 +#: src/libslic3r/PrintConfig.cpp:925 msgid "G-code flavor" msgstr "" -#: src/libslic3r/PrintConfig.cpp:914 +#: 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 " @@ -6373,15 +6957,15 @@ msgid "" "extrusion value at all." msgstr "" -#: src/libslic3r/PrintConfig.cpp:937 +#: src/libslic3r/PrintConfig.cpp:949 msgid "No extrusion" msgstr "" -#: src/libslic3r/PrintConfig.cpp:942 +#: src/libslic3r/PrintConfig.cpp:954 msgid "Label objects" msgstr "" -#: src/libslic3r/PrintConfig.cpp:943 +#: 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 " @@ -6389,46 +6973,46 @@ msgid "" "setup and Wipe into Object / Wipe into Infill." msgstr "" -#: src/libslic3r/PrintConfig.cpp:950 +#: src/libslic3r/PrintConfig.cpp:962 msgid "High extruder current on filament swap" msgstr "" -#: src/libslic3r/PrintConfig.cpp:951 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:959 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:967 +#: src/libslic3r/PrintConfig.cpp:979 msgid "Combine infill every" msgstr "" -#: src/libslic3r/PrintConfig.cpp:969 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:972 +#: src/libslic3r/PrintConfig.cpp:984 msgid "Combine infill every n layers" msgstr "" -#: src/libslic3r/PrintConfig.cpp:978 +#: src/libslic3r/PrintConfig.cpp:990 msgid "Infill extruder" msgstr "" -#: src/libslic3r/PrintConfig.cpp:980 +#: src/libslic3r/PrintConfig.cpp:992 msgid "The extruder to use when printing infill." msgstr "" -#: src/libslic3r/PrintConfig.cpp:988 +#: 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 " @@ -6437,32 +7021,32 @@ msgid "" "example 90%) it will be computed over layer height." msgstr "" -#: src/libslic3r/PrintConfig.cpp:997 +#: src/libslic3r/PrintConfig.cpp:1010 msgid "Infill before perimeters" msgstr "" -#: src/libslic3r/PrintConfig.cpp:998 +#: src/libslic3r/PrintConfig.cpp:1011 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:1016 msgid "Only infill where needed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1005 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1012 +#: src/libslic3r/PrintConfig.cpp:1025 msgid "Infill/perimeters overlap" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1014 +#: 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 " @@ -6470,30 +7054,30 @@ msgid "" "perimeter extrusion width." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1025 +#: src/libslic3r/PrintConfig.cpp:1038 msgid "Speed for printing the internal fill. Set to zero for auto." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1033 +#: src/libslic3r/PrintConfig.cpp:1046 msgid "Inherits profile" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1034 +#: src/libslic3r/PrintConfig.cpp:1047 msgid "Name of the profile, from which this profile inherits." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1047 +#: src/libslic3r/PrintConfig.cpp:1060 msgid "Interface shells" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1048 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1057 +#: 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 " @@ -6501,11 +7085,11 @@ msgid "" "[layer_z]." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1068 +#: src/libslic3r/PrintConfig.cpp:1081 msgid "Supports remaining times" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1069 +#: 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. " @@ -6513,152 +7097,152 @@ msgid "" "firmware supports M73 Qxx Sxx for the silent mode." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1077 +#: src/libslic3r/PrintConfig.cpp:1090 msgid "Supports stealth mode" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1078 +#: src/libslic3r/PrintConfig.cpp:1091 msgid "The firmware supports stealth mode" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1102 +#: src/libslic3r/PrintConfig.cpp:1115 msgid "Maximum feedrate X" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1103 +#: src/libslic3r/PrintConfig.cpp:1116 msgid "Maximum feedrate Y" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1104 +#: src/libslic3r/PrintConfig.cpp:1117 msgid "Maximum feedrate Z" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1105 +#: src/libslic3r/PrintConfig.cpp:1118 msgid "Maximum feedrate E" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1108 +#: src/libslic3r/PrintConfig.cpp:1121 msgid "Maximum feedrate of the X axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1109 +#: src/libslic3r/PrintConfig.cpp:1122 msgid "Maximum feedrate of the Y axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1110 +#: src/libslic3r/PrintConfig.cpp:1123 msgid "Maximum feedrate of the Z axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1111 +#: src/libslic3r/PrintConfig.cpp:1124 msgid "Maximum feedrate of the E axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1120 +#: src/libslic3r/PrintConfig.cpp:1133 msgid "Maximum acceleration X" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1121 +#: src/libslic3r/PrintConfig.cpp:1134 msgid "Maximum acceleration Y" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1122 +#: src/libslic3r/PrintConfig.cpp:1135 msgid "Maximum acceleration Z" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1123 +#: src/libslic3r/PrintConfig.cpp:1136 msgid "Maximum acceleration E" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1126 +#: src/libslic3r/PrintConfig.cpp:1139 msgid "Maximum acceleration of the X axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1127 +#: src/libslic3r/PrintConfig.cpp:1140 msgid "Maximum acceleration of the Y axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1128 +#: src/libslic3r/PrintConfig.cpp:1141 msgid "Maximum acceleration of the Z axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1129 +#: src/libslic3r/PrintConfig.cpp:1142 msgid "Maximum acceleration of the E axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1138 +#: src/libslic3r/PrintConfig.cpp:1151 msgid "Maximum jerk X" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1139 +#: src/libslic3r/PrintConfig.cpp:1152 msgid "Maximum jerk Y" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1140 +#: src/libslic3r/PrintConfig.cpp:1153 msgid "Maximum jerk Z" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1141 +#: src/libslic3r/PrintConfig.cpp:1154 msgid "Maximum jerk E" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1144 +#: src/libslic3r/PrintConfig.cpp:1157 msgid "Maximum jerk of the X axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1145 +#: src/libslic3r/PrintConfig.cpp:1158 msgid "Maximum jerk of the Y axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1146 +#: src/libslic3r/PrintConfig.cpp:1159 msgid "Maximum jerk of the Z axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1147 +#: src/libslic3r/PrintConfig.cpp:1160 msgid "Maximum jerk of the E axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1158 +#: src/libslic3r/PrintConfig.cpp:1171 msgid "Minimum feedrate when extruding" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1160 +#: src/libslic3r/PrintConfig.cpp:1173 msgid "Minimum feedrate when extruding (M205 S)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1169 +#: src/libslic3r/PrintConfig.cpp:1182 msgid "Minimum travel feedrate" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1171 +#: src/libslic3r/PrintConfig.cpp:1184 msgid "Minimum travel feedrate (M205 T)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1180 +#: src/libslic3r/PrintConfig.cpp:1193 msgid "Maximum acceleration when extruding" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1182 +#: src/libslic3r/PrintConfig.cpp:1195 msgid "Maximum acceleration when extruding (M204 S)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1191 +#: src/libslic3r/PrintConfig.cpp:1204 msgid "Maximum acceleration when retracting" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1193 +#: src/libslic3r/PrintConfig.cpp:1206 msgid "Maximum acceleration when retracting (M204 T)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1201 src/libslic3r/PrintConfig.cpp:1210 +#: src/libslic3r/PrintConfig.cpp:1214 src/libslic3r/PrintConfig.cpp:1223 msgid "Max" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1202 +#: src/libslic3r/PrintConfig.cpp:1215 msgid "This setting represents the maximum speed of your fan." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1211 -#, possible-c-format +#: 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 " @@ -6666,28 +7250,28 @@ msgid "" "adhesion. If set to 0, layer height is limited to 75% of the nozzle diameter." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1221 +#: src/libslic3r/PrintConfig.cpp:1234 msgid "Max print speed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1222 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1232 +#: src/libslic3r/PrintConfig.cpp:1245 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:1254 msgid "Max volumetric slope positive" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1242 src/libslic3r/PrintConfig.cpp:1253 +#: 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 " @@ -6695,95 +7279,95 @@ msgid "" "s) to 5.4 mm³/s (feedrate 60 mm/s) will take at least 2 seconds." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1246 src/libslic3r/PrintConfig.cpp:1257 +#: src/libslic3r/PrintConfig.cpp:1259 src/libslic3r/PrintConfig.cpp:1270 msgid "mm³/s²" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1252 +#: src/libslic3r/PrintConfig.cpp:1265 msgid "Max volumetric slope negative" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1264 src/libslic3r/PrintConfig.cpp:1273 +#: src/libslic3r/PrintConfig.cpp:1277 src/libslic3r/PrintConfig.cpp:1286 msgid "Min" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1265 +#: src/libslic3r/PrintConfig.cpp:1278 msgid "This setting represents the minimum PWM your fan needs to work." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1274 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1282 +#: src/libslic3r/PrintConfig.cpp:1295 msgid "Min print speed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1283 +#: src/libslic3r/PrintConfig.cpp:1296 msgid "Slic3r will not scale speed down below this speed." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1290 +#: src/libslic3r/PrintConfig.cpp:1303 msgid "Minimal filament extrusion length" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1291 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1300 +#: src/libslic3r/PrintConfig.cpp:1313 msgid "Configuration notes" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1301 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1311 +#: src/libslic3r/PrintConfig.cpp:1324 msgid "" "This is the diameter of your extruder nozzle (for example: 0.5, 0.35 etc.)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1316 +#: src/libslic3r/PrintConfig.cpp:1329 msgid "Host Type" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1317 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1328 +#: src/libslic3r/PrintConfig.cpp:1343 msgid "Only retract when crossing perimeters" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1329 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1336 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1343 +#: src/libslic3r/PrintConfig.cpp:1358 msgid "Output filename format" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1344 +#: 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], " @@ -6791,31 +7375,31 @@ msgid "" "[input_filename], [input_filename_base]." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1353 +#: src/libslic3r/PrintConfig.cpp:1368 msgid "Detect bridging perimeters" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1355 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1361 +#: src/libslic3r/PrintConfig.cpp:1376 msgid "Filament parking position" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1362 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1370 +#: src/libslic3r/PrintConfig.cpp:1385 msgid "Extra loading distance" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1371 +#: 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 " @@ -6823,28 +7407,28 @@ msgid "" "than unloading." 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:1394 src/libslic3r/PrintConfig.cpp:1412 +#: src/libslic3r/PrintConfig.cpp:1425 src/libslic3r/PrintConfig.cpp:1435 msgid "Perimeters" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1380 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1388 +#: src/libslic3r/PrintConfig.cpp:1403 msgid "Perimeter extruder" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1390 +#: src/libslic3r/PrintConfig.cpp:1405 msgid "" "The extruder to use when printing perimeters and brim. First extruder is 1." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1399 +#: 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 " @@ -6853,12 +7437,12 @@ msgid "" "it will be computed over layer height." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1411 +#: src/libslic3r/PrintConfig.cpp:1427 msgid "" "Speed for perimeters (contours, aka vertical shells). Set to zero for auto." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1421 +#: 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 " @@ -6866,11 +7450,11 @@ msgid "" "Perimeters option is enabled." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1425 +#: src/libslic3r/PrintConfig.cpp:1441 msgid "(minimum)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1433 +#: 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. " @@ -6879,55 +7463,55 @@ msgid "" "environment variables." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1445 +#: src/libslic3r/PrintConfig.cpp:1461 msgid "Printer type" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1446 +#: src/libslic3r/PrintConfig.cpp:1462 msgid "Type of the printer." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1451 +#: src/libslic3r/PrintConfig.cpp:1467 msgid "Printer notes" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1452 +#: src/libslic3r/PrintConfig.cpp:1468 msgid "You can put your notes regarding the printer here." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1460 +#: src/libslic3r/PrintConfig.cpp:1476 msgid "Printer vendor" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1461 +#: src/libslic3r/PrintConfig.cpp:1477 msgid "Name of the printer vendor." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1466 +#: src/libslic3r/PrintConfig.cpp:1482 msgid "Printer variant" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1467 +#: src/libslic3r/PrintConfig.cpp:1483 msgid "" "Name of the printer variant. For example, the printer variants may be " "differentiated by a nozzle diameter." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1480 +#: src/libslic3r/PrintConfig.cpp:1496 msgid "Raft layers" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1482 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1490 +#: src/libslic3r/PrintConfig.cpp:1506 msgid "Resolution" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1491 +#: 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 " @@ -6935,278 +7519,278 @@ msgid "" "simplification and use full resolution from input." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1501 +#: src/libslic3r/PrintConfig.cpp:1517 msgid "Minimum travel after retraction" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1502 +#: src/libslic3r/PrintConfig.cpp:1518 msgid "" "Retraction is not triggered when travel moves are shorter than this length." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1508 +#: src/libslic3r/PrintConfig.cpp:1524 msgid "Retract amount before wipe" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1509 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1516 +#: src/libslic3r/PrintConfig.cpp:1532 msgid "Retract on layer change" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1517 +#: src/libslic3r/PrintConfig.cpp:1533 msgid "This flag enforces a retraction whenever a Z move is done." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1522 src/libslic3r/PrintConfig.cpp:1530 +#: src/libslic3r/PrintConfig.cpp:1538 src/libslic3r/PrintConfig.cpp:1546 msgid "Length" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1523 +#: src/libslic3r/PrintConfig.cpp:1539 msgid "Retraction Length" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1524 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1526 src/libslic3r/PrintConfig.cpp:1535 +#: src/libslic3r/PrintConfig.cpp:1542 src/libslic3r/PrintConfig.cpp:1551 msgid "mm (zero to disable)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1531 +#: src/libslic3r/PrintConfig.cpp:1547 msgid "Retraction Length (Toolchange)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1532 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1540 +#: src/libslic3r/PrintConfig.cpp:1556 msgid "Lift Z" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1541 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1548 +#: src/libslic3r/PrintConfig.cpp:1564 msgid "Above Z" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1549 +#: src/libslic3r/PrintConfig.cpp:1565 msgid "Only lift Z above" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1550 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1557 +#: src/libslic3r/PrintConfig.cpp:1573 msgid "Below Z" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1558 +#: src/libslic3r/PrintConfig.cpp:1574 msgid "Only lift Z below" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1559 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1567 src/libslic3r/PrintConfig.cpp:1575 +#: src/libslic3r/PrintConfig.cpp:1583 src/libslic3r/PrintConfig.cpp:1591 msgid "Extra length on restart" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1568 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1576 +#: src/libslic3r/PrintConfig.cpp:1592 msgid "" "When the retraction is compensated after changing tool, the extruder will " "push this additional amount of filament." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1583 src/libslic3r/PrintConfig.cpp:1584 +#: src/libslic3r/PrintConfig.cpp:1599 src/libslic3r/PrintConfig.cpp:1600 msgid "Retraction Speed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1585 +#: src/libslic3r/PrintConfig.cpp:1601 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:1607 src/libslic3r/PrintConfig.cpp:1608 msgid "Deretraction Speed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1593 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1600 +#: src/libslic3r/PrintConfig.cpp:1616 msgid "Seam position" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1602 +#: src/libslic3r/PrintConfig.cpp:1618 msgid "Position of perimeters starting points." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1608 +#: src/libslic3r/PrintConfig.cpp:1624 msgid "Random" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1609 +#: src/libslic3r/PrintConfig.cpp:1625 msgid "Nearest" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1610 +#: src/libslic3r/PrintConfig.cpp:1626 msgid "Aligned" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1618 +#: src/libslic3r/PrintConfig.cpp:1634 msgid "Direction" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1620 +#: src/libslic3r/PrintConfig.cpp:1636 msgid "Preferred direction of the seam" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1621 +#: src/libslic3r/PrintConfig.cpp:1637 msgid "Seam preferred direction" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1628 +#: src/libslic3r/PrintConfig.cpp:1644 msgid "Jitter" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1630 +#: src/libslic3r/PrintConfig.cpp:1646 msgid "Seam preferred direction jitter" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1631 +#: src/libslic3r/PrintConfig.cpp:1647 msgid "Preferred direction of the seam - jitter" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1641 +#: src/libslic3r/PrintConfig.cpp:1657 msgid "USB/serial port for printer connection." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1648 +#: src/libslic3r/PrintConfig.cpp:1664 msgid "Serial port speed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1649 +#: src/libslic3r/PrintConfig.cpp:1665 msgid "Speed (baud) of USB/serial port for printer connection." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1658 +#: src/libslic3r/PrintConfig.cpp:1674 msgid "Distance from object" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1659 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1666 +#: src/libslic3r/PrintConfig.cpp:1682 msgid "Skirt height" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1667 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1674 +#: src/libslic3r/PrintConfig.cpp:1690 msgid "Loops (minimum)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1675 +#: src/libslic3r/PrintConfig.cpp:1691 msgid "Skirt Loops" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1676 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1684 +#: src/libslic3r/PrintConfig.cpp:1700 msgid "Slow down if layer print time is below" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1685 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1695 +#: src/libslic3r/PrintConfig.cpp:1711 msgid "Small perimeters" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1697 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1707 +#: src/libslic3r/PrintConfig.cpp:1723 msgid "Solid infill threshold area" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1709 +#: src/libslic3r/PrintConfig.cpp:1725 msgid "" "Force solid infill for regions having a smaller area than the specified " "threshold." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1710 +#: src/libslic3r/PrintConfig.cpp:1726 msgid "mm²" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1716 +#: src/libslic3r/PrintConfig.cpp:1732 msgid "Solid infill extruder" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1718 +#: src/libslic3r/PrintConfig.cpp:1734 msgid "The extruder to use when printing solid infill." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1724 +#: src/libslic3r/PrintConfig.cpp:1740 msgid "Solid infill every" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1726 +#: 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 " @@ -7214,7 +7798,7 @@ msgid "" "according to nozzle diameter and layer height." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1738 +#: 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, " @@ -7222,22 +7806,22 @@ msgid "" "(for example 90%) it will be computed over layer height." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1748 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1760 +#: src/libslic3r/PrintConfig.cpp:1777 msgid "Number of solid layers to generate on top and bottom surfaces." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1766 +#: src/libslic3r/PrintConfig.cpp:1783 msgid "Spiral vase" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1767 +#: 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, " @@ -7246,18 +7830,18 @@ msgid "" "when printing more than an object." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1775 +#: src/libslic3r/PrintConfig.cpp:1792 msgid "Temperature variation" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1776 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1786 +#: 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 " @@ -7268,7 +7852,7 @@ msgid "" "put a \"M109 S[first_layer_temperature]\" command wherever you want." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1801 +#: 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 " @@ -7281,93 +7865,105 @@ msgid "" "extruders, the gcode is processed in extruder order." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1817 +#: src/libslic3r/PrintConfig.cpp:1834 msgid "Single Extruder Multi Material" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1818 +#: src/libslic3r/PrintConfig.cpp:1835 msgid "The printer multiplexes filaments into a single hot end." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1823 +#: src/libslic3r/PrintConfig.cpp:1840 msgid "Prime all printing extruders" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1824 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1829 +#: src/libslic3r/PrintConfig.cpp:1846 +msgid "No sparse layers (EXPERIMENTAL)" +msgstr "" + +#: 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 "" + +#: src/libslic3r/PrintConfig.cpp:1854 msgid "Generate support material" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1831 +#: src/libslic3r/PrintConfig.cpp:1856 msgid "Enable support material generation." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1835 +#: src/libslic3r/PrintConfig.cpp:1860 msgid "Auto generated supports" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1837 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1843 +#: src/libslic3r/PrintConfig.cpp:1868 msgid "XY separation between an object and its support" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1845 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1855 +#: src/libslic3r/PrintConfig.cpp:1880 msgid "Pattern angle" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1857 +#: src/libslic3r/PrintConfig.cpp:1882 msgid "" "Use this setting to rotate the support material pattern on the horizontal " "plane." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1867 src/libslic3r/PrintConfig.cpp:2563 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1873 +#: src/libslic3r/PrintConfig.cpp:1898 msgid "Contact Z distance" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1875 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1882 +#: src/libslic3r/PrintConfig.cpp:1907 msgid "0 (soluble)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1883 +#: src/libslic3r/PrintConfig.cpp:1908 msgid "0.2 (detachable)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1888 +#: src/libslic3r/PrintConfig.cpp:1913 msgid "Enforce support for the first" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1890 +#: 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 " @@ -7375,21 +7971,21 @@ msgid "" "of objects having a very thin or poor footprint on the build plate." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1895 +#: src/libslic3r/PrintConfig.cpp:1920 msgid "Enforce support for the first n layers" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1901 +#: src/libslic3r/PrintConfig.cpp:1926 msgid "Support material/raft/skirt extruder" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1903 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1912 +#: 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, " @@ -7397,89 +7993,89 @@ msgid "" "example 90%) it will be computed over layer height." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1920 +#: src/libslic3r/PrintConfig.cpp:1946 msgid "Interface loops" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1922 +#: src/libslic3r/PrintConfig.cpp:1948 msgid "" "Cover the top contact layer of the supports with loops. Disabled by default." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1927 +#: src/libslic3r/PrintConfig.cpp:1953 msgid "Support material/raft interface extruder" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1929 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1936 +#: src/libslic3r/PrintConfig.cpp:1962 msgid "Interface layers" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1938 +#: src/libslic3r/PrintConfig.cpp:1964 msgid "" "Number of interface layers to insert between the object(s) and support " "material." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1945 +#: src/libslic3r/PrintConfig.cpp:1971 msgid "Interface pattern spacing" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1947 +#: src/libslic3r/PrintConfig.cpp:1973 msgid "Spacing between interface lines. Set zero to get a solid interface." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1956 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:1965 +#: src/libslic3r/PrintConfig.cpp:1991 msgid "Pattern" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1967 +#: src/libslic3r/PrintConfig.cpp:1993 msgid "Pattern used to generate support material." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1973 +#: src/libslic3r/PrintConfig.cpp:1999 msgid "Rectilinear grid" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1979 +#: src/libslic3r/PrintConfig.cpp:2005 msgid "Pattern spacing" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1981 +#: src/libslic3r/PrintConfig.cpp:2007 msgid "Spacing between support material lines." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1990 +#: src/libslic3r/PrintConfig.cpp:2016 msgid "Speed for printing support material." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1997 +#: src/libslic3r/PrintConfig.cpp:2023 msgid "Synchronize with object layers" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1999 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2005 +#: src/libslic3r/PrintConfig.cpp:2031 msgid "Overhang threshold" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2007 +#: 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 " @@ -7488,43 +8084,43 @@ msgid "" "detection (recommended)." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2019 +#: src/libslic3r/PrintConfig.cpp:2045 msgid "With sheath around the support" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2021 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2028 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2036 +#: src/libslic3r/PrintConfig.cpp:2062 msgid "Detect thin walls" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2038 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2044 +#: src/libslic3r/PrintConfig.cpp:2070 msgid "Threads" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2045 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2057 +#: 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 " @@ -7534,7 +8130,7 @@ msgid "" "behaviour both before and after the toolchange." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2070 +#: 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 " @@ -7543,7 +8139,7 @@ msgid "" "percentage (for example 90%) it will be computed over layer height." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2081 +#: 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 " @@ -7552,43 +8148,43 @@ msgid "" "for auto." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2096 +#: src/libslic3r/PrintConfig.cpp:2123 msgid "Number of solid layers to generate on top surfaces." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2097 +#: src/libslic3r/PrintConfig.cpp:2124 msgid "Top solid layers" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2103 +#: src/libslic3r/PrintConfig.cpp:2130 msgid "Speed for travel moves (jumps between distant extrusion points)." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2111 +#: src/libslic3r/PrintConfig.cpp:2138 msgid "Use firmware retraction" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2112 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2118 +#: src/libslic3r/PrintConfig.cpp:2145 msgid "Use relative E distances" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2119 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2125 +#: src/libslic3r/PrintConfig.cpp:2152 msgid "Use volumetric E" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2126 +#: 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 " @@ -7598,127 +8194,127 @@ msgid "" "only supported in recent Marlin." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2136 +#: src/libslic3r/PrintConfig.cpp:2163 msgid "Enable variable layer height feature" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2137 +#: src/libslic3r/PrintConfig.cpp:2164 msgid "" "Some printers or printer setups may have difficulties printing with a " "variable layer height. Enabled by default." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2143 +#: src/libslic3r/PrintConfig.cpp:2170 msgid "Wipe while retracting" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2144 +#: src/libslic3r/PrintConfig.cpp:2171 msgid "" "This flag will move the nozzle while retracting to minimize the possible " "blob on leaky extruders." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2151 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2157 +#: src/libslic3r/PrintConfig.cpp:2184 msgid "Purging volumes - load/unload volumes" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2158 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2164 +#: src/libslic3r/PrintConfig.cpp:2191 msgid "Purging volumes - matrix" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2165 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2174 +#: src/libslic3r/PrintConfig.cpp:2201 msgid "Position X" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2175 +#: src/libslic3r/PrintConfig.cpp:2202 msgid "X coordinate of the left front corner of a wipe tower" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2181 +#: src/libslic3r/PrintConfig.cpp:2208 msgid "Position Y" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2182 +#: src/libslic3r/PrintConfig.cpp:2209 msgid "Y coordinate of the left front corner of a wipe tower" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2189 +#: src/libslic3r/PrintConfig.cpp:2216 msgid "Width of a wipe tower" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2195 +#: src/libslic3r/PrintConfig.cpp:2222 msgid "Wipe tower rotation angle" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2196 +#: src/libslic3r/PrintConfig.cpp:2223 msgid "Wipe tower rotation angle with respect to x-axis." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2203 +#: src/libslic3r/PrintConfig.cpp:2230 msgid "Wipe into this object's infill" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2204 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2211 +#: src/libslic3r/PrintConfig.cpp:2238 msgid "Wipe into this object" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2212 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2218 +#: src/libslic3r/PrintConfig.cpp:2245 msgid "Maximal bridging distance" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2219 +#: src/libslic3r/PrintConfig.cpp:2246 msgid "Maximal distance between supports on sparse infill sections." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2225 +#: src/libslic3r/PrintConfig.cpp:2252 msgid "XY Size Compensation" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2227 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2235 +#: src/libslic3r/PrintConfig.cpp:2262 msgid "Z offset" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2236 +#: 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 " @@ -7726,361 +8322,389 @@ msgid "" "print bed, set this to -0.3 (or fix your endstop)." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2294 +#: src/libslic3r/PrintConfig.cpp:2330 msgid "Display width" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2295 +#: src/libslic3r/PrintConfig.cpp:2331 msgid "Width of the display" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2300 +#: src/libslic3r/PrintConfig.cpp:2336 msgid "Display height" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2301 +#: src/libslic3r/PrintConfig.cpp:2337 msgid "Height of the display" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2306 +#: src/libslic3r/PrintConfig.cpp:2342 msgid "Number of pixels in" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2308 +#: src/libslic3r/PrintConfig.cpp:2344 msgid "Number of pixels in X" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2314 +#: src/libslic3r/PrintConfig.cpp:2350 msgid "Number of pixels in Y" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2319 +#: src/libslic3r/PrintConfig.cpp:2355 msgid "Display horizontal mirroring" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2320 +#: src/libslic3r/PrintConfig.cpp:2356 msgid "Mirror horizontally" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2321 +#: src/libslic3r/PrintConfig.cpp:2357 msgid "Enable horizontal mirroring of output images" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2326 +#: src/libslic3r/PrintConfig.cpp:2362 msgid "Display vertical mirroring" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2327 +#: src/libslic3r/PrintConfig.cpp:2363 msgid "Mirror vertically" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2328 +#: src/libslic3r/PrintConfig.cpp:2364 msgid "Enable vertical mirroring of output images" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2333 +#: src/libslic3r/PrintConfig.cpp:2369 msgid "Display orientation" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2334 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2340 +#: src/libslic3r/PrintConfig.cpp:2376 msgid "Landscape" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2341 +#: src/libslic3r/PrintConfig.cpp:2377 msgid "Portrait" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2346 +#: src/libslic3r/PrintConfig.cpp:2382 msgid "Fast" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2347 +#: src/libslic3r/PrintConfig.cpp:2383 msgid "Fast tilt" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2348 +#: src/libslic3r/PrintConfig.cpp:2384 msgid "Time of the fast tilt" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2355 +#: src/libslic3r/PrintConfig.cpp:2391 msgid "Slow" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2356 +#: src/libslic3r/PrintConfig.cpp:2392 msgid "Slow tilt" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2357 +#: src/libslic3r/PrintConfig.cpp:2393 msgid "Time of the slow tilt" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2364 +#: src/libslic3r/PrintConfig.cpp:2400 msgid "Area fill" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2365 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2372 src/libslic3r/PrintConfig.cpp:2373 -#: src/libslic3r/PrintConfig.cpp:2374 +#: src/libslic3r/PrintConfig.cpp:2408 src/libslic3r/PrintConfig.cpp:2409 +#: src/libslic3r/PrintConfig.cpp:2410 msgid "Printer scaling correction" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2380 src/libslic3r/PrintConfig.cpp:2381 +#: src/libslic3r/PrintConfig.cpp:2416 src/libslic3r/PrintConfig.cpp:2417 msgid "Printer absolute correction" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2382 +#: src/libslic3r/PrintConfig.cpp:2418 msgid "" "Will inflate or deflate the sliced 2D polygons according to the sign of the " "correction." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2388 src/libslic3r/PrintConfig.cpp:2389 +#: src/libslic3r/PrintConfig.cpp:2424 src/libslic3r/PrintConfig.cpp:2425 msgid "Printer gamma correction" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2390 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2401 src/libslic3r/PrintConfig.cpp:2402 +#: src/libslic3r/PrintConfig.cpp:2438 src/libslic3r/PrintConfig.cpp:2439 +msgid "SLA material type" +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:2450 src/libslic3r/PrintConfig.cpp:2451 msgid "Initial layer height" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2408 +#: src/libslic3r/PrintConfig.cpp:2457 src/libslic3r/PrintConfig.cpp:2458 +msgid "Bottle volume" +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:2459 +msgid "ml" +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:2464 src/libslic3r/PrintConfig.cpp:2465 +msgid "Bottle weight" +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:2466 +msgid "kg" +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:2473 +msgid "g/ml" +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:2480 +msgid "money/bottle" +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:2485 msgid "Faded layers" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2409 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2416 src/libslic3r/PrintConfig.cpp:2417 +#: src/libslic3r/PrintConfig.cpp:2493 src/libslic3r/PrintConfig.cpp:2494 msgid "Minimum exposure time" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2424 src/libslic3r/PrintConfig.cpp:2425 +#: src/libslic3r/PrintConfig.cpp:2501 src/libslic3r/PrintConfig.cpp:2502 msgid "Maximum exposure time" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2432 src/libslic3r/PrintConfig.cpp:2433 +#: src/libslic3r/PrintConfig.cpp:2509 src/libslic3r/PrintConfig.cpp:2510 msgid "Exposure time" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2439 src/libslic3r/PrintConfig.cpp:2440 +#: src/libslic3r/PrintConfig.cpp:2516 src/libslic3r/PrintConfig.cpp:2517 msgid "Minimum initial exposure time" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2447 src/libslic3r/PrintConfig.cpp:2448 +#: src/libslic3r/PrintConfig.cpp:2524 src/libslic3r/PrintConfig.cpp:2525 msgid "Maximum initial exposure time" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2455 src/libslic3r/PrintConfig.cpp:2456 +#: src/libslic3r/PrintConfig.cpp:2532 src/libslic3r/PrintConfig.cpp:2533 msgid "Initial exposure time" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2462 src/libslic3r/PrintConfig.cpp:2463 +#: src/libslic3r/PrintConfig.cpp:2539 src/libslic3r/PrintConfig.cpp:2540 msgid "Correction for expansion" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2469 +#: src/libslic3r/PrintConfig.cpp:2546 msgid "SLA print material notes" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2470 +#: src/libslic3r/PrintConfig.cpp:2547 msgid "You can put your notes regarding the SLA print material here." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2478 src/libslic3r/PrintConfig.cpp:2489 +#: src/libslic3r/PrintConfig.cpp:2559 src/libslic3r/PrintConfig.cpp:2570 msgid "Default SLA material profile" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2500 +#: src/libslic3r/PrintConfig.cpp:2581 msgid "Generate supports" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2502 +#: src/libslic3r/PrintConfig.cpp:2583 msgid "Generate supports for the models" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2507 +#: src/libslic3r/PrintConfig.cpp:2588 msgid "Support head front diameter" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2509 +#: src/libslic3r/PrintConfig.cpp:2590 msgid "Diameter of the pointing side of the head" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2516 +#: src/libslic3r/PrintConfig.cpp:2597 msgid "Support head penetration" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2518 +#: src/libslic3r/PrintConfig.cpp:2599 msgid "How much the pinhead has to penetrate the model surface" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2525 +#: src/libslic3r/PrintConfig.cpp:2606 msgid "Support head width" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2527 +#: src/libslic3r/PrintConfig.cpp:2608 msgid "Width from the back sphere center to the front sphere center" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2535 +#: src/libslic3r/PrintConfig.cpp:2616 msgid "Support pillar diameter" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2537 +#: src/libslic3r/PrintConfig.cpp:2618 msgid "Diameter in mm of the support pillars" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2545 +#: src/libslic3r/PrintConfig.cpp:2626 msgid "Support pillar connection mode" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2546 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2554 +#: src/libslic3r/PrintConfig.cpp:2635 msgid "Zig-Zag" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2555 +#: src/libslic3r/PrintConfig.cpp:2636 msgid "Cross" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2556 +#: src/libslic3r/PrintConfig.cpp:2637 msgid "Dynamic" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2568 +#: src/libslic3r/PrintConfig.cpp:2649 msgid "Pillar widening factor" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2570 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2579 +#: src/libslic3r/PrintConfig.cpp:2660 msgid "Support base diameter" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2581 +#: src/libslic3r/PrintConfig.cpp:2662 msgid "Diameter in mm of the pillar base" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2589 +#: src/libslic3r/PrintConfig.cpp:2670 msgid "Support base height" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2591 +#: src/libslic3r/PrintConfig.cpp:2672 msgid "The height of the pillar base cone" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2598 +#: src/libslic3r/PrintConfig.cpp:2679 msgid "Support base safety distance" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2601 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2611 +#: src/libslic3r/PrintConfig.cpp:2692 msgid "Critical angle" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2613 +#: src/libslic3r/PrintConfig.cpp:2694 msgid "The default angle for connecting support sticks and junctions." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2621 +#: src/libslic3r/PrintConfig.cpp:2702 msgid "Max bridge length" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2623 +#: src/libslic3r/PrintConfig.cpp:2704 msgid "The max length of a bridge" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2630 +#: src/libslic3r/PrintConfig.cpp:2711 msgid "Max pillar linking distance" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2632 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2640 +#: src/libslic3r/PrintConfig.cpp:2721 msgid "Object elevation" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2642 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2653 +#: src/libslic3r/PrintConfig.cpp:2734 msgid "This is a relative measure of support points density." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2659 +#: src/libslic3r/PrintConfig.cpp:2740 msgid "Minimal distance of the support points" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2661 +#: src/libslic3r/PrintConfig.cpp:2742 msgid "No support points will be placed closer than this threshold." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2667 +#: src/libslic3r/PrintConfig.cpp:2748 msgid "Use pad" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2669 +#: src/libslic3r/PrintConfig.cpp:2750 msgid "Add a pad underneath the supported model" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2674 +#: src/libslic3r/PrintConfig.cpp:2755 msgid "Pad wall thickness" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2676 +#: src/libslic3r/PrintConfig.cpp:2757 msgid "The thickness of the pad and its optional cavity walls." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2684 +#: src/libslic3r/PrintConfig.cpp:2765 msgid "Pad wall height" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2685 +#: 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 " @@ -8088,376 +8712,373 @@ msgid "" "difficult." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2698 +#: src/libslic3r/PrintConfig.cpp:2779 +msgid "Pad brim size" +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:2780 +msgid "How far should the pad extend around the contained geometry" +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:2790 msgid "Max merge distance" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2700 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:2720 +#: src/libslic3r/PrintConfig.cpp:2812 msgid "Pad wall slope" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2722 +#: src/libslic3r/PrintConfig.cpp:2814 msgid "" "The slope of the pad wall relative to the bed plane. 90 degrees means " "straight walls." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2731 +#: src/libslic3r/PrintConfig.cpp:2823 msgid "Pad around object" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2733 +#: src/libslic3r/PrintConfig.cpp:2825 msgid "Create pad around object and ignore the support elevation" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2738 +#: src/libslic3r/PrintConfig.cpp:2830 +msgid "Pad around object everywhere" +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:2832 +msgid "Force pad around object everywhere" +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:2837 msgid "Pad object gap" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2740 +#: src/libslic3r/PrintConfig.cpp:2839 msgid "" "The gap between the object bottom and the generated pad in zero elevation " "mode." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2749 +#: src/libslic3r/PrintConfig.cpp:2848 msgid "Pad object connector stride" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2751 +#: src/libslic3r/PrintConfig.cpp:2850 msgid "" "Distance between two connector sticks which connect the object and the " "generated pad." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2758 +#: src/libslic3r/PrintConfig.cpp:2857 msgid "Pad object connector width" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2760 +#: src/libslic3r/PrintConfig.cpp:2859 msgid "" "Width of the connector sticks which connect the object and the generated pad." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2767 +#: src/libslic3r/PrintConfig.cpp:2866 msgid "Pad object connector penetration" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2770 +#: src/libslic3r/PrintConfig.cpp:2869 msgid "How much should the tiny connectors penetrate into the model body." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3130 +#: src/libslic3r/PrintConfig.cpp:3247 msgid "Export OBJ" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3131 +#: src/libslic3r/PrintConfig.cpp:3248 msgid "Export the model(s) as OBJ." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3142 +#: src/libslic3r/PrintConfig.cpp:3259 msgid "Export SLA" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3143 +#: src/libslic3r/PrintConfig.cpp:3260 msgid "Slice the model and export SLA printing layers as PNG." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3148 +#: src/libslic3r/PrintConfig.cpp:3265 msgid "Export 3MF" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3149 +#: src/libslic3r/PrintConfig.cpp:3266 msgid "Export the model(s) as 3MF." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3153 +#: src/libslic3r/PrintConfig.cpp:3270 msgid "Export AMF" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3154 +#: src/libslic3r/PrintConfig.cpp:3271 msgid "Export the model(s) as AMF." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3158 +#: src/libslic3r/PrintConfig.cpp:3275 msgid "Export STL" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3159 +#: src/libslic3r/PrintConfig.cpp:3276 msgid "Export the model(s) as STL." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3164 +#: src/libslic3r/PrintConfig.cpp:3281 msgid "Slice the model and export toolpaths as G-code." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3169 +#: src/libslic3r/PrintConfig.cpp:3286 msgid "Slice" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3170 +#: src/libslic3r/PrintConfig.cpp:3287 msgid "" "Slice the model as FFF or SLA based on the printer_technology configuration " "value." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3175 +#: src/libslic3r/PrintConfig.cpp:3292 msgid "Help" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3176 +#: src/libslic3r/PrintConfig.cpp:3293 msgid "Show this help." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3181 +#: src/libslic3r/PrintConfig.cpp:3298 msgid "Help (FFF options)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3182 +#: src/libslic3r/PrintConfig.cpp:3299 msgid "Show the full list of print/G-code configuration options." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3186 +#: src/libslic3r/PrintConfig.cpp:3303 msgid "Help (SLA options)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3187 +#: src/libslic3r/PrintConfig.cpp:3304 msgid "Show the full list of SLA print configuration options." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3191 +#: src/libslic3r/PrintConfig.cpp:3308 msgid "Output Model Info" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3192 +#: src/libslic3r/PrintConfig.cpp:3309 msgid "Write information about the model to the console." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3196 +#: src/libslic3r/PrintConfig.cpp:3313 msgid "Save config file" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3197 +#: src/libslic3r/PrintConfig.cpp:3314 msgid "Save configuration to the specified file." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3207 +#: src/libslic3r/PrintConfig.cpp:3324 msgid "Align XY" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3208 +#: src/libslic3r/PrintConfig.cpp:3325 msgid "Align the model to the given point." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3213 +#: src/libslic3r/PrintConfig.cpp:3330 msgid "Cut model at the given Z." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3234 +#: src/libslic3r/PrintConfig.cpp:3351 msgid "Center" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3235 +#: src/libslic3r/PrintConfig.cpp:3352 msgid "Center the print around the given center." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3239 +#: src/libslic3r/PrintConfig.cpp:3356 msgid "Don't arrange" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3240 +#: src/libslic3r/PrintConfig.cpp:3357 msgid "" "Do not rearrange the given models before merging and keep their original XY " "coordinates." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3243 +#: src/libslic3r/PrintConfig.cpp:3360 msgid "Duplicate" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3244 +#: src/libslic3r/PrintConfig.cpp:3361 msgid "Multiply copies by this factor." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3248 +#: src/libslic3r/PrintConfig.cpp:3365 msgid "Duplicate by grid" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3249 +#: src/libslic3r/PrintConfig.cpp:3366 msgid "Multiply copies by creating a grid." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3252 +#: src/libslic3r/PrintConfig.cpp:3369 msgid "Merge" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3253 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:3258 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:3262 +#: src/libslic3r/PrintConfig.cpp:3379 msgid "Rotation angle around the Z axis in degrees." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3266 +#: src/libslic3r/PrintConfig.cpp:3383 msgid "Rotate around X" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3267 +#: src/libslic3r/PrintConfig.cpp:3384 msgid "Rotation angle around the X axis in degrees." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3271 +#: src/libslic3r/PrintConfig.cpp:3388 msgid "Rotate around Y" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3272 +#: src/libslic3r/PrintConfig.cpp:3389 msgid "Rotation angle around the Y axis in degrees." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3277 +#: src/libslic3r/PrintConfig.cpp:3394 msgid "Scaling factor or percentage." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3282 +#: src/libslic3r/PrintConfig.cpp:3399 msgid "" "Detect unconnected parts in the given model(s) and split them into separate " "objects." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3285 +#: src/libslic3r/PrintConfig.cpp:3402 msgid "Scale to Fit" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3286 +#: src/libslic3r/PrintConfig.cpp:3403 msgid "Scale to fit the given volume." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3295 +#: src/libslic3r/PrintConfig.cpp:3412 msgid "Ignore non-existent config files" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3296 +#: src/libslic3r/PrintConfig.cpp:3413 msgid "Do not fail if a file supplied to --load does not exist." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3299 +#: src/libslic3r/PrintConfig.cpp:3416 msgid "Load config file" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3300 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:3303 +#: src/libslic3r/PrintConfig.cpp:3420 msgid "Output File" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3304 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:3314 +#: src/libslic3r/PrintConfig.cpp:3431 msgid "Data directory" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3315 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:3318 +#: src/libslic3r/PrintConfig.cpp:3435 msgid "Logging level" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3319 +#: 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 "" -#: src/libslic3r/PrintConfig.cpp:3324 +#: src/libslic3r/PrintConfig.cpp:3441 msgid "Render with a software renderer" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3325 +#: 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 "" -#: src/libslic3r/PrintObject.cpp:110 +#: src/libslic3r/PrintObject.cpp:106 msgid "Processing triangulated mesh" msgstr "" -#: src/libslic3r/PrintObject.cpp:141 +#: src/libslic3r/PrintObject.cpp:150 msgid "Generating perimeters" msgstr "" -#: src/libslic3r/PrintObject.cpp:251 +#: src/libslic3r/PrintObject.cpp:260 msgid "Preparing infill" msgstr "" -#: src/libslic3r/PrintObject.cpp:391 +#: src/libslic3r/PrintObject.cpp:400 msgid "Generating support material" msgstr "" -#: src/libslic3r/GCode/PreviewData.cpp:160 -msgid "Mixed" -msgstr "" - -#: src/libslic3r/GCode/PreviewData.cpp:380 +#: src/libslic3r/GCode/PreviewData.cpp:362 msgid "Height (mm)" msgstr "" -#: src/libslic3r/GCode/PreviewData.cpp:382 +#: src/libslic3r/GCode/PreviewData.cpp:364 msgid "Width (mm)" msgstr "" -#: src/libslic3r/GCode/PreviewData.cpp:384 +#: src/libslic3r/GCode/PreviewData.cpp:366 msgid "Speed (mm/s)" msgstr "" -#: src/libslic3r/GCode/PreviewData.cpp:386 -msgid "Volumetric flow rate (mm3/s)" +#: src/libslic3r/GCode/PreviewData.cpp:368 +msgid "Fan Speed (%)" msgstr "" -#: src/libslic3r/GCode/PreviewData.cpp:477 -msgid "Default print color" -msgstr "" - -#: src/libslic3r/GCode/PreviewData.cpp:484 -#, possible-c-format -msgid "up to %.2f mm" -msgstr "" - -#: src/libslic3r/GCode/PreviewData.cpp:488 -#, possible-c-format -msgid "above %.2f mm" -msgstr "" - -#: src/libslic3r/GCode/PreviewData.cpp:493 -#, possible-c-format -msgid "%.2f - %.2f mm" +#: src/libslic3r/GCode/PreviewData.cpp:370 +msgid "Volumetric flow rate (mm³/s)" msgstr "" diff --git a/resources/localization/ja/PrusaSlicer.mo b/resources/localization/ja/PrusaSlicer.mo new file mode 100644 index 000000000..964437275 Binary files /dev/null and b/resources/localization/ja/PrusaSlicer.mo differ diff --git a/resources/localization/ja/PrusaSlicer_ja.po b/resources/localization/ja/PrusaSlicer_ja.po new file mode 100644 index 000000000..8480650ea --- /dev/null +++ b/resources/localization/ja/PrusaSlicer_ja.po @@ -0,0 +1,7753 @@ +msgid "" +msgstr "" +"Language: ja\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\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/PresetHints.cpp:39 +msgid "During the other layers, fan" +msgstr "他のレイヤーの間、ファン" + +#: 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 "レイヤーのプリント予測時間が長くなったものの、まだおよそ%1%sより短い場合、冷却ファンは%2%%%から%3%%%の間で時間に比例した回転数になります。" + +#: src/slic3r/GUI/MainFrame.cpp:61 +msgid " - Remember to check for updates at http://github.com/prusa3d/PrusaSlicer/releases" +msgstr "-http://github.com/prusa3d/PrusaSlicer/releasesで更新を確認することを忘れないでください" + +#: src/slic3r/GUI/MainFrame.cpp:727 +msgid " was successfully sliced." +msgstr "スライス完了" + +#: src/libslic3r/PrintConfig.cpp:179 src/libslic3r/PrintConfig.cpp:745 +#: src/libslic3r/PrintConfig.cpp:1154 src/libslic3r/PrintConfig.cpp:1217 +#: src/libslic3r/PrintConfig.cpp:1462 src/libslic3r/PrintConfig.cpp:2260 +#: src/libslic3r/PrintConfig.cpp:2502 +msgid "%" +msgstr "%" + +#: src/libslic3r/GCode/PreviewData.cpp:504 +#, c-format +msgid "%.2f - %.2f mm" +msgstr "%.2f〜%.2f mm" + +#: src/slic3r/GUI/Tab.cpp:2895 +msgid "%1% - Copy" +msgstr "%1% - コピー" + +#. TRN Remove/Delete +#: src/slic3r/GUI/Tab.cpp:2958 +msgid "%1% Preset" +msgstr "プリセット%1%" + +#: src/slic3r/GUI/Plater.cpp:3831 +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%プリセットの再読み込みが必要です。" + +#: src/libslic3r/Print.cpp:1282 +msgid "%1%=%2% mm is too low to be printable at a layer height %3% mm" +msgstr "%1%=%2% mmはレイヤーの高さ%3% mmでプリントするには低すぎます" + +#: 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で%3.2f mm³/ s。" + +#: src/slic3r/GUI/Plater.cpp:974 +#, c-format +msgid "%d (%d shells)" +msgstr "%d (%d 領域)" + +#: src/slic3r/GUI/Plater.cpp:982 +#, c-format +msgid "%d degenerate facets, %d edges fixed, %d facets removed, %d facets added, %d facets reversed, %d backwards edges" +msgstr "%d縮退ファセット、%dエッジ修正、%dファセット削除、%dファセット追加、%dファセット反転、%d後方エッジ" + +#: src/slic3r/GUI/PresetHints.cpp:268 +#, c-format +msgid "%d lines: %.2f mm" +msgstr "%dライン:%.2f mm" + +#: src/slic3r/GUI/MainFrame.cpp:894 +#, c-format +msgid "%d presets successfully imported." +msgstr "%d プリセットを正常にインポートしました。" + +#: src/slic3r/GUI/MainFrame.cpp:550 +#, c-format +msgid "%s &Website" +msgstr "%s&Webサイト" + +#: src/slic3r/GUI/UpdateDialogs.cpp:113 +#, c-format +msgid "%s configuration is incompatible" +msgstr "%s構成に互換性がありません" + +#: src/slic3r/GUI/Field.cpp:136 +#, c-format +msgid "%s doesn't support percentage" +msgstr "%sは比率をサポートしていません" + +#: src/slic3r/GUI/MsgDialog.cpp:73 +#, c-format +msgid "%s error" +msgstr "%sエラー" + +#: src/slic3r/GUI/ConfigWizard.cpp:336 +#, c-format +msgid "%s Family" +msgstr "%sファミリー" + +#: src/slic3r/GUI/MsgDialog.cpp:74 +#, c-format +msgid "%s has encountered an error" +msgstr "%sでエラーが発生しました" + +#: src/slic3r/GUI/GUI_App.cpp:132 +#, 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/BackgroundSlicingProcess.cpp:155 +#, 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でエラーが発生しました。 メモリ不足の可能性があります。 システムに十分な空きメモリー領域があるのに発生した場合、バグの可能性がありますので、ご報告いただければ幸いです。" + +#: src/slic3r/GUI/UpdateDialogs.cpp:112 +#, c-format +msgid "%s incompatibility" +msgstr "%sと互換性がありません" + +#: src/slic3r/GUI/UpdateDialogs.cpp:172 +#, 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は、更新された構成を使用するようになりました。さまざまなプリンターのデフォルト設定を含む、いわゆる「システムプリセット」が導入されました。 これらのシステムプリセットは変更できません。代わりに、ユーザーはシステムプリセットの1つから設定を継承して独自のプリセットを作成できます。新しく作成されたプリセットは、その前身から値を継承するか、変更された値で上書きできます。%sの指示に従って新しい設定を行い、自動プリセット更新を有効にするかどうかを選択します。" + +#: src/slic3r/GUI/GUI_App.cpp:681 +#, c-format +msgid "%s View Mode" +msgstr "%s表示モード" + +#: src/slic3r/GUI/MainFrame.cpp:563 +#, c-format +msgid "&About %s" +msgstr "%sについて" + +#: src/slic3r/GUI/GUI_App.cpp:769 +msgid "&Configuration" +msgstr "構成" + +#: src/slic3r/GUI/GUI_App.cpp:661 +msgid "&Configuration Snapshots" +msgstr "構成スナップショット" + +#: src/slic3r/GUI/MainFrame.cpp:454 +msgid "&Copy" +msgstr "コピー" + +#: src/slic3r/GUI/MainFrame.cpp:447 +msgid "&Delete selected" +msgstr "選択したものを削除" + +#: src/slic3r/GUI/MainFrame.cpp:575 +msgid "&Edit" +msgstr "編集" + +#: src/slic3r/GUI/MainFrame.cpp:377 +msgid "&Export" +msgstr "エクスポート" + +#: src/slic3r/GUI/MainFrame.cpp:480 src/slic3r/GUI/MainFrame.cpp:604 +msgid "&Filament Settings Tab" +msgstr "フィラメント設定タブ" + +#: src/slic3r/GUI/MainFrame.cpp:574 +msgid "&File" +msgstr "ファイル" + +#: src/slic3r/GUI/ConfigWizard.cpp:1094 +msgid "&Finish" +msgstr "&終了" + +#: src/slic3r/GUI/MainFrame.cpp:580 +msgid "&Help" +msgstr "ヘルプ" + +#: src/slic3r/GUI/MainFrame.cpp:359 +msgid "&Import" +msgstr "インポート" + +#: src/slic3r/GUI/MainFrame.cpp:376 +msgid "&New Project" +msgstr "新しいプロジェクト" + +#: src/slic3r/GUI/ConfigWizard.cpp:1093 +msgid "&Next >" +msgstr "次 >" + +#: src/slic3r/GUI/MainFrame.cpp:339 +msgid "&Open Project" +msgstr "プロジェクトのオープン" + +#: src/slic3r/GUI/MainFrame.cpp:456 +msgid "&Paste" +msgstr "ペースト" + +#: src/slic3r/GUI/MainFrame.cpp:471 +msgid "&Plater Tab" +msgstr "プレートタブ" + +#: src/slic3r/GUI/GUI_App.cpp:665 +msgid "&Preferences" +msgstr "環境設定" + +#: src/slic3r/GUI/MainFrame.cpp:409 +msgid "&Quit" +msgstr "中止" + +#: src/slic3r/GUI/MainFrame.cpp:561 +msgid "&Redo" +msgstr "やり直し" + +#: src/slic3r/GUI/MainFrame.cpp:406 +msgid "&Repair STL file" +msgstr "STLファイルの修復" + +#: src/slic3r/GUI/MainFrame.cpp:341 +msgid "&Save Project" +msgstr "プロジェクトを保存" + +#: src/slic3r/GUI/MainFrame.cpp:444 +msgid "&Select all" +msgstr "全て選択" + +#: src/slic3r/GUI/MainFrame.cpp:558 +msgid "&Undo" +msgstr "やり直し" + +#: src/slic3r/GUI/MainFrame.cpp:577 +msgid "&View" +msgstr "ビュー" + +#: src/slic3r/GUI/MainFrame.cpp:576 +msgid "&Window" +msgstr "ウィンドウ" + +#: src/libslic3r/PrintConfig.cpp:1376 +msgid "(minimum)" +msgstr "(最小)" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:111 +msgid "(Re)slice" +msgstr "スライス" + +#: src/slic3r/GUI/MainFrame.cpp:455 +msgid "(Re)Slice No&w" +msgstr "(再)スライス実行" + +#: src/slic3r/GUI/MainFrame.cpp:641 +msgid ") not found." +msgstr ")見つかりません。" + +#: src/libslic3r/PrintConfig.cpp:1857 +msgid "0 (soluble)" +msgstr "0 (溶解性)" + +#: src/libslic3r/PrintConfig.cpp:1858 +msgid "0.2 (detachable)" +msgstr "0.2(分離可能)" + +#: src/slic3r/GUI/MainFrame.cpp:487 +msgid "3&D" +msgstr "3&D" + +#: src/slic3r/GUI/Plater.cpp:3074 +msgid "3D editor view" +msgstr "3D編集画面" + +#: src/libslic3r/PrintConfig.cpp:804 +msgid "3D Honeycomb" +msgstr "3Dハニカム" + +#: src/slic3r/GUI/Plater.cpp:3590 +#, c-format +msgid "3MF file exported to %s" +msgstr "3MFファイルを%sにエクスポートしました" + +#: src/slic3r/GUI/ConfigWizard.cpp:1092 +msgid "< &Back" +msgstr "< 戻る" + +#: src/libslic3r/PrintConfig.cpp:251 +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 "アクティブなプリントプロファイルの構成値を使用する論理式。 この式の結果がtrueの場合、このプロファイルはアクティブなプリントプロファイルと互換性があるとみなされます。" + +#: src/libslic3r/PrintConfig.cpp:236 +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/slic3r/GUI/ConfigWizard.cpp:609 +msgid "A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS." +msgstr "一般的には、PLAは160〜230℃、ABSは215〜250℃です。" + +#: src/slic3r/GUI/ConfigWizard.cpp:623 +msgid "A rule of thumb is 60 °C for PLA and 110 °C for ABS. Leave zero if you have no heated bed." +msgstr "一般的には、PLAでは60℃、ABSでは110℃です。 ヒートベッドがないプリンタではゼロを入力します。" + +#: src/slic3r/GUI/GLCanvas3D.cpp:721 +msgid "A toolpath outside the print area was detected" +msgstr "プリント可能範囲外のツールパスが検出されました" + +#: src/slic3r/GUI/AboutDialog.cpp:35 +#, c-format +msgid "About %s" +msgstr "%sについて" + +#: src/libslic3r/GCode/PreviewData.cpp:499 +#, c-format +msgid "above %.2f mm" +msgstr "%.2fmm以上" + +#: src/libslic3r/PrintConfig.cpp:1499 +msgid "Above Z" +msgstr "Zの上" + +#: src/slic3r/GUI/Tab.cpp:1103 +msgid "Acceleration control (advanced)" +msgstr "加速度コントロール (上級者向け)" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:73 +msgid "Activate" +msgstr "アクティベート" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:39 +msgid "Active" +msgstr "アクティブ" + +#: src/slic3r/GUI/Preset.cpp:1003 src/slic3r/GUI/Tab.cpp:237 +msgid "Add a new printer" +msgstr "新しいプリンターを追加" + +#: src/libslic3r/PrintConfig.cpp:2517 +msgid "Add a pad underneath the supported model" +msgstr "サポートされているモデルの下にパッドを追加します" + +#: src/libslic3r/PrintConfig.cpp:1971 +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 "サポートの周りに覆い(1つの円周線)を追加します。 これにより、サポートの造形信頼性が高まりますが、除去するのが難しくなります。" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:186 +msgid "Add color change marker for current layer" +msgstr "現在のレイヤーに色変更マーカーを追加" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1662 +msgid "Add Generic Subobject" +msgstr "一般的なサブオブジェクトの追加" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2584 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2613 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2631 +msgid "Add Height Range" +msgstr "高さ範囲追加" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3463 +msgid "Add instance" +msgstr "インスタンス追加" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:124 +msgid "Add Instance of the selected object" +msgstr "選択したオブジェクトのインスタンスを追加" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:162 +msgid "Add layer range" +msgstr "レイヤー範囲追加" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1950 +msgid "Add Layers" +msgstr "レイヤー追加" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1067 +msgid "Add modifier" +msgstr "個別条件領域の追加" + +#: src/libslic3r/PrintConfig.cpp:447 +#, 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 "傾斜したモデルに隙間ができるのを避けるために、必要に応じて外周を追加します。 Slic3rは、すぐ上のループの70%以上がカバーされるまで、外周を追加します。" + +#: src/slic3r/GUI/Plater.cpp:3516 +msgid "Add one more instance of the selected object" +msgstr "選択したオブジェクトの1つ以上のインスタンスを追加します" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1066 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1082 +msgid "Add part" +msgstr "パーツ追加" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1229 +msgid "Add point" +msgstr "ポイント追加" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1232 +msgid "Add point to selection" +msgstr "選択ポイントを追加" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1118 +msgid "Add settings" +msgstr "設定を追加" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1252 +msgid "Add Settings Bundle for Height range" +msgstr "高さ範囲の設定バンドルを追加" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1191 +msgid "Add Settings Bundle for Object" +msgstr "オブジェクトの設定バンドルを追加" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1190 +msgid "Add Settings Bundle for Sub-object" +msgstr "サブオブジェクトの設定バンドルを追加" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1126 +msgid "Add Settings for Layers" +msgstr "レイヤー設定の追加" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1128 +msgid "Add Settings for Object" +msgstr "オブジェクト設定の追加" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1127 +msgid "Add Settings for Sub-object" +msgstr "サブオブジェクト設定の追加" + +#: src/libslic3r/PrintConfig.cpp:382 +msgid "Add solid infill near sloping surfaces to guarantee the vertical shell thickness (top+bottom solid layers)." +msgstr "傾斜面付近でソリッドインフィル(塗りつぶし)を追加して、垂直方向の厚みを保証します(トップ+ボトムソリッドレイヤー)。" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1069 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1089 +msgid "Add support blocker" +msgstr "サポートブロッカーを追加" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1068 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1086 +msgid "Add support enforcer" +msgstr "強制サポートを追加する" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:531 +msgid "Add support point" +msgstr "サポートポイントの追加" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3392 +msgid "Add..." +msgstr "追加..." + +#: src/slic3r/GUI/wxExtensions.cpp:2571 +msgid "Add/Del color change" +msgstr "カラーチェンジの追加/削除" + +#: src/slic3r/GUI/Tab.cpp:920 +msgid "Additional information:" +msgstr "追加情報:" + +#: src/slic3r/GUI/GUI_ObjectSettings.cpp:58 +msgid "Additional Settings" +msgstr "追加設定" + +#: src/slic3r/GUI/ConfigWizard.cpp:431 +msgid "Additionally a backup snapshot of the whole configuration is created before an update is applied." +msgstr "更新が適用される前に、構成全体の追加バックアップスナップショットが作成されます。" + +#: src/slic3r/GUI/BonjourDialog.cpp:72 +msgid "Address" +msgstr "アドレス" + +#: src/slic3r/GUI/GUI_App.cpp:675 src/slic3r/GUI/GUI_ObjectList.cpp:76 +#: src/slic3r/GUI/GUI_ObjectList.cpp:517 src/slic3r/GUI/Tab.cpp:1026 +#: src/slic3r/GUI/Tab.cpp:1041 src/slic3r/GUI/Tab.cpp:1139 +#: src/slic3r/GUI/Tab.cpp:1142 src/slic3r/GUI/Tab.cpp:1515 +#: src/slic3r/GUI/Tab.cpp:1940 src/slic3r/GUI/Tab.cpp:3435 +#: src/slic3r/GUI/wxExtensions.cpp:2460 src/libslic3r/PrintConfig.cpp:72 +#: src/libslic3r/PrintConfig.cpp:187 src/libslic3r/PrintConfig.cpp:350 +#: src/libslic3r/PrintConfig.cpp:988 src/libslic3r/PrintConfig.cpp:2175 +msgid "Advanced" +msgstr "上級者向け" + +#: src/slic3r/GUI/GUI_App.cpp:675 +msgid "Advanced View Mode" +msgstr "高度なビューモード" + +#: src/slic3r/GUI/FirmwareDialog.cpp:803 +msgid "Advanced: Output log" +msgstr "上級者向け:出力ログ" + +#: src/libslic3r/PrintConfig.cpp:636 +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 "ツールを交換した後、ノズル内に新しく挿入されたフィラメントの正確な位置がわからない場合があり、フィラメントの押圧が安定していない場合があります。 プリントヘッドをインフィル(中塗り)または犠牲オブジェクトでパージする前に、Slic3rは常にこの量の材料をワイプタワーに試し出しすることで、インフィルまたは犠牲オブジェクトを確実に形成します。" + +#: src/slic3r/GUI/Tab.cpp:1967 src/libslic3r/PrintConfig.cpp:1031 +msgid "After layer change G-code" +msgstr "レイヤーチェンジ後のGコード" + +#: src/libslic3r/PrintConfig.cpp:3009 +msgid "Align the model to the given point." +msgstr "モデルを指定されたポイントに合わせます。" + +#: src/libslic3r/PrintConfig.cpp:3008 +msgid "Align XY" +msgstr "XYで整列" + +#: src/libslic3r/PrintConfig.cpp:1561 +msgid "Aligned" +msgstr "整列された" + +#: src/slic3r/GUI/ConfigWizard.cpp:189 src/slic3r/GUI/Tab.cpp:2986 +msgid "All" +msgstr "全て" + +#: src/libslic3r/Print.cpp:1135 +msgid "All objects are outside of the print volume." +msgstr "すべてのオブジェクトはプリントボリュームの外側にあります。" + +#: src/slic3r/GUI/Plater.cpp:3298 +msgid "All objects will be removed, continue ?" +msgstr "全てのオブジェクトが除去されます。続行しますか?" + +#: src/slic3r/GUI/ConfigWizard.cpp:188 +msgid "All standard" +msgstr "すべての標準" + +#: src/libslic3r/Zipper.cpp:65 +msgid "allocation failed" +msgstr "割り当て失敗" + +#: src/slic3r/GUI/Plater.cpp:2939 +msgid "Along X axis" +msgstr "X軸に沿って" + +#: src/slic3r/GUI/Plater.cpp:2941 +msgid "Along Y axis" +msgstr "Y軸に沿って" + +#: src/slic3r/GUI/Plater.cpp:2943 +msgid "Along Z axis" +msgstr "Z軸に沿って" + +#: src/slic3r/GUI/ConfigWizard.cpp:122 +msgid "Alternate nozzles:" +msgstr "代替ノズル:" + +#: src/slic3r/GUI/Plater.cpp:3561 +#, c-format +msgid "AMF file exported to %s" +msgstr "%sにエクスポートされたAMFファイル" + +#: src/slic3r/GUI/GLCanvas3D.cpp:725 +msgid "" +"An object outside the print area was detected\n" +"Resolve the current problem to continue slicing" +msgstr "プリント領域外のオブジェクトが検出されました。スライスを続行するには、この問題を解決してください" + +#: src/slic3r/GUI/GLCanvas3D.cpp:720 +msgid "An object outside the print area was detected" +msgstr "プリント可能範囲外のオブジェクトが検出されました" + +#: src/slic3r/GUI/Tab.cpp:2781 +msgid "and it has the following unsaved changes:" +msgstr "また、次の未保存の変更があります :" + +#: src/slic3r/GUI/Plater.cpp:2461 +msgid "Another export job is currently running." +msgstr "現在、別のエクスポートジョブを実行中です。" + +#: src/slic3r/GUI/Tab.cpp:926 +msgid "Any modifications should be saved as a new preset inherited from this one." +msgstr "修正したら、これから継承された新しいプリセットとして保存する必要があります。" + +#: src/libslic3r/PrintConfig.cpp:88 +msgid "API Key / Password" +msgstr "APIキー/パスワード" + +#: src/slic3r/GUI/GUI_App.cpp:671 +msgid "Application preferences" +msgstr "ソフトウェア設定" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:864 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1240 +msgid "Apply changes" +msgstr "変更を適用" + +#: src/libslic3r/PrintConfig.cpp:542 src/libslic3r/PrintConfig.cpp:1638 +msgid "approximate seconds" +msgstr "秒(約)" + +#: src/libslic3r/PrintConfig.cpp:401 src/libslic3r/PrintConfig.cpp:807 +msgid "Archimedean Chords" +msgstr "アルキメデスコード" + +#: src/libslic3r/Zipper.cpp:91 +msgid "archive is too large" +msgstr "アーカイブが大きすぎます" + +#. TRN remove/delete +#: src/slic3r/GUI/Tab.cpp:2955 +msgid "Are you sure you want to %1% the selected preset?" +msgstr "%1%のプリセットを選択してよろしいですか?" + +#: src/slic3r/GUI/FirmwareDialog.cpp:862 +msgid "" +"Are you sure you want to cancel firmware flashing?\n" +"This could leave your printer in an unusable state!" +msgstr "" +"ファームウェアの書込みをキャンセルしてもよろしいですか?\n" +"これにより、プリンターが使用できない状態になる可能性があります!" + +#: src/libslic3r/PrintConfig.cpp:2258 +msgid "Area fill" +msgstr "領域塗りつぶし" + +#: src/slic3r/GUI/Plater.cpp:609 +msgid "Around object" +msgstr "オブジェクトの周り" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:135 +msgid "Arrange" +msgstr "整列" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3486 +msgid "Arrange selection" +msgstr "選択の整列" + +#: src/libslic3r/PrintConfig.cpp:3054 +msgid "Arrange the supplied models in a plate and merge them in a single model in order to perform actions once." +msgstr "モデルをプリントパッド上に配置し、それらを1つのモデルにマージして、一度で実行できるようにします。" + +#: src/slic3r/GUI/Plater.cpp:2106 +msgid "Arranging" +msgstr "整列" + +#: src/slic3r/GUI/Plater.cpp:2718 +msgid "Arranging canceled." +msgstr "配列中止" + +#: src/slic3r/GUI/Plater.cpp:2144 +msgid "Arranging done." +msgstr "準備完了。" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:172 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:183 +msgid "Arrow Down" +msgstr "下矢印" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:184 +msgid "Arrow Left" +msgstr "左矢印" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:185 +msgid "Arrow Right" +msgstr "右矢印" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:171 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:182 +msgid "Arrow Up" +msgstr "上矢印" + +#: src/slic3r/GUI/GUI_App.cpp:303 +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 "回避策として、--sw_rendererパラメーターを指定してprusa-slicer.exeを実行することにより、PrusaSlicerの3Dグラフィックのレンダリングにソフトウェアレンダラーを使用させます。" + +#: src/slic3r/GUI/GUI.cpp:144 src/slic3r/GUI/GUI_App.cpp:743 +#: src/slic3r/GUI/Tab.cpp:2798 +msgid "Attention!" +msgstr "注意!" + +#: src/libslic3r/PrintConfig.cpp:1785 +msgid "Auto generated supports" +msgstr "自動生成サポート" + +#: src/slic3r/GUI/Preferences.cpp:44 +msgid "Auto-center parts" +msgstr "パーツの自動センタリング" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:902 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1243 +msgid "Auto-generate points" +msgstr "自動ポイント生成" + +#: src/slic3r/GUI/Plater.cpp:979 +#, c-format +msgid "Auto-repaired (%d errors)" +msgstr "自動修復( エラー: %d)" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:230 +#, c-format +msgid "Auto-repaired (%d errors):" +msgstr "自動修正(エラー:%d)" + +#: src/slic3r/GUI/FirmwareDialog.cpp:771 +msgid "Autodetected" +msgstr "自動検出" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1338 +msgid "Autogenerate support points" +msgstr "サポートポイントの自動生成" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1166 +msgid "Autogeneration will erase all manually edited points." +msgstr "自動生成は、マニュアルで編集されたすべてのポイントを消去します。" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1166 +msgid "Are you sure you want to do it?" +msgstr "本当に実行しますか?" + +#: src/slic3r/GUI/Tab.cpp:3421 +msgid "Automatic generation" +msgstr "自動生成" + +#: src/slic3r/GUI/ConfigWizard.cpp:401 +msgid "Automatic updates" +msgstr "自動アップデート" + +#: src/slic3r/GUI/MainFrame.cpp:406 +msgid "Automatically repair an STL file" +msgstr "STLファイルの自動修復" + +#: src/slic3r/GUI/Tab.cpp:1110 +msgid "Autospeed (advanced)" +msgstr "オートスピード(上級者向け)" + +#: src/libslic3r/PrintConfig.cpp:111 +msgid "Avoid crossing perimeters" +msgstr "外周をまたがないようにする" + +#: src/slic3r/GUI/Tab.cpp:3081 +msgid "BACK ARROW" +msgstr "戻る矢印" + +#: src/slic3r/GUI/Tab.cpp:3113 +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 "" +"戻る矢印アイコンは、現在の設定グループが最後に保存されたプリセットとは異なる設定に変更されたことを示します。\n" +"クリックすると、現在の設定グループのすべての設定が最後に保存されたプリセットに戻されます。" + +#: src/slic3r/GUI/Tab.cpp:3127 +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 "" +"戻る矢印アイコンは、値が変更され、最後に保存されたプリセットと等しくないことを示します。\n" +"クリックすると、現在の値が最後に保存されたプリセットにリセットされます。" + +#: src/slic3r/GUI/Preferences.cpp:52 +msgid "Background processing" +msgstr "バックグラウンドで実行中" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:242 +msgid "backwards edges" +msgstr "後方エッジ" + +#: src/slic3r/GUI/MainFrame.cpp:152 +msgid "based on Slic3r" +msgstr "Slic3rをベースに" + +#: src/slic3r/GUI/Tab.cpp:1484 +msgid "Bed" +msgstr "ベッド" + +#: src/libslic3r/PrintConfig.cpp:61 +msgid "Bed custom model" +msgstr "カスタムベッドモデル" + +#: src/libslic3r/PrintConfig.cpp:56 +msgid "Bed custom texture" +msgstr "ベッドのカスタムイメージ" + +#: src/slic3r/GUI/BedShapeDialog.hpp:45 src/slic3r/GUI/ConfigWizard.cpp:524 +msgid "Bed Shape" +msgstr "ベッドの形状" + +#: src/libslic3r/PrintConfig.cpp:50 +msgid "Bed shape" +msgstr "ベッドの形状" + +#: src/slic3r/GUI/ConfigWizard.cpp:524 +msgid "Bed Shape and Size" +msgstr "ベッドの形状とサイズ" + +#: src/libslic3r/PrintConfig.cpp:122 +msgid "Bed temperature" +msgstr "ベッドの温度" + +#: src/libslic3r/PrintConfig.cpp:120 +msgid "Bed temperature for layers after the first one. Set this to zero to disable bed temperature control commands in the output." +msgstr "最初のレイヤー以降のレイヤーのベッド温度。 ベッド温度制御コマンドを無効にするには、これをゼロに設定します。" + +#: src/slic3r/GUI/ConfigWizard.cpp:626 +msgid "Bed Temperature:" +msgstr "ベッド温度:" + +#: src/slic3r/GUI/Tab.cpp:1961 src/libslic3r/PrintConfig.cpp:128 +msgid "Before layer change G-code" +msgstr "レイヤー変更前のGコード" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:22 +msgid "Before roll back" +msgstr "元に戻す前に" + +#: src/slic3r/GUI/Plater.cpp:608 +msgid "Below object" +msgstr "下のオブジェクト" + +#: src/libslic3r/PrintConfig.cpp:1508 +msgid "Below Z" +msgstr "Zの下" + +#: src/libslic3r/PrintConfig.cpp:139 +msgid "Between objects G-code" +msgstr "オブジェクト間のGコード" + +#: src/slic3r/GUI/Tab.cpp:1979 +msgid "Between objects G-code (for sequential printing)" +msgstr "オブジェクト間のGコード(シーケンシャルプリントの場合)" + +#. TRN To be shown in the main menu View->Bottom +#: src/slic3r/GUI/MainFrame.cpp:524 +msgid "Bottom" +msgstr "ボトム" + +#: src/libslic3r/PrintConfig.cpp:409 +msgid "Bottom fill pattern" +msgstr "ボトム塗りつぶしパターン" + +#: src/libslic3r/PrintConfig.cpp:152 +msgid "Bottom solid layers" +msgstr "底部ソリッドレイヤー" + +#: src/slic3r/GUI/MainFrame.cpp:524 +msgid "Bottom View" +msgstr "下面表示" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1055 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1087 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1090 +msgid "Box" +msgstr "ボックス" + +#: src/libslic3r/PrintConfig.cpp:157 +msgid "Bridge" +msgstr "ブリッジ" + +#: src/libslic3r/PrintConfig.cpp:186 +msgid "Bridge flow ratio" +msgstr "ブリッジ部吐出率" + +#: src/slic3r/GUI/GUI_Preview.cpp:233 src/libslic3r/GCode/PreviewData.cpp:169 +msgid "Bridge infill" +msgstr "ブリッジインフィル" + +#: src/libslic3r/PrintConfig.cpp:198 +msgid "Bridges" +msgstr "ブリッジ" + +#: src/libslic3r/PrintConfig.cpp:177 +msgid "Bridges fan speed" +msgstr "ブリッジファン速度" + +#: src/libslic3r/PrintConfig.cpp:166 +msgid "Bridging angle" +msgstr "ブリッジ形成角" + +#: src/libslic3r/PrintConfig.cpp:168 +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 "ブリッジ作成時の角度設定を上書きします。 値を0に設定すると角度が自動的に計算されます。 角度を入力すると、入力した角度がすべてのブリッジに適用されます。 強制的に角度を0に指定したい場合は、180°を入力してください。" + +#: src/slic3r/GUI/PresetHints.cpp:216 +msgid "Bridging volumetric" +msgstr "ブリッジの体積値" + +#: src/slic3r/GUI/Plater.cpp:446 src/slic3r/GUI/Tab.cpp:1056 +msgid "Brim" +msgstr "ブリム" + +#: src/libslic3r/PrintConfig.cpp:208 +msgid "Brim width" +msgstr "ブリム幅" + +#: src/slic3r/GUI/Tab.cpp:1681 +msgid "Browse" +msgstr "ブラウズ" + +#: src/libslic3r/Zipper.cpp:85 +msgid "buffer too small" +msgstr "バッファーが少なすぎます" + +#: src/slic3r/GUI/ButtonsDescription.cpp:16 +msgid "Buttons And Text Colors Description" +msgstr "ボタンとテキストカラーの種類" + +#: src/slic3r/GUI/PresetHints.cpp:220 +msgid "by the print profile maximum" +msgstr "プリントプロファイルの最大値" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:123 +msgid "Camera view" +msgstr "カメラビュー" + +#: src/slic3r/GUI/ConfigWizard.cpp:1095 src/slic3r/GUI/FirmwareDialog.cpp:147 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:37 +#: src/slic3r/GUI/ProgressStatusBar.cpp:28 +msgid "Cancel" +msgstr "中止" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:156 +msgid "Cancel selected" +msgstr "選択取り消し" + +#: src/slic3r/GUI/Plater.cpp:2727 src/slic3r/GUI/PrintHostDialogs.cpp:232 +msgid "Cancelled" +msgstr "中止" + +#: src/slic3r/GUI/Plater.cpp:2444 src/slic3r/GUI/PrintHostDialogs.cpp:231 +msgid "Cancelling" +msgstr "中止中" + +#: src/slic3r/GUI/FirmwareDialog.cpp:866 +msgid "Cancelling..." +msgstr "取り消し中..." + +#: src/slic3r/GUI/Tab.cpp:2905 +msgid "Cannot overwrite a system profile." +msgstr "システムプロファイルを上書きできません。" + +#: src/slic3r/GUI/Tab.cpp:2909 +msgid "Cannot overwrite an external profile." +msgstr "外部プロファイルを上書きできません。" + +#: src/libslic3r/SLAPrint.cpp:612 +msgid "Cannot proceed without support points! Add support points or disable support generation." +msgstr "サポートポイントなしでは続行できません! サポートポイントを追加するか、サポート生成を無効にします。" + +#: src/slic3r/GUI/Tab.cpp:1840 +msgid "Capabilities" +msgstr "オプション" + +#: src/slic3r/GUI/GUI_App.cpp:662 +msgid "Capture a configuration snapshot" +msgstr "構成スナップショットをキャプチャーする" + +#: src/libslic3r/PrintConfig.cpp:3035 +msgid "Center" +msgstr "中心" + +#: src/libslic3r/PrintConfig.cpp:3036 +msgid "Center the print around the given center." +msgstr "指定されたポイントを中心にプリントを配置します。" + +#: src/slic3r/GUI/Tab.cpp:1744 +msgid "Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*" +msgstr "証明書ファイル (*.crt, *.pem)|*.crt;*.pem|全て|*.*" + +#: src/slic3r/GUI/GUI_App.cpp:683 +msgid "Change Application &Language" +msgstr "アプリケーション言語の変更" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:154 +msgid "Change camera type (perspective, orthographic)" +msgstr "カメラタイプの変更(パース/アイソメ)" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1226 +msgid "Change extruder" +msgstr "エクストルーダー切替え" + +#: src/slic3r/GUI/GUI_ObjectSettings.cpp:144 +#, c-format +msgid "Change Option %s" +msgstr "オプション%s変更" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3134 +msgid "Change Part Type" +msgstr "パーツタイプの変更" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:925 +msgid "Change point head diameter" +msgstr "ポイントヘッド径の変更" + +#: src/slic3r/GUI/Plater.cpp:3520 +msgid "Change the number of instances of the selected object" +msgstr "選択したオブジェクトのインスタンス数を変更します" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1185 +msgid "Change type" +msgstr "タイプを変更" + +#: src/slic3r/GUI/UpdateDialogs.cpp:56 +msgid "Changelog && Download" +msgstr "変更ログ && ダウンロード" + +#: src/slic3r/GUI/GUI_App.cpp:378 +msgid "Changing of an application language" +msgstr "アプリケーション言語の変更" + +#: src/slic3r/GUI/ConfigWizard.cpp:409 src/slic3r/GUI/Preferences.cpp:61 +msgid "Check for application updates" +msgstr "最新バージョンをチェック" + +#: src/slic3r/GUI/BedShapeDialog.cpp:509 +msgid "Choose a file to import bed texture from (PNG/SVG):" +msgstr "ベッドのイメージファイルを選択(PNG/SVG):" + +#: src/slic3r/GUI/MainFrame.cpp:621 +msgid "Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):" +msgstr "スライスするファイルを選択(STL/OBJ/AMF/3MF/PRUSA):" + +#: src/slic3r/GUI/BedShapeDialog.cpp:532 +msgid "Choose an STL file to import bed model from:" +msgstr "ベッドモデルをインポートするSTLファイルを選択します:" + +#: src/slic3r/GUI/BedShapeDialog.cpp:464 +msgid "Choose an STL file to import bed shape from:" +msgstr "ベッド形状をインポートするためのSTLファイルを選択:" + +#: src/slic3r/GUI/GUI_App.cpp:510 +msgid "Choose one file (3MF/AMF):" +msgstr "1つのファイルを選択します(3MF/AMF):" + +#: src/slic3r/GUI/GUI_App.cpp:501 +msgid "Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):" +msgstr "1つ以上のファイルの選択(STL/OBJ/AMF/3MF/PRUSA):" + +#: src/slic3r/GUI/ConfigWizard.cpp:490 +msgid "Choose the type of firmware used by your printer." +msgstr "プリンタのファームウェアタイプを選択します。" + +#: src/slic3r/GUI/BedShapeDialog.cpp:84 +msgid "Circular" +msgstr "円形" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3701 src/slic3r/GUI/GLCanvas3D.cpp:3734 +msgid "Click right mouse button to open History" +msgstr "マウスの右クリックでヒストリーが表示されます" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:383 +msgid "Click the icon to change the object printable property" +msgstr "アイコンをクリックして、オブジェクトのプリント可能なプロパティを変更します" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:377 +msgid "Click the icon to change the object settings" +msgstr "アイコンをクリックして、オブジェクトの設定を変更します" + +#: src/slic3r/GUI/Plater.cpp:292 +msgid "Click to edit preset" +msgstr "クリックしてプリセットを編集" + +#: src/libslic3r/PrintConfig.cpp:216 +msgid "Clip multi-part objects" +msgstr "マルチパートオブジェクトをクリップする" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:928 +msgid "Clipping of view" +msgstr "ビューのクリッピング" + +#: src/slic3r/GUI/FirmwareDialog.cpp:814 +#: src/slic3r/GUI/PrintHostDialogs.cpp:160 +msgid "Close" +msgstr "閉じる" + +#: src/libslic3r/PrintConfig.cpp:550 +msgid "Color" +msgstr "色" + +#: src/slic3r/GUI/GUI_Preview.cpp:218 src/slic3r/GUI/GUI_Preview.cpp:535 +#: src/libslic3r/GCode/PreviewData.cpp:406 +msgid "Color Print" +msgstr "カラープリント" + +#: src/libslic3r/PrintConfig.cpp:224 +msgid "Colorprint height" +msgstr "カラープリント高" + +#: src/libslic3r/PrintConfig.cpp:942 +msgid "Combine infill every" +msgstr "インフィルをこれ毎に結合する" + +#: src/libslic3r/PrintConfig.cpp:947 +msgid "Combine infill every n layers" +msgstr "インフィルをnレイヤー組合わせる" + +#: src/slic3r/GUI/UpdateDialogs.cpp:116 +msgid "Comment:" +msgstr "コメント:" + +#: src/slic3r/GUI/Tab.cpp:56 src/libslic3r/PrintConfig.cpp:244 +msgid "Compatible print profiles" +msgstr "互換性のあるプリントプロファイル" + +#: src/libslic3r/PrintConfig.cpp:250 +msgid "Compatible print profiles condition" +msgstr "互換性のあるプリントプロファイル条件" + +#: src/slic3r/GUI/Tab.cpp:50 src/libslic3r/PrintConfig.cpp:229 +msgid "Compatible printers" +msgstr "互換プリンター" + +#: src/libslic3r/PrintConfig.cpp:235 +msgid "Compatible printers condition" +msgstr "互換性のあるプリンターのコンディション" + +#: src/libslic3r/PrintConfig.cpp:268 +msgid "Complete individual objects" +msgstr "個々のオブジェクトを完成させる" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:233 +msgid "Completed" +msgstr "完了" + +#: src/libslic3r/Zipper.cpp:57 +msgid "compression failed" +msgstr "圧縮失敗" + +#: src/libslic3r/PrintConfig.cpp:399 src/libslic3r/PrintConfig.cpp:802 +msgid "Concentric" +msgstr "同心円" + +#: src/slic3r/GUI/ConfigWizard.cpp:1185 +msgid "Configuration &Assistant" +msgstr "セットアップガイド" + +#: src/slic3r/GUI/ConfigWizard.cpp:1182 +msgid "Configuration &Wizard" +msgstr "設定ウィザード" + +#: src/slic3r/GUI/ConfigWizard.cpp:1184 +msgid "Configuration Assistant" +msgstr "設定アシスタント" + +#: src/libslic3r/PrintConfig.cpp:1251 +msgid "Configuration notes" +msgstr "設定上の注意" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:99 +msgid "Configuration Snapshots" +msgstr "設定のスナップショット" + +#: src/slic3r/GUI/UpdateDialogs.cpp:73 src/slic3r/GUI/UpdateDialogs.cpp:168 +msgid "Configuration update" +msgstr "構成の更新" + +#: src/slic3r/GUI/UpdateDialogs.cpp:73 +msgid "Configuration update is available" +msgstr "環境をアップデートできます" + +#: src/slic3r/GUI/ConfigWizard.cpp:1181 +msgid "Configuration Wizard" +msgstr "設定ウィザード" + +#: src/slic3r/GUI/FirmwareDialog.cpp:863 +msgid "Confirmation" +msgstr "確認" + +#: src/slic3r/GUI/Tab.cpp:1904 +msgid "Connection failed." +msgstr "接続失敗" + +#: src/slic3r/GUI/Tab.cpp:3416 +msgid "Connection of the support sticks and junctions" +msgstr "サポートスティックとジャンクションの接続" + +#: src/slic3r/Utils/Duet.cpp:51 +msgid "Connection to Duet works correctly." +msgstr "Duetへの接続は機能しています" + +#: src/slic3r/Utils/OctoPrint.cpp:84 +msgid "Connection to OctoPrint works correctly." +msgstr "OctPrintとの接続成功。" + +#: src/slic3r/GUI/Tab.cpp:1901 +msgid "Connection to printer works correctly." +msgstr "プリンターとの接続成功" + +#: src/slic3r/Utils/OctoPrint.cpp:195 +msgid "Connection to Prusa SL1 works correctly." +msgstr "Prusa SL1と正しく接続しました" + +#: src/libslic3r/PrintConfig.cpp:1823 +msgid "Contact Z distance" +msgstr "Z軸上のサポートーオブジェクト間距離" + +#: src/slic3r/GUI/AboutDialog.cpp:96 +msgid "Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and numerous others." +msgstr "Henrik Brix Andersen、 Nicolas Dandrimont、 Mark Hindess、 Petr Ledvina、 Joseph Lenox、 Y. Sapir、 Mike Sheldrake、 Vojtech Bubnik 、その他多くの方々の貢献。" + +#: src/libslic3r/PrintConfig.cpp:2433 +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 "2つの隣接する柱のブリッジタイプを設定します。 2本の柱の距離に応じて、最初の2つを自動的に切り替えるジグザグ、クロス(ダブルジグザグ)、またはダイナミックにすることができます。" + +#: src/slic3r/GUI/Tab.cpp:1489 +msgid "Cooling" +msgstr "クーリング" + +#: src/libslic3r/PrintConfig.cpp:629 +msgid "Cooling moves are gradually accelerating beginning at this speed." +msgstr "クーリング動作はこのスピードから徐々に加速します。" + +#: src/libslic3r/PrintConfig.cpp:648 +msgid "Cooling moves are gradually accelerating towards this speed." +msgstr "冷却動作は、この速度に向かって徐々に加速しています。" + +#: src/slic3r/GUI/Tab.cpp:1510 +msgid "Cooling thresholds" +msgstr "クーリングしきい値" + +#: src/libslic3r/PrintConfig.cpp:291 +msgid "Cooling tube length" +msgstr "冷却チューブの長さ" + +#: src/libslic3r/PrintConfig.cpp:283 +msgid "Cooling tube position" +msgstr "冷却チューブ位置" + +#: src/slic3r/GUI/Tab.cpp:2878 +msgid "Copy" +msgstr "コピー" + +#: src/slic3r/GUI/MainFrame.cpp:454 +msgid "Copy selection to clipboard" +msgstr "選択をクリップボードにコピー" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:139 +msgid "Copy to clipboard" +msgstr "クリップボードにコピー" + +#: src/slic3r/GUI/SysInfoDialog.cpp:120 +msgid "Copy to Clipboard" +msgstr "クリップボードにコピー" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:84 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:400 +msgid "Copying of the temporary G-code to the output G-code failed" +msgstr "一時Gコードから出力Gコードへのコピーに失敗しました" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:92 +msgid "Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?" +msgstr "一時Gコードの出力Gコードへのコピーに失敗しました。 SDカードが書き込みロックされていませんか?" + +#: src/slic3r/GUI/AboutDialog.cpp:92 +msgid "Copyright" +msgstr "コピーライト" + +#: src/libslic3r/PrintConfig.cpp:2324 src/libslic3r/PrintConfig.cpp:2325 +msgid "Correction for expansion" +msgstr "膨張補正" + +#: src/slic3r/GUI/Tab.cpp:2059 src/slic3r/GUI/Tab.cpp:3310 +msgid "Corrections" +msgstr "補正" + +#: src/slic3r/GUI/Plater.cpp:216 src/slic3r/GUI/Plater.cpp:1056 +#: src/libslic3r/PrintConfig.cpp:717 +msgid "Cost" +msgstr "費用" + +#: src/slic3r/GUI/Plater.cpp:2140 +msgid "Could not arrange model objects! Some geometries may be invalid." +msgstr "モデルオブジェクトを配置できませんでした! 一部のジオメトリが無効のようです。" + +#: src/slic3r/Utils/Duet.cpp:56 +msgid "Could not connect to Duet" +msgstr "Duetに接続できませんでした" + +#: src/slic3r/Utils/OctoPrint.cpp:90 +msgid "Could not connect to OctoPrint" +msgstr "OctoPrintに接続できません" + +#: src/slic3r/Utils/OctoPrint.cpp:200 +msgid "Could not connect to Prusa SLA" +msgstr "Prusa SLAに接続できませんでした" + +#: src/slic3r/GUI/Tab.cpp:1710 +msgid "Could not get a valid Printer Host reference" +msgstr "有効なプリントサーバー参照を取得できません" + +#: src/slic3r/Utils/Duet.cpp:151 +msgid "Could not get resources to create a new connection" +msgstr "新しい接続を作成するためのリソースを取得できません" + +#: src/libslic3r/PrintConfig.cpp:1872 +msgid "Cover the top contact layer of the supports with loops. Disabled by default." +msgstr "ループでサポート上部の接触層を覆います。 デフォルトでは無効になっています。" + +#: src/libslic3r/PrintConfig.cpp:73 +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 "ポリゴンメッシュのスライス中に、ギャップを閉じる半径の2倍より小さいクラックが埋められます。 ギャップを閉じることで、最終的なプリント解像度が低下する可能性があるため、この値は適度に小さくすることをお勧めします。" + +#: src/libslic3r/Zipper.cpp:61 +msgid "CRC-32 check failed" +msgstr "CRC-32チェックに失敗しました" + +#: src/libslic3r/PrintConfig.cpp:2734 +msgid "Create pad around object and ignore the support elevation" +msgstr "オブジェクトの周りにパッドを作成し、サポートでオブジェクトを上げることを無視します" + +#: src/libslic3r/PrintConfig.cpp:2460 +msgid "Critical angle" +msgstr "限界角" + +#: src/libslic3r/PrintConfig.cpp:2417 +msgid "Cross" +msgstr "クロス" + +#: src/libslic3r/PrintConfig.cpp:800 +msgid "Cubic" +msgstr "立方" + +#: src/slic3r/GUI/wxExtensions.cpp:2413 +#, c-format +msgid "Current mode is %s" +msgstr "現在のモードは%sです。" + +#: src/slic3r/GUI/Tab.cpp:925 +msgid "Current preset is inherited from the default preset." +msgstr "現在の設定はデフォルト設定から継承されます。" + +#: src/slic3r/GUI/Tab.cpp:928 +#, c-format +msgid "" +"Current preset is inherited from:\n" +"\t%s" +msgstr "現在のプリセット継承元:%s" + +#: src/slic3r/GUI/UpdateDialogs.cpp:45 +msgid "Current version:" +msgstr "現在のバージョン" + +#: src/slic3r/GUI/BedShapeDialog.cpp:93 src/slic3r/GUI/GUI_Preview.cpp:239 +#: src/libslic3r/GCode/PreviewData.cpp:175 +msgid "Custom" +msgstr "カスタム" + +#: src/libslic3r/PrintConfig.cpp:96 +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 "HTTPS OctoPrint接続用にカスタムCA証明書ファイルをcrt/pem形式で指定できます。 空白のままにすると、デフォルトのOS CA証明書リポジトリが使用されます。" + +#: src/slic3r/GUI/Tab.cpp:1563 src/slic3r/GUI/Tab.cpp:1948 +msgid "Custom G-code" +msgstr "カスタムGコード" + +#: src/slic3r/GUI/ConfigWizard.cpp:373 +msgid "Custom Printer" +msgstr "カスタムプリンター" + +#: src/slic3r/GUI/ConfigWizard.cpp:373 +msgid "Custom Printer Setup" +msgstr "カスタムプリンター設定" + +#: src/slic3r/GUI/ConfigWizard.cpp:377 +msgid "Custom profile name:" +msgstr "カスタムプロファイル名:" + +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:188 src/libslic3r/PrintConfig.cpp:3013 +msgid "Cut" +msgstr "カット" + +#: src/slic3r/GUI/Plater.cpp:4193 +msgid "Cut by Plane" +msgstr "面でカット" + +#: src/libslic3r/PrintConfig.cpp:3014 +msgid "Cut model at the given Z." +msgstr "指定されたZでモデルをカットします。" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1055 +msgid "Cylinder" +msgstr "シリンダー" + +#: src/slic3r/GUI/MainFrame.cpp:491 +msgid "D&eselect all" +msgstr "全てを非選択状態に" + +#: src/libslic3r/PrintConfig.cpp:3115 +msgid "Data directory" +msgstr "データディレクトリー" + +#: src/libslic3r/Zipper.cpp:55 +msgid "decompression failed or archive is corrupted" +msgstr "解凍に失敗したか、アーカイブが破損しています" + +#: src/slic3r/GUI/Plater.cpp:4127 +msgid "Decrease Instances" +msgstr "インスタンスを減らす" + +#: src/slic3r/GUI/GUI_App.cpp:594 src/slic3r/GUI/GUI_ObjectList.cpp:1245 +#: src/libslic3r/PrintConfig.cpp:299 +msgid "Default" +msgstr "デフォルト" + +#: xs/src/slic3r/GUI/Field.cpp:98 +msgid "default" +msgstr "デフォルト" + +#: src/libslic3r/PrintConfig.cpp:730 +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 "インフィル(塗りつぶし)方向のデフォルトの角度。レイヤー毎に90°切替るクロスハッチングされます。 ブリッジはSlic3rが最適な方向を自動設定するため、この設定はブリッジ部分には影響しません。" + +#: src/libslic3r/PrintConfig.cpp:522 +msgid "Default extrusion width" +msgstr "デフォルト射出幅" + +#: src/slic3r/GUI/Tab.cpp:937 +msgid "default filament profile" +msgstr "デフォルトフィラメントプロファイル" + +#: src/libslic3r/PrintConfig.cpp:309 +msgid "Default filament profile" +msgstr "デフォルトフィラメントプロファイル" + +#: src/libslic3r/PrintConfig.cpp:310 +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/slic3r/GUI/Tab.cpp:2757 +#, c-format +msgid "Default preset (%s)" +msgstr "デフォルトプリセット(%s)" + +#: src/libslic3r/GCode/PreviewData.cpp:491 +msgid "Default print color" +msgstr "デフォルトプリントカラー" + +#: src/slic3r/GUI/Tab.cpp:934 +msgid "default print profile" +msgstr "デフォルトプリントプロファイル" + +#: src/libslic3r/PrintConfig.cpp:316 +msgid "Default print profile" +msgstr "デフォルトプリントプロファイル" + +#: src/libslic3r/PrintConfig.cpp:317 src/libslic3r/PrintConfig.cpp:2341 +#: src/libslic3r/PrintConfig.cpp:2352 +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/slic3r/GUI/Tab.cpp:951 +msgid "default SLA material profile" +msgstr "デフォルトのSLA材料プロファイル" + +#: src/libslic3r/PrintConfig.cpp:2340 src/libslic3r/PrintConfig.cpp:2351 +msgid "Default SLA material profile" +msgstr "デフォルトのSLA材料プロファイル" + +#: src/slic3r/GUI/Tab.cpp:955 +msgid "default SLA print profile" +msgstr "デフォルトのSLAプリントプロファイル" + +#: src/slic3r/GUI/Field.cpp:105 +msgid "default value" +msgstr "デフォルト値" + +#: src/slic3r/GUI/ConfigWizard.cpp:375 +msgid "Define a custom printer profile" +msgstr "カスタムプリンタープロファイルを定義する" + +#: src/libslic3r/PrintConfig.cpp:2558 +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 "キャビティの深さを定義します。 キャビティをオフにするにはゼロに設定します。 この機能を有効にするときは注意してください。一部の樹脂はキャビティ内で極端な吸引効果がでてしまい、バットフィルムからプリント物の剥離が困難になる場合があります。" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:237 +msgid "degenerate facets" +msgstr "縮退したファセット" + +#: src/libslic3r/PrintConfig.cpp:608 +msgid "Delay after unloading" +msgstr "アンロードした後の待ち時間" + +#: src/slic3r/GUI/Tab.cpp:2935 +msgid "delete" +msgstr "削除" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1257 src/slic3r/GUI/Plater.cpp:2891 +#: src/slic3r/GUI/Plater.cpp:2909 src/slic3r/GUI/Tab.cpp:2937 +msgid "Delete" +msgstr "削除" + +#: src/slic3r/GUI/MainFrame.cpp:449 +msgid "Delete &all" +msgstr "全て削除" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:138 +msgid "Delete All" +msgstr "全て削除" + +#: src/slic3r/GUI/Plater.cpp:3298 +msgid "Delete all" +msgstr "全て削除" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1806 +msgid "Delete All Instances from Object" +msgstr "オブジェクトのすべてのインスタンスを削除" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:187 +msgid "Delete color change marker for current layer" +msgstr "現在のレイヤーの色変更マーカーを削除" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1898 +msgid "Delete Height Range" +msgstr "高さ範囲を削除" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1876 +msgid "Delete Instance" +msgstr "インスタンス削除" + +#: src/slic3r/GUI/Plater.cpp:2592 +msgid "Delete Object" +msgstr "オブジェクト削除" + +#: src/slic3r/GUI/GUI_ObjectSettings.cpp:100 +#, c-format +msgid "Delete Option %s" +msgstr "オプション%s削除" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:137 +msgid "Delete selected" +msgstr "選択を削除します" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2415 +msgid "Delete Selected" +msgstr "選択を削除" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2303 +msgid "Delete Selected Item" +msgstr "選択したアイテムを削除" + +#: src/slic3r/GUI/Plater.cpp:4083 +msgid "Delete Selected Objects" +msgstr "選択オブジェクトの削除" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1782 +msgid "Delete Settings" +msgstr "設定削除" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1857 +msgid "Delete Subobject" +msgstr "サブオブジェクト削除" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:720 +msgid "Delete support point" +msgstr "サポートポイントの削除" + +#: src/slic3r/GUI/Tab.cpp:131 +msgid "Delete this preset" +msgstr "このプリセットを削除" + +#: src/slic3r/GUI/MainFrame.cpp:449 +msgid "Deletes all objects" +msgstr "全てのオブジェクトを削除" + +#: src/slic3r/GUI/MainFrame.cpp:447 +msgid "Deletes the current selection" +msgstr "現在の選択を削除します" + +#: src/libslic3r/PrintConfig.cpp:685 +msgid "Density" +msgstr "密度" + +#: src/libslic3r/PrintConfig.cpp:744 +msgid "Density of internal infill, expressed in the range 0% - 100%." +msgstr "0%-100%の範囲で表される内部インフィルの密度。" + +#: src/slic3r/GUI/Tab.cpp:1200 src/slic3r/GUI/Tab.cpp:1584 +#: src/slic3r/GUI/Tab.cpp:1992 src/slic3r/GUI/Tab.cpp:2086 +#: src/slic3r/GUI/Tab.cpp:3336 src/slic3r/GUI/Tab.cpp:3445 +msgid "Dependencies" +msgstr "依存関係" + +#: src/libslic3r/PrintConfig.cpp:1542 src/libslic3r/PrintConfig.cpp:1543 +msgid "Deretraction Speed" +msgstr "待避からの復帰速度" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1235 +msgid "Deselect by rectangle" +msgstr "方形で選択解除" + +#: src/slic3r/GUI/MainFrame.cpp:492 +msgid "Deselects all objects" +msgstr "全てのオブジェクトの選択解除" + +#: src/libslic3r/PrintConfig.cpp:1304 +msgid "Detect bridging perimeters" +msgstr "ブリッジ外周の検出" + +#: src/libslic3r/PrintConfig.cpp:1988 +msgid "Detect single-width walls (parts where two extrusions don't fit and we need to collapse them into a single trace)." +msgstr "単一の線の太さ(2本の線が入れられず、1本の線で射出する必要がある部分)の壁を検出します。" + +#: src/libslic3r/PrintConfig.cpp:1986 +msgid "Detect thin walls" +msgstr "薄壁を検知" + +#: src/libslic3r/PrintConfig.cpp:3083 +msgid "Detect unconnected parts in the given model(s) and split them into separate objects." +msgstr "指定されたモデルで接続されていないパーツを検出し、それらを個別のオブジェクトに分割します。" + +#: src/slic3r/GUI/Plater.cpp:1713 +msgid "Detected advanced data" +msgstr "検出された高度なデータ" + +#: src/slic3r/GUI/BedShapeDialog.cpp:88 src/libslic3r/PrintConfig.cpp:677 +msgid "Diameter" +msgstr "直径" + +#: src/libslic3r/PrintConfig.cpp:2443 +msgid "Diameter in mm of the pillar base" +msgstr "支柱ベースの直径(mm)" + +#: src/libslic3r/PrintConfig.cpp:2399 +msgid "Diameter in mm of the support pillars" +msgstr "サポート支柱の直径(mm)" + +#: src/libslic3r/PrintConfig.cpp:2371 +msgid "Diameter of the pointing side of the head" +msgstr "サポート先端の直径" + +#: src/slic3r/GUI/BedShapeDialog.cpp:89 +msgid "Diameter of the print bed. It is assumed that origin (0,0) is located in the center." +msgstr "プリントベッドの直径。 原点(0,0)は中央にあるとみなされます。" + +#: src/libslic3r/PrintConfig.cpp:1569 +msgid "Direction" +msgstr "方向" + +#: src/libslic3r/PrintConfig.cpp:323 +msgid "Disable fan for the first" +msgstr "ファンをオフにする最初のレイヤー" + +#: src/libslic3r/PrintConfig.cpp:1280 +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/slic3r/GUI/wxExtensions.cpp:2572 +msgid "Discard all color changes" +msgstr "すべての色の変更を破棄" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:869 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1241 +msgid "Discard changes" +msgstr "変更取りやめ" + +#: src/slic3r/GUI/Tab.cpp:2784 +msgid "Discard changes and continue anyway?" +msgstr "変更を破棄して続行しますか?" + +#: src/slic3r/GUI/Gizmos/GLGizmoMove.cpp:177 +msgid "Displacement (mm)" +msgstr "変位(mm)" + +#: src/slic3r/GUI/Tab.cpp:2041 +msgid "Display" +msgstr "ディスプレイ" + +#: src/libslic3r/PrintConfig.cpp:2208 +msgid "Display height" +msgstr "ディスプレイの高さ" + +#: src/libslic3r/PrintConfig.cpp:2319 +msgid "Display horizontal mirroring" +msgstr "水平ディスプレイミラーリング" + +#: src/libslic3r/PrintConfig.cpp:2227 +msgid "Display orientation" +msgstr "ディスプレイの向き" + +#: src/slic3r/GUI/MainFrame.cpp:510 +msgid "Display the Print Host Upload Queue window" +msgstr "プリントサーバーのアップロードキュー画面を表示する" + +#: src/libslic3r/PrintConfig.cpp:2326 +msgid "Display vertical mirroring" +msgstr "垂直ミラーリングを表示する" + +#: src/libslic3r/PrintConfig.cpp:2202 +msgid "Display width" +msgstr "画面の幅" + +#: src/libslic3r/PrintConfig.cpp:341 +msgid "Distance between copies" +msgstr "コピー間の距離" + +#: src/libslic3r/PrintConfig.cpp:1610 +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:2752 +msgid "Distance between two connector sticks which connect the object and the generated pad." +msgstr "オブジェクトと生成されたパッドを接続する2つのコネクタスティック間の距離。" + +#: src/libslic3r/PrintConfig.cpp:1609 +msgid "Distance from object" +msgstr "オブジェクトからの距離" + +#: src/slic3r/GUI/BedShapeDialog.cpp:80 +msgid "Distance of the 0,0 G-code coordinate from the front left corner of the rectangle." +msgstr "四角形の左前隅からの0,0 Gコード座標の距離。" + +#: src/libslic3r/PrintConfig.cpp:285 +msgid "Distance of the center-point of the cooling tube from the extruder tip." +msgstr "ノズル先端から冷却チューブの中心までの距離。" + +#: src/libslic3r/PrintConfig.cpp:1338 +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:342 +msgid "Distance used for the auto-arrange feature of the plater." +msgstr "プレートの自動配置機能で使用される距離。" + +#: src/libslic3r/PrintConfig.cpp:3097 +msgid "Do not fail if a file supplied to --load does not exist." +msgstr "存在しない読込みが行われても提供されたファイルは失敗させない。" + +#: src/libslic3r/PrintConfig.cpp:3041 +msgid "Do not rearrange the given models before merging and keep their original XY coordinates." +msgstr "元のXY座標を残して、マージする前にモデルを再配置しないでください。" + +#: src/slic3r/GUI/Field.cpp:206 +#, 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 "%s%sではなく%s%%ですか?この値を%s %%に変更するなら「はい」を、%s%sでよろしいなら「いいえ」を選択してください。" + +#: src/slic3r/GUI/GUI_App.cpp:754 +msgid "Do you want to proceed?" +msgstr "続行しますか?" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1024 +msgid "Do you want to save your manually edited support points?" +msgstr "マニュアル編集したサポートポイントを保存しますか?" + +#: src/libslic3r/PrintConfig.cpp:3040 +msgid "Don't arrange" +msgstr "整列させない" + +#: src/slic3r/GUI/UpdateDialogs.cpp:55 +msgid "Don't notify about new releases any more" +msgstr "新しいリリースについて通知しない" + +#: src/libslic3r/PrintConfig.cpp:333 +msgid "Don't support bridges" +msgstr "ブリッジ部のサポート禁止" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:20 +msgid "Downgrade" +msgstr "ダウングレード" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1233 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1236 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1237 +msgid "Drag" +msgstr "ドラッグ" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:340 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:355 +msgid "Drop to bed" +msgstr "ベッドに落とす" + +#: src/libslic3r/PrintConfig.cpp:3044 +msgid "Duplicate" +msgstr "複製" + +#: src/libslic3r/PrintConfig.cpp:3049 +msgid "Duplicate by grid" +msgstr "グリッドで複製" + +#: src/libslic3r/PrintConfig.cpp:2418 +msgid "Dynamic" +msgstr "動的" + +#: src/slic3r/GUI/MainFrame.cpp:708 +msgid "E&xport" +msgstr "エクスポート" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:238 +msgid "edges fixed" +msgstr "エッジ修正" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2690 +msgid "Edit Height Range" +msgstr "高さ範囲の編集" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:373 +msgid "Editing" +msgstr "編集中" + +#: src/libslic3r/PrintConfig.cpp:349 +msgid "Elephant foot compensation" +msgstr "最初の層の広がり補正" + +#: src/libslic3r/SLAPrint.cpp:681 +msgid "Elevation is too low for object. Use the \"Pad around obect\" feature to print the object without elevation." +msgstr "オブジェクトに対して上昇高さが低すぎます。 「オブジェクトの周囲のパッド」機能を使用して、オブジェクトを上昇させずにプリントします。" + +#: src/libslic3r/SLAPrint.cpp:678 +msgid "Elevation is too low for object. Use the \"Pad around object\" feature to print the object without elevation." +msgstr "オブジェクトに対して持上げ高さが低すぎます。 「オブジェクトの周囲のパッド」機能を使用して、オブジェクトを持上げ高さなしでプリントします。" + +#: src/libslic3r/PrintConfig.cpp:1044 +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 "正確な残り時間を表示させるために、Gコードに1分間隔でM73 P [プリント率(%)] R [残り時間(分)]を埋め込みます。 現在のところ、M73を認識するのはPrusa i3 MK3ファームウェアのみです。 また、Prusa i3 MK3ファームウェアは、サイレントモードのM73 Qxx Sxxもサポートしています。" + +#: src/slic3r/GUI/Tab.cpp:1490 src/libslic3r/PrintConfig.cpp:1286 +#: src/libslic3r/PrintConfig.cpp:2099 +msgid "Enable" +msgstr "有効" + +#: src/libslic3r/PrintConfig.cpp:277 +msgid "Enable auto cooling" +msgstr "自動クーリングを有効化する" + +#: src/libslic3r/PrintConfig.cpp:539 +msgid "Enable fan if layer print time is below" +msgstr "レイヤーのプリント時間がこれ以下の場合にファンをオンにします" + +#: src/libslic3r/PrintConfig.cpp:2321 +msgid "Enable horizontal mirroring of output images" +msgstr "出力画像の水平ミラーリングを有効にします" + +#: src/libslic3r/PrintConfig.cpp:1781 +msgid "Enable support material generation." +msgstr "サポート材生成の有効化" + +#: src/libslic3r/PrintConfig.cpp:918 +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コードのプリント移動コマンドに、どのオブジェクトに属するものかがわかるようにラベルコメントが追加されます。これはOctoprintのCancelObjectプラグインに役立ちます。 この設定は、単一エクストルーダーのマルチマテリアル構成およびオブジェクト内ワイプのノズルクリーニング機能と互換性がありません。" + +#: src/libslic3r/PrintConfig.cpp:881 +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 "これを有効にすると、コメント化されたGコードファイルが生成され、各行に説明テキストが追加されます。 ただし、SDカードからプリントする場合、ファイルサイズ増大が原因で処理が間に合わずプリント速度が低下する可能性があります。" + +#: src/libslic3r/PrintConfig.cpp:2085 +msgid "Enable variable layer height feature" +msgstr "可変レイヤー高さ機能を有効にする" + +#: src/libslic3r/PrintConfig.cpp:2328 +msgid "Enable vertical mirroring of output images" +msgstr "出力イメージの垂直ミラーリングをオンにします" + +#: src/slic3r/GUI/Tab.cpp:1570 src/slic3r/GUI/Tab.cpp:1955 +#: src/libslic3r/PrintConfig.cpp:359 src/libslic3r/PrintConfig.cpp:369 +msgid "End G-code" +msgstr "終了Gコード" + +#: src/libslic3r/PrintConfig.cpp:1838 +msgid "Enforce support for the first" +msgstr "最初のサポートを強制" + +#: src/libslic3r/PrintConfig.cpp:1845 +msgid "Enforce support for the first n layers" +msgstr "最初のnレイヤーのサポートを強制します" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:197 +#: src/slic3r/GUI/PrintHostDialogs.cpp:228 +msgid "Enqueued" +msgstr "キュー追加済み" + +#: src/libslic3r/PrintConfig.cpp:380 +msgid "Ensure vertical shell thickness" +msgstr "垂直壁の厚さを確認する" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2709 +msgid "Enter new name" +msgstr "新しい名前を入力" + +#: src/slic3r/GUI/ConfigWizard.cpp:622 +msgid "Enter the bed temperature needed for getting your filament to stick to your heated bed." +msgstr "フィラメントをベッドに確実に付着させるために必要なベッド温度を入力します。" + +#: src/slic3r/GUI/ConfigWizard.cpp:570 +msgid "Enter the diameter of your filament." +msgstr "フィラメント径を入力します。" + +#: src/slic3r/GUI/ConfigWizard.cpp:557 +msgid "Enter the diameter of your printer's hot end nozzle." +msgstr "プリンターのホットエンドノズルの直径を入力します。" + +#: src/slic3r/GUI/ConfigWizard.cpp:608 +msgid "Enter the temperature needed for extruding your filament." +msgstr "フィラメントを押し出すのに必要な温度を入力します。" + +#: src/libslic3r/PrintConfig.cpp:718 +msgid "Enter your filament cost per kg here. This is only for statistical information." +msgstr "ここに1kgあたりのフィラメント価格を入力します。 プリント情報表示に使われます。" + +#: src/libslic3r/PrintConfig.cpp:686 +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 "ここにフィラメント密度を入力します。 これは統計情報に使われます。 適切な方法は、フィラメントの既知の長さを量り、長さと体積の比率を計算することです。 より良いのは、変位によって直接体積を計算することです。" + +#: src/libslic3r/PrintConfig.cpp:678 +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/slic3r/GUI/MainFrame.cpp:636 src/slic3r/GUI/PrintHostDialogs.cpp:230 +msgid "Error" +msgstr "エラー" + +#: src/slic3r/GUI/FirmwareDialog.cpp:608 +#, c-format +msgid "Error accessing port at %s: %s" +msgstr "%sポートへのアクセスエラー:%s" + +#: src/slic3r/GUI/Plater.cpp:3593 +#, c-format +msgid "Error exporting 3MF file %s" +msgstr "3MFファイル%sのエクスポートエラー" + +#: src/slic3r/GUI/Plater.cpp:3564 +#, c-format +msgid "Error exporting AMF file %s" +msgstr "AMFファイル%sのエクスポートエラー" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:153 +msgid "Error Message" +msgstr "エラーメッセージ" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:271 +msgid "Error uploading to print host:" +msgstr "プリントサーバーへのアップロードエラー:" + +#: src/libslic3r/Zipper.cpp:105 +msgid "Error with zip archive" +msgstr "zipアーカイブのエラー" + +#: src/slic3r/GUI/BedShapeDialog.cpp:333 src/slic3r/GUI/GUI_ObjectList.cpp:1443 +msgid "Error!" +msgstr "エラー!" + +#: src/slic3r/GUI/BedShapeDialog.cpp:482 +msgid "Error! Invalid model" +msgstr "エラー!無効なモデル" + +#: src/slic3r/GUI/FirmwareDialog.cpp:610 +#, c-format +msgid "Error: %s" +msgstr "エラー: %s" + +#: src/slic3r/GUI/Plater.cpp:1503 +msgid "ERROR: not enough resources to execute a new job." +msgstr "エラー:新しいジョブを実行するのに十分なリソースがありません。" + +#: src/slic3r/GUI/Plater.cpp:217 src/slic3r/GUI/Plater.cpp:1028 +#: src/slic3r/GUI/Plater.cpp:1070 +msgid "Estimated printing time" +msgstr "予測プリント時間" + +#: src/slic3r/GUI/Plater.cpp:424 +msgid "Everywhere" +msgstr "どこでも" + +#: src/slic3r/GUI/PresetHints.cpp:50 +msgid "except for the first %1% layers." +msgstr "最初の%1%レイヤーを除きます。" + +#: src/slic3r/GUI/PresetHints.cpp:52 +msgid "except for the first layer." +msgstr "最初のレイヤーを除きます。" + +#: src/libslic3r/Print.cpp:1285 +msgid "Excessive %1%=%2% mm to be printable with a nozzle diameter %3% mm" +msgstr "ノズル径%3% mmで過剰な%1%=%2% mmをプリント可能" + +#: src/slic3r/GUI/UpdateDialogs.cpp:148 +#, c-format +msgid "Exit %s" +msgstr "%s終了" + +#: src/libslic3r/PrintConfig.cpp:335 +msgid "Experimental option for preventing support material from being generated under bridged areas." +msgstr "ブリッジエリアでサポート材の生成をしない試用的オプション。" + +#: src/libslic3r/PrintConfig.cpp:1306 +msgid "Experimental option to adjust flow for overhangs (bridge flow will be used), to apply bridge speed to them and enable fan." +msgstr "オーバーハング時の流量を調整する試用的なオプション(ブリッジ流量が使用されます)、ブリッジ速度を適用してファンを有効にします。" + +#: src/slic3r/GUI/GUI_App.cpp:676 src/slic3r/GUI/wxExtensions.cpp:2461 +msgid "Expert" +msgstr "高度" + +#: src/slic3r/GUI/GUI_App.cpp:676 +msgid "Expert View Mode" +msgstr "エキスパートビューモード" + +#: src/slic3r/GUI/MainFrame.cpp:602 src/slic3r/GUI/Plater.cpp:3821 +msgid "Export" +msgstr "エクスポート" + +#: src/slic3r/GUI/MainFrame.cpp:373 +msgid "Export &Config" +msgstr "設定のエクスポート" + +#: src/slic3r/GUI/MainFrame.cpp:362 src/slic3r/GUI/MainFrame.cpp:602 +msgid "Export &G-code" +msgstr "&Gコードのエクスポート" + +#: src/slic3r/GUI/MainFrame.cpp:488 +msgid "Export &toolpaths as OBJ" +msgstr "ツールパスをOBJとしてエクスポート" + +#: src/libslic3r/PrintConfig.cpp:2949 +msgid "Export 3MF" +msgstr "3MFのエクスポート" + +#: src/slic3r/GUI/MainFrame.cpp:375 +msgid "Export all presets to file" +msgstr "すべてのプリセットをファイルにエクスポートします" + +#: src/libslic3r/PrintConfig.cpp:2954 +msgid "Export AMF" +msgstr "AMFのエクスポート" + +#: src/slic3r/GUI/Plater.cpp:1932 +msgid "Export AMF file:" +msgstr "AMFファイルのエクスポート:" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1219 src/slic3r/GUI/Plater.cpp:2927 +msgid "Export as STL" +msgstr "STLとしてエクスポート" + +#: src/slic3r/GUI/MainFrame.cpp:375 +msgid "Export Config &Bundle" +msgstr "設定とバンドルのエクスポート" + +#: src/slic3r/GUI/MainFrame.cpp:373 +msgid "Export current configuration to file" +msgstr "現在の構成をファイルにエクスポート" + +#: src/slic3r/GUI/MainFrame.cpp:370 +msgid "Export current plate as AMF" +msgstr "現在のプレートをAMFとしてエクスポート" + +#: src/slic3r/GUI/MainFrame.cpp:362 +msgid "Export current plate as G-code" +msgstr "現在のプレートをGコードとしてエクスポート" + +#: src/slic3r/GUI/MainFrame.cpp:366 +msgid "Export current plate as STL" +msgstr "現在のプレートをSTLとしてエクスポート" + +#: src/slic3r/GUI/MainFrame.cpp:368 +msgid "Export current plate as STL including supports" +msgstr "サポートを含むSTLとして現在のプレートをエクスポート" + +#: src/slic3r/GUI/Plater.cpp:2722 +msgid "Export failed" +msgstr "エクスポート失敗" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:108 src/slic3r/GUI/Plater.cpp:733 +#: src/slic3r/GUI/Plater.cpp:3821 src/libslic3r/PrintConfig.cpp:2964 +msgid "Export G-code" +msgstr "Gコードのエクスポート" + +#: src/libslic3r/PrintConfig.cpp:2931 +msgid "Export OBJ" +msgstr "OBJのエクスポート" + +#: src/slic3r/GUI/Plater.cpp:2531 +msgid "Export OBJ file:" +msgstr "OBJファイルのエクスポート :" + +#: src/slic3r/Utils/FixModelByWin10.cpp:368 +msgid "Export of a temporary 3mf file failed" +msgstr "3MFの一時ファイルのエクスポートに失敗しました" + +#: src/slic3r/GUI/MainFrame.cpp:370 +msgid "Export plate as &AMF" +msgstr "プレートを&AMFとしてエクスポート" + +#: src/slic3r/GUI/MainFrame.cpp:366 +msgid "Export plate as &STL" +msgstr "プレートを&STLとしてエクスポート" + +#: src/slic3r/GUI/MainFrame.cpp:481 +msgid "Export plate as STL &including supports" +msgstr "サポートを含むSTLとしてプレートをエクスポート" + +#: src/libslic3r/PrintConfig.cpp:2943 +msgid "Export SLA" +msgstr "SLAのエクスポート" + +#: src/libslic3r/PrintConfig.cpp:2959 +msgid "Export STL" +msgstr "STLのエクスポート" + +#: src/slic3r/GUI/Plater.cpp:1925 +msgid "Export STL file:" +msgstr "STLファイルのエクスポート :" + +#: src/libslic3r/PrintConfig.cpp:2950 +msgid "Export the model(s) as 3MF." +msgstr "モデルを3MFとしてエクスポートします。" + +#: src/libslic3r/PrintConfig.cpp:2955 +msgid "Export the model(s) as AMF." +msgstr "モデルをAMF形式でエクスポート" + +#: src/libslic3r/PrintConfig.cpp:2932 +msgid "Export the model(s) as OBJ." +msgstr "モデルをOBJとしてエクスポート" + +#: src/libslic3r/PrintConfig.cpp:2960 +msgid "Export the model(s) as STL." +msgstr "STLとしてモデルをエクスポート" + +#: src/slic3r/GUI/Plater.cpp:2927 +msgid "Export the selected object as STL file" +msgstr "選択したオブジェクトをSTL形式でエクスポートします" + +#: src/slic3r/GUI/MainFrame.cpp:488 +msgid "Export toolpaths as OBJ" +msgstr "ツールパスをOBJとしてエクスポート" + +#: src/libslic3r/Print.cpp:1517 +msgid "Exporting G-code" +msgstr "Gコードのエクスポート" + +#: src/slic3r/Utils/FixModelByWin10.cpp:341 +msgid "Exporting model..." +msgstr "モデルのエクスポート中..." + +#: src/slic3r/Utils/FixModelByWin10.cpp:219 +#: src/slic3r/Utils/FixModelByWin10.cpp:359 +msgid "Exporting source model" +msgstr "ソースモデルのエクスポート" + +#: src/libslic3r/SLAPrint.cpp:700 +msgid "Exposition time is out of printer profile bounds." +msgstr "露光時間がプリンタープロファイルの範囲外です。" + +#: src/slic3r/GUI/Tab.cpp:3306 +msgid "Exposure" +msgstr "露光" + +#: src/libslic3r/PrintConfig.cpp:2310 src/libslic3r/PrintConfig.cpp:2311 +msgid "Exposure time" +msgstr "露光時間" + +#: src/slic3r/GUI/GUI_Preview.cpp:228 src/libslic3r/GCode/PreviewData.cpp:164 +msgid "External perimeter" +msgstr "最外周" + +#: src/slic3r/GUI/PresetHints.cpp:153 +msgid "external perimeters" +msgstr "最外周" + +#: src/libslic3r/PrintConfig.cpp:415 src/libslic3r/PrintConfig.cpp:425 +msgid "External perimeters" +msgstr "最外周" + +#: src/libslic3r/PrintConfig.cpp:437 +msgid "External perimeters first" +msgstr "最外周を先にプリント" + +#: src/libslic3r/PrintConfig.cpp:1518 src/libslic3r/PrintConfig.cpp:1526 +msgid "Extra length on restart" +msgstr "追加の戻り距離" + +#: src/libslic3r/PrintConfig.cpp:1321 +msgid "Extra loading distance" +msgstr "追加ローディング長さ" + +#: src/libslic3r/PrintConfig.cpp:445 +msgid "Extra perimeters if needed" +msgstr "必要に応じて外周を追加" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:335 src/slic3r/GUI/Tab.cpp:1479 +#: src/libslic3r/PrintConfig.cpp:455 +msgid "Extruder" +msgstr "エクストルーダー" + +#: src/slic3r/GUI/Tab.cpp:2253 src/libslic3r/GCode/PreviewData.cpp:475 +#, c-format +msgid "Extruder %d" +msgstr "エクストルーダー %d" + +#: src/slic3r/GUI/ConfigWizard.cpp:592 +msgid "Extruder and Bed Temperatures" +msgstr "エクストルーダーとベッド温度" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:226 +msgid "Extruder changed to" +msgstr "エクストルーダーを変更" + +#: src/slic3r/GUI/Tab.cpp:1171 +msgid "Extruder clearance (mm)" +msgstr "エクストルーダーのクリアランス(mm)" + +#: src/libslic3r/PrintConfig.cpp:490 +msgid "Extruder Color" +msgstr "エクストルーダーカラー" + +#: src/libslic3r/PrintConfig.cpp:497 +msgid "Extruder offset" +msgstr "エクストルーダーのオフセット" + +#: src/libslic3r/PrintConfig.cpp:863 +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:1978 +msgid "Extruder temperature for layers after the first one. Set this to zero to disable temperature control commands in the output." +msgstr "最初のレイヤー以降のレイヤーのエクストルーダー温度。 出力の温度制御コマンドを無効にするには、これをゼロに設定します。" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:33 src/slic3r/GUI/GUI_ObjectList.cpp:72 +#: src/slic3r/GUI/GUI_ObjectList.cpp:513 src/slic3r/GUI/Tab.cpp:1119 +#: src/slic3r/GUI/Tab.cpp:1844 src/libslic3r/PrintConfig.cpp:456 +#: src/libslic3r/PrintConfig.cpp:954 src/libslic3r/PrintConfig.cpp:1340 +#: src/libslic3r/PrintConfig.cpp:1668 src/libslic3r/PrintConfig.cpp:1852 +#: src/libslic3r/PrintConfig.cpp:1878 src/libslic3r/PrintConfig.cpp:2151 +#: src/libslic3r/PrintConfig.cpp:2159 +msgid "Extruders" +msgstr "エクストルーダー" + +#: src/libslic3r/PrintConfig.cpp:507 +msgid "Extrusion axis" +msgstr "射出軸" + +#: src/libslic3r/PrintConfig.cpp:513 +msgid "Extrusion multiplier" +msgstr "射出率" + +#: src/slic3r/GUI/ConfigWizard.cpp:612 +msgid "Extrusion Temperature:" +msgstr "射出温度" + +#: src/slic3r/GUI/Tab.cpp:1143 +msgid "Extrusion width" +msgstr "射出幅" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:73 src/slic3r/GUI/GUI_ObjectList.cpp:514 +#: src/libslic3r/PrintConfig.cpp:416 src/libslic3r/PrintConfig.cpp:523 +#: src/libslic3r/PrintConfig.cpp:830 src/libslic3r/PrintConfig.cpp:962 +#: src/libslic3r/PrintConfig.cpp:1349 src/libslic3r/PrintConfig.cpp:1688 +#: src/libslic3r/PrintConfig.cpp:1861 src/libslic3r/PrintConfig.cpp:2018 +msgid "Extrusion Width" +msgstr "射出幅" + +#: src/slic3r/GUI/Plater.cpp:139 +msgid "Facets" +msgstr "面" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:240 +msgid "facets added" +msgstr "追加されたファセット" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:239 +msgid "facets removed" +msgstr "削除されたファセット" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:241 +msgid "facets reversed" +msgstr "ファセットが反転しました" + +#: src/libslic3r/PrintConfig.cpp:2302 +msgid "Faded layers" +msgstr "初期露出レイヤー" + +#: src/libslic3r/Zipper.cpp:47 +msgid "failed finding central directory" +msgstr "基準ディレクトリの検索に失敗しました" + +#: src/slic3r/Utils/FixModelByWin10.cpp:235 +msgid "Failed loading the input model." +msgstr "入力モデルの読み込みに失敗しました。" + +#: src/libslic3r/PrintBase.cpp:65 +msgid "Failed processing of the output_filename_format template." +msgstr "output_filename_formatの処理に失敗しました。" + +#: src/slic3r/GUI/PresetHints.cpp:41 +msgid "Fan" +msgstr "ファン" + +#: src/slic3r/GUI/Tab.cpp:1501 +msgid "Fan settings" +msgstr "ファン設定" + +#: src/slic3r/GUI/Tab.cpp:1502 +msgid "Fan speed" +msgstr "ファンスピード" + +#: src/libslic3r/PrintConfig.cpp:2240 +msgid "Fast" +msgstr "早い" + +#: src/libslic3r/PrintConfig.cpp:2241 +msgid "Fast tilt" +msgstr "早いチルト" + +#: src/slic3r/GUI/GUI_App.cpp:135 +msgid "Fatal error" +msgstr "致命的なエラー" + +#: src/slic3r/GUI/GUI_Preview.cpp:212 src/slic3r/GUI/GUI_Preview.cpp:537 +#: src/libslic3r/GCode/PreviewData.cpp:394 +msgid "Feature type" +msgstr "機能タイプ" + +#: src/slic3r/GUI/GUI_Preview.cpp:224 src/slic3r/GUI/GUI_Preview.cpp:225 +msgid "Feature types" +msgstr "射出の種類" + +#: src/slic3r/GUI/Plater.cpp:682 src/slic3r/GUI/Tab.cpp:1470 +#: src/slic3r/GUI/Tab.cpp:1471 +msgid "Filament" +msgstr "フィラメント" + +#: src/slic3r/GUI/Preset.cpp:1275 +msgid "filament" +msgstr "フィラメント" + +#: src/slic3r/GUI/ConfigWizard.cpp:541 +msgid "Filament and Nozzle Diameters" +msgstr "フィラメントとノズル径" + +#: src/slic3r/GUI/ConfigWizard.cpp:574 +msgid "Filament Diameter:" +msgstr "フィラメント径:" + +#: src/libslic3r/PrintConfig.cpp:620 +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:654 +msgid "Filament load time" +msgstr "フィラメントのローディング時間" + +#: src/libslic3r/PrintConfig.cpp:556 +msgid "Filament notes" +msgstr "フィラメントメモ" + +#: src/slic3r/GUI/Tab.cpp:1502 src/slic3r/GUI/Tab.cpp:1557 +msgid "Filament Overrides" +msgstr "フィラメント上書き" + +#: src/libslic3r/PrintConfig.cpp:1312 +msgid "Filament parking position" +msgstr "フィラメント待避ポジション" + +#: src/slic3r/GUI/Tab.cpp:1516 +msgid "Filament properties" +msgstr "フィラメント特性" + +#: src/slic3r/GUI/Tab.hpp:335 +msgid "Filament Settings" +msgstr "フィラメント設定" + +#: src/libslic3r/PrintConfig.cpp:694 +msgid "Filament type" +msgstr "フィラメントの種類" + +#: src/libslic3r/PrintConfig.cpp:669 +msgid "Filament unload time" +msgstr "フィラメントアンロード時間" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:47 +msgid "filaments" +msgstr "フィラメント" + +#: src/libslic3r/Zipper.cpp:75 +msgid "file close failed" +msgstr "ファイルのクローズに失敗しました" + +#: src/libslic3r/Zipper.cpp:69 +msgid "file create failed" +msgstr "ファイルの作成に失敗しました" + +#: src/slic3r/GUI/MainFrame.cpp:642 +msgid "File Not Found" +msgstr "ファイルが見つかりません" + +#: src/libslic3r/Zipper.cpp:89 +msgid "file not found" +msgstr "ファイルがありません" + +#: src/libslic3r/Zipper.cpp:67 +msgid "file open failed" +msgstr "ファイルオープンエラー" + +#: src/libslic3r/Zipper.cpp:73 +msgid "file read failed" +msgstr "ファイルの読込に失敗しました" + +#: src/libslic3r/Zipper.cpp:77 +msgid "file seek failed" +msgstr "ファイル検索に失敗" + +#: src/libslic3r/Zipper.cpp:79 +msgid "file stat failed" +msgstr "ファイル情報失敗" + +#: src/libslic3r/Zipper.cpp:39 +msgid "file too large" +msgstr "ファイルが大きすぎます" + +#: src/libslic3r/Zipper.cpp:71 +msgid "file write failed" +msgstr "ファイルの書き込みに失敗しました" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:152 +msgid "Filename" +msgstr "ファイル名" + +#: src/libslic3r/PrintConfig.cpp:728 +msgid "Fill angle" +msgstr "塗りつぶし角" + +#: src/libslic3r/PrintConfig.cpp:742 +msgid "Fill density" +msgstr "充填密度" + +#: src/libslic3r/PrintConfig.cpp:779 +msgid "Fill pattern" +msgstr "インフィルパターン" + +#: src/libslic3r/PrintConfig.cpp:410 +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:781 +msgid "Fill pattern for general low-density infill." +msgstr "一般的な低密度インフィルのパターン。" + +#: src/libslic3r/PrintConfig.cpp:390 +msgid "Fill pattern for top infill. This only affects the top visible layer, and not its adjacent solid shells." +msgstr "トップレイヤーのインフィル(塗りつぶし)パターン。 これは最上層レイヤーにのみに適用され、それ以外のソリッドシェル(塗りつぶし)には影響しません。" + +#: src/slic3r/GUI/BonjourDialog.cpp:225 +msgid "Finished" +msgstr "完了" + +#: src/slic3r/GUI/ConfigWizard.cpp:486 src/slic3r/GUI/Tab.cpp:1920 +msgid "Firmware" +msgstr "ファームウェア" + +#: src/slic3r/GUI/FirmwareDialog.cpp:740 +msgid "Firmware flasher" +msgstr "ファームウェア更新" + +#: src/slic3r/GUI/FirmwareDialog.cpp:765 +msgid "Firmware image:" +msgstr "ファームウェアイメージ:" + +#: src/slic3r/GUI/Tab.cpp:2431 +msgid "Firmware Retraction" +msgstr "ファームウェア引き込み" + +#: src/slic3r/GUI/ConfigWizard.cpp:486 +msgid "Firmware Type" +msgstr "ファームウェアタイプ" + +#: src/libslic3r/PrintConfig.cpp:812 src/libslic3r/PrintConfig.cpp:821 +#: src/libslic3r/PrintConfig.cpp:829 src/libslic3r/PrintConfig.cpp:862 +msgid "First layer" +msgstr "1番目のレイヤー" + +#: src/libslic3r/PrintConfig.cpp:841 +msgid "First layer height" +msgstr "最初のレイヤー高さ" + +#: src/libslic3r/Print.cpp:1328 +msgid "First layer height can't be greater than nozzle diameter" +msgstr "最初のレイヤー高は、ノズルの直径より大きくすることはできません" + +#: src/libslic3r/PrintConfig.cpp:852 +msgid "First layer speed" +msgstr "最初のレイヤーの速度" + +#: src/slic3r/GUI/PresetHints.cpp:216 +msgid "First layer volumetric" +msgstr "最初のレイヤーの体積押出し" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1210 +msgid "Fix through the Netfabb" +msgstr "Netfabbで修正" + +#: src/slic3r/GUI/Plater.cpp:3072 +msgid "Fix Throught NetFabb" +msgstr "NetFabbで修正" + +#: src/slic3r/GUI/GUI_App.cpp:685 +msgid "Flash printer &firmware" +msgstr "&firmwareをプリンタに書込む" + +#: src/slic3r/GUI/FirmwareDialog.cpp:146 +msgid "Flash!" +msgstr "書込み!" + +#: src/slic3r/GUI/FirmwareDialog.cpp:275 +msgid "Flashing cancelled." +msgstr "書込み中止。" + +#: src/slic3r/GUI/FirmwareDialog.cpp:192 +msgid "Flashing failed" +msgstr "アップロード失敗" + +#: src/slic3r/GUI/FirmwareDialog.cpp:274 +msgid "Flashing failed. Please see the avrdude log below." +msgstr "更新に失敗しました。 以下のavrdudeログを参照してください。" + +#: src/slic3r/GUI/FirmwareDialog.cpp:148 +msgid "Flashing in progress. Please do not disconnect the printer!" +msgstr "更新中。 プリンターの接続を切らないでください!" + +#: src/slic3r/GUI/FirmwareDialog.cpp:273 +msgid "Flashing succeeded!" +msgstr "更新完了!" + +#: src/slic3r/GUI/Tab.cpp:1156 +msgid "Flow" +msgstr "移動" + +#: src/slic3r/GUI/PresetHints.cpp:219 +msgid "flow rate is maximized" +msgstr "最大送り量になります" + +#: src/slic3r/GUI/UpdateDialogs.cpp:188 +msgid "For more information please visit our wiki page:" +msgstr "詳細については、Wikiページをご覧ください:" + +#: src/slic3r/GUI/Plater.cpp:435 src/slic3r/GUI/Plater.cpp:528 +msgid "For support enforcers only" +msgstr "強制サポートのみ" + +#. TRN Description for "WHITE BULLET" +#: src/slic3r/GUI/Tab.cpp:3345 +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/libslic3r/Print.cpp:1302 +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/PrintConfig.cpp:1660 +msgid "Force solid infill for regions having a smaller area than the specified threshold." +msgstr "指定されたしきい値よりも小さい領域を、ソリッドインフィル(塗りつぶし)にします。" + +#: src/libslic3r/PrintConfig.cpp:1023 +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/slic3r/GUI/WipeTowerDialog.cpp:262 +msgid "From" +msgstr "前のエクストルーダー" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1853 +msgid "From Object List You can't delete the last solid part from object." +msgstr "オブジェクトリストからオブジェクトの最後のパートを削除することはできません。" + +#: src/slic3r/GUI/MainFrame.cpp:525 +msgid "Front" +msgstr "正面" + +#: src/slic3r/GUI/MainFrame.cpp:525 +msgid "Front View" +msgstr "正面" + +#: src/slic3r/GUI/MainFrame.cpp:677 +msgid "G-code" +msgstr "Gコード" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:87 +msgid "G-code file exported to %1%" +msgstr "Gコードファイルを%1%にエクスポートしました" + +#: src/libslic3r/PrintConfig.cpp:888 +msgid "G-code flavor" +msgstr "Gコード型" + +#: src/libslic3r/PrintConfig.cpp:689 +msgid "g/cm³" +msgstr "g/cm³" + +#: src/slic3r/GUI/GUI_Preview.cpp:234 src/libslic3r/PrintConfig.cpp:870 +#: src/libslic3r/GCode/PreviewData.cpp:170 +msgid "Gap fill" +msgstr "ギャップフィル" + +#: src/slic3r/GUI/Preferences.cpp:19 src/slic3r/GUI/Tab.cpp:1812 +#: src/slic3r/GUI/Tab.cpp:2013 +msgid "General" +msgstr "全般" + +#: src/libslic3r/PrintConfig.cpp:1242 +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 "1番目のレイヤーで指定された量のフィラメントを射出するためにスカート(パーツを囲むアウトライン)周回数を設定を超えて生成します。 マルチエクストルーダーの場合、この最小値は各エクストルーダーに適用されます。" + +#: src/libslic3r/PrintConfig.cpp:1779 +msgid "Generate support material" +msgstr "サポート材の生成" + +#: src/libslic3r/PrintConfig.cpp:1840 +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 "通常のサポート材が有効/無効、およびオーバハング角度に関係なく、下から数えて指定された数のレイヤーまでのサポートを強制的に生成します。 これは、ビルドプレート(ベッド)上のプリント領域(フットプリント)が非常に薄い/不十分なオブジェクトの密着力を高めるのに役立ちます。" + +#: src/libslic3r/PrintConfig.cpp:2362 +msgid "Generate supports" +msgstr "サポート生成" + +#: src/libslic3r/PrintConfig.cpp:2364 +msgid "Generate supports for the models" +msgstr "モデルのサポートを生成する" + +#: src/libslic3r/Print.cpp:1492 +msgid "Generating brim" +msgstr "ブリム生成" + +#: src/libslic3r/Print.cpp:1524 +msgid "Generating G-code" +msgstr "Gコード作成中" + +#: src/libslic3r/SLAPrint.cpp:58 +msgid "Generating pad" +msgstr "パッド生成" + +#: src/libslic3r/PrintObject.cpp:141 +msgid "Generating perimeters" +msgstr "境界線の生成中" + +#: src/libslic3r/Print.cpp:1484 +msgid "Generating skirt" +msgstr "スカート生成" + +#: src/libslic3r/PrintObject.cpp:391 +msgid "Generating support material" +msgstr "サポート材の生成" + +#: src/libslic3r/SLAPrint.cpp:56 src/libslic3r/SLAPrint.cpp:809 +msgid "Generating support points" +msgstr "サポートポイントの生成" + +#: src/libslic3r/SLAPrint.cpp:57 +msgid "Generating support tree" +msgstr "サポートツリーの生成" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1551 +msgid "Generic" +msgstr "一般" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:144 +msgid "Gizmo cut" +msgstr "ギズモカット" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:141 +msgid "Gizmo move" +msgstr "ギズモ移動" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:145 +msgid "Gizmo Place face on bed" +msgstr "ベッド面にギズモ配置" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:143 +msgid "Gizmo rotate" +msgstr "ギズモ回転" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:142 +msgid "Gizmo scale" +msgstr "ギズモ縮尺" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:146 +msgid "Gizmo SLA support points" +msgstr "ギズモ-SLAサポートポイント" + +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:641 +msgid "Gizmo-Move" +msgstr " ギズモ移動" + +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:569 +msgid "Gizmo-Place on Face" +msgstr "ギズモ-面に配置" + +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:651 +msgid "Gizmo-Rotate" +msgstr "ギズモ-回転" + +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:646 +msgid "Gizmo-Scale" +msgstr "ギズモ-縮尺" + +#: src/slic3r/GUI/AboutDialog.cpp:95 +msgid "GNU Affero General Public License, version 3" +msgstr "GNU Affero General Public License, version 3 (AGPL v3)" + +#: src/slic3r/GUI/ConfigWizard.cpp:571 +msgid "Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average." +msgstr "高い精度が必要なため、ノギスを使用して何カ所かフィラメントの測定を行い、直径を計算します。" + +#: src/libslic3r/PrintConfig.cpp:797 +msgid "Grid" +msgstr "グリッド" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1846 +msgid "Group manipulation" +msgstr "グループ操作" + +#: src/libslic3r/PrintConfig.cpp:805 +msgid "Gyroid" +msgstr "ジャイロイド" + +#: src/slic3r/GUI/Tab.cpp:2775 +msgid "has the following unsaved changes:" +msgstr "次の未保存の変更があります:" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:840 +msgid "Head diameter" +msgstr "先端径" + +#: src/libslic3r/PrintConfig.cpp:822 +msgid "Heated build plate temperature for the first layer. Set this to zero to disable bed temperature control commands in the output." +msgstr "最初のレイヤーのビルドプレート(ベッド)の加熱温度。ベッド温度制御コマンドを無効にするには、これをゼロにします。" + +#: src/slic3r/GUI/GUI_Preview.cpp:213 src/libslic3r/PrintConfig.cpp:468 +msgid "Height" +msgstr "高さ" + +#: src/libslic3r/GCode/PreviewData.cpp:396 +msgid "Height (mm)" +msgstr "高さ(mm)" + +#: src/libslic3r/PrintConfig.cpp:1618 +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:2209 +msgid "Height of the display" +msgstr "ディスプレイの高さ" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1350 +msgid "Height range Modifier" +msgstr "個別条件領域の高さ範囲" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3650 src/slic3r/GUI/GUI_ObjectList.cpp:2206 +msgid "Height ranges" +msgstr "高さ範囲" + +#: src/libslic3r/PrintConfig.cpp:226 +msgid "Heights at which a filament change is to occur." +msgstr "フィラメントを切り替える高さ。" + +#: src/slic3r/GUI/ConfigWizard.cpp:300 +#, 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は初期設定に役立ちます。 いくつかの設定を行うだけで、プリントの準備ができます。" + +#: src/libslic3r/PrintConfig.cpp:2976 +msgid "Help" +msgstr "ヘルプ" + +#: src/libslic3r/PrintConfig.cpp:2982 +msgid "Help (FFF options)" +msgstr "ヘルプ(FFFオプション)" + +#: src/libslic3r/PrintConfig.cpp:2987 +msgid "Help (SLA options)" +msgstr "ヘルプ(SLAオプション)" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:225 +msgid "Here you can adjust required purging volume (mm³) for any given pair of tools." +msgstr "ここで、ツールの任意のペアで必要なパージ量(mm³)を調整できます。" + +#: src/libslic3r/PrintConfig.cpp:925 +msgid "High extruder current on filament swap" +msgstr "フィラメント交換時の高いエクストルーダー電流" + +#: src/libslic3r/PrintConfig.cpp:400 src/libslic3r/PrintConfig.cpp:806 +msgid "Hilbert Curve" +msgstr "ヒルベルト曲線" + +#: src/slic3r/GUI/Plater.cpp:873 +msgid "Hold Shift to Slice & Export G-code" +msgstr "シフトキーを押しながらで、スライス&Gコードエクスポート" + +#: src/libslic3r/PrintConfig.cpp:803 src/libslic3r/PrintConfig.cpp:1924 +msgid "Honeycomb" +msgstr "ハニカム" + +#: src/slic3r/GUI/Tab.cpp:1013 +msgid "Horizontal shells" +msgstr "水平構造設定" + +#: src/libslic3r/PrintConfig.cpp:209 +msgid "Horizontal width of the brim that will be printed around each object on the first layer." +msgstr "各オブジェクトの最初のレイヤーにプリントされるブリム(縁)の幅。" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:151 +msgid "Host" +msgstr "サーバー" + +#: src/libslic3r/PrintConfig.cpp:1267 +msgid "Host Type" +msgstr "サーバータイプ" + +#: src/slic3r/GUI/BonjourDialog.cpp:73 +msgid "Hostname" +msgstr "ホスト名" + +#: src/libslic3r/PrintConfig.cpp:81 +msgid "Hostname, IP or URL" +msgstr "ホスト名、IPアドレス、もしくはURL" + +#: src/slic3r/GUI/Tab.cpp:136 +msgid "" +"Hover the cursor over buttons to find more information \n" +"or click this button." +msgstr "カーソルをボタンの上に置くと、詳細情報が表示されます。またはこのボタンをクリックします。" + +#: src/libslic3r/PrintConfig.cpp:2734 +msgid "How much should the tiny connectors penetrate into the model body." +msgstr "小さなコネクターをモデルにどの程度深く入れるか。" + +#: src/libslic3r/PrintConfig.cpp:2380 +msgid "How much the pinhead has to penetrate the model surface" +msgstr "サポートの先端がモデルの表面をどの程度貫通しているか" + +#: src/libslic3r/PrintConfig.cpp:2642 +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:95 +msgid "HTTPS CA File" +msgstr "HTTPS CAファイル" + +#: src/slic3r/GUI/Tab.cpp:1731 +msgid "HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate." +msgstr "HTTPS CAファイルはオプションです。 HTTPSを自己署名証明書で使用する場合にのみ必要です。" + +#: src/slic3r/GUI/Tab.cpp:1773 +#, 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ファイル:\n" +"     このシステムでは、%sはシステムの証明書ストアまたはキーチェーンからのHTTPS証明書を使用します。\n" +"     カスタムCAファイルを使用するには、CAファイルを証明書ストア/キーチェーンにインポートしてください。" + +#: src/slic3r/GUI/Preferences.cpp:192 +msgid "Icon size in a respect to the default size" +msgstr "デフォルトのサイズと相対的なアイコンのサイズ" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:148 +msgid "ID" +msgstr "ID" + +#: src/libslic3r/PrintConfig.cpp:1787 +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/slic3r/GUI/ConfigWizard.cpp:413 +#, 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 "有効にすると、%sはプログラムの新バージョンをオンラインでチェックします。 新しいバージョンが利用可能になると、次のアプリケーションの起動時にメッセージが表示されます(プログラムの使用中は表示されません)。 これは単なる通知であり、自動インストールは行われません。" + +#: src/slic3r/GUI/ConfigWizard.cpp:423 +#, 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 "有効にすると、%sはバックグラウンドでビルトインシステムプリセットアップデートをダウンロードします。 これらの更新は一時的な場所にダウンロードされます。 新しいプリセットが利用可能な場合、プログラムの起動時に警告が表示されます。" + +#: src/libslic3r/PrintConfig.cpp:1774 +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/slic3r/GUI/Preferences.cpp:63 +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 "有効にすると、PrusaSlicerはプログラムの新しく利用可能なバージョンをチェックします。 新しいバージョンが利用可能な場合、プログラムの次回起動時に通知が表示されます(アプリケーションの使用中は表示されません)。これは単なる通知メカニズムであり、自動インストールは行われません。" + +#: src/slic3r/GUI/Preferences.cpp:71 +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はビルトインシステムプリセットの更新をバックグラウンドでダウンロードします。 更新ファイルは、一時的な場所にダウンロードされます。 新しいプリセットバージョンが利用可能になると、アプリケーションの起動時に通知されます。" + +#: src/slic3r/GUI/Preferences.cpp:105 +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 "有効にすると、3DシーンはRetina解像度でレンダリングされます。 3Dパフォーマンスに問題がある場合は、このオプションを無効にしてください。" + +#: src/slic3r/GUI/Preferences.cpp:112 +msgid "If enabled, use perspective camera. If not enabled, use orthographic camera." +msgstr "有効にした場合、パース(遠近)ビューカメラを使用します。 有効になっていない場合は、アイソメ(等角)ビューカメラを使用します。" + +#: src/slic3r/GUI/Preferences.cpp:119 +msgid "If enabled, you can change size of toolbar icons manually." +msgstr "有効にすると、ツールバーアイコンのサイズをマニュアル変更できます。" + +#: 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 "レイヤーの推定時間が〜%1%s未満の場合、ファンは%2%%%で動作し、プリント速度が低下して、そのレイヤーで%3%s以上が費やされます(ただし、速度が%4%mm/s以下になることはありません)。" + +#: src/libslic3r/PrintConfig.cpp:853 +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 "値がmm/s単位の絶対値として入力された場合、この速度はタイプに関係なく、最初のレイヤーのすべてのプリント移動に適用されます。 パーセンテージ(例:40%)で入力された場合、デフォルトの速度をスケーリングします。" + +#: src/libslic3r/PrintConfig.cpp:540 +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:1636 +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:534 +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 "この機能がオンの場合、ファンはオフにならず、少なくとも最低速度として設定された値で回転し続けます。 PLAに有用ですが、ABSには不向きです。" + +#: src/slic3r/GUI/Preferences.cpp:46 +msgid "If this is enabled, Slic3r will auto-center objects around the print bed center." +msgstr "有効にすると、Slic3rは自動的にオブジェクトをプリント領域の中央に配置します。" + +#: src/slic3r/GUI/Preferences.cpp:54 +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コードをエクスポートまでの時間を短縮するために、オブジェクトがロードされるとすぐに前処理を行います。" + +#: src/slic3r/GUI/Preferences.cpp:38 +msgid "If this is enabled, Slic3r will prompt the last output directory instead of the one containing the input files." +msgstr "これを有効にすると、Slic3rは入力ファイルを含むディレクトリではなく、最後の出力ディレクトリを取得します。" + +#: src/libslic3r/PrintConfig.cpp:1492 +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 "これに正の値を入力すると、引き込みされるたびにZが瞬間持ち上げられます。 マルチエクストルーダーを使用する場合、最初のエクストルーダーの設定が優先されます。" + +#: src/libslic3r/PrintConfig.cpp:1501 +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 "Zリフトが設定された絶対値の高さZ以上に制限されます。これを使って1レイヤー目のZリフトをスキップさせることが出来ます。" + +#: src/libslic3r/PrintConfig.cpp:1510 +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 "正の値を入力すると、Zリフトは指定された絶対Z未満でのみ発生します。この設定を調整して、リフトを最初のレイヤーに制限できます。" + +#: src/libslic3r/PrintConfig.cpp:1384 +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 "独自のスクリプトを使用して出力Gコードを制御する場合は、ここで絶対パスを指定します。 複数のスクリプトをセミコロンで区切ってください。 スクリプトは最初の引数としてGコードファイルへの絶対パスを渡し、環境変数を読み取ることでSlic3r構成設定にアクセスできます。" + +#: src/libslic3r/PrintConfig.cpp:498 +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 "ファームウェアがエクストルーダーの変位を処理しない場合は、それを考慮するためのGコードが必要です。 このオプションでは、最初のエクストルーダーに対する各エクストルーダーのオフセットを指定できます。 正の座標が必要です(XY座標から減算されます)。" + +#: src/libslic3r/PrintConfig.cpp:2068 +msgid "If your firmware requires relative E values, check this, otherwise leave it unchecked. Most firmwares use absolute values." +msgstr "ファームウェアに相対的なE値が必要な場合にチェックします。そうでない場合はオフのままにします。 ほとんどのファームウェアは絶対値を使用します。" + +#: src/libslic3r/PrintConfig.cpp:3096 +msgid "Ignore non-existent config files" +msgstr "存在しない設定ファイルを無視する" + +#: src/slic3r/GUI/MainFrame.cpp:352 +msgid "Import &Config" +msgstr "設定インポート" + +#: src/slic3r/GUI/MainFrame.cpp:357 +msgid "Import Config &Bundle" +msgstr "構成バンドルのインポート" + +#: src/slic3r/GUI/MainFrame.cpp:354 +msgid "Import Config from &project" +msgstr "プロジェクトから構成をインポート" + +#: src/slic3r/GUI/Plater.cpp:4016 +msgid "Import Object" +msgstr "オブジェクトをインポート" + +#: src/slic3r/GUI/Plater.cpp:4020 +msgid "Import Objects" +msgstr "オブジェクトのインポート" + +#: src/slic3r/Utils/FixModelByWin10.cpp:383 +msgid "Import of the repaired 3mf file failed" +msgstr "修正した3mfファイルのインポートに失敗しました" + +#: src/slic3r/GUI/MainFrame.cpp:349 +msgid "Import STL/OBJ/AM&F/3MF" +msgstr "STL/OBJ/AMF/3MFのインポート" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:106 +msgid "Import STL/OBJ/AMF/3MF without config, keep bed" +msgstr "設定なしでSTL/OBJ/AMF/3MFをインポートします(プリント領域を維持します)" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2416 +#, c-format +msgid "In this mode you can select only other %s Items%s" +msgstr "このモードでは、他の%sアイテム%sのみを選択できます" + +#: src/slic3r/GUI/UpdateDialogs.cpp:132 +msgid "Incompatible bundles:" +msgstr "互換性のないパッケージ:" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:70 +#, c-format +msgid "Incompatible with this %s" +msgstr "この%sと互換性がありません" + +#: src/slic3r/GUI/Plater.cpp:4091 +msgid "Increase Instances" +msgstr "インスタンスを増やす" + +#. TRN Description for "UNLOCKED LOCK" +#: src/slic3r/GUI/Tab.cpp:3338 +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 "" +"一部の設定が変更され、現在のオプショングループのシステム(またはデフォルト)値と等しくないことを示します。\n" +"開いたカギアイコンをクリックして、現在のオプショングループのすべての設定をシステム(またはデフォルト)値にリセットします。" + +#. TRN Description for "LOCKED LOCK" +#: src/slic3r/GUI/Tab.cpp:3334 +msgid "indicates that the settings are the same as the system (or default) values for the current option group" +msgstr "設定が現在の設定グループのシステム(デフォルト)値と同じであることを示します" + +#. TRN Description for "BACK ARROW" +#: src/slic3r/GUI/Tab.cpp:3083 +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 "設定が変更され、現在のオプショングループに最後に保存されたプリセットと等しくないことを示します。戻る矢印アイコンをクリックして、現在のオプショングループのすべての設定を最後に保存されたプリセットに戻します。" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:29 src/slic3r/GUI/GUI_ObjectList.cpp:69 +#: src/slic3r/GUI/GUI_ObjectList.cpp:510 src/slic3r/GUI/Plater.cpp:439 +#: src/slic3r/GUI/Tab.cpp:1030 src/slic3r/GUI/Tab.cpp:1031 +#: src/slic3r/GUI/Tab.cpp:1360 src/libslic3r/PrintConfig.cpp:167 +#: src/libslic3r/PrintConfig.cpp:389 src/libslic3r/PrintConfig.cpp:729 +#: src/libslic3r/PrintConfig.cpp:743 src/libslic3r/PrintConfig.cpp:780 +#: src/libslic3r/PrintConfig.cpp:933 src/libslic3r/PrintConfig.cpp:943 +#: src/libslic3r/PrintConfig.cpp:961 src/libslic3r/PrintConfig.cpp:979 +#: src/libslic3r/PrintConfig.cpp:998 src/libslic3r/PrintConfig.cpp:1659 +#: src/libslic3r/PrintConfig.cpp:1676 +msgid "Infill" +msgstr "インフィル" + +#: src/slic3r/GUI/PresetHints.cpp:171 +msgid "infill" +msgstr "インフィル" + +#: src/libslic3r/PrintConfig.cpp:972 +msgid "Infill before perimeters" +msgstr "外周よりも先にインフィルを実施" + +#: src/libslic3r/PrintConfig.cpp:953 +msgid "Infill extruder" +msgstr "インフィルエクストルーダー" + +#: src/libslic3r/PrintConfig.cpp:987 +msgid "Infill/perimeters overlap" +msgstr "外周とインフィルの重なり幅" + +#: src/libslic3r/Print.cpp:1476 +msgid "Infilling layers" +msgstr "レイヤーのインフィル" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2424 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2497 src/slic3r/GUI/Plater.cpp:118 +msgid "Info" +msgstr "情報" + +#: src/libslic3r/PrintConfig.cpp:1008 +msgid "Inherits profile" +msgstr "プロファイルを継承" + +#: src/libslic3r/SLAPrint.cpp:707 +msgid "Initial exposition time is out of printer profile bounds." +msgstr "初期露出時間は、プリンタプロファイルの範囲外です。" + +#: src/libslic3r/PrintConfig.cpp:2317 src/libslic3r/PrintConfig.cpp:2318 +msgid "Initial exposure time" +msgstr "初期露出時間" + +#: src/libslic3r/PrintConfig.cpp:2295 src/libslic3r/PrintConfig.cpp:2296 +msgid "Initial layer height" +msgstr "初期レイヤー高さ" + +#: src/slic3r/GUI/Field.cpp:155 +msgid "Input value is out of range" +msgstr "入力値が範囲を超えています" + +#: src/slic3r/GUI/GUI_App.cpp:661 +msgid "Inspect / activate configuration snapshots" +msgstr "構成スナップショットの点検/有効化" + +#: src/slic3r/GUI/wxExtensions.cpp:407 src/slic3r/GUI/wxExtensions.cpp:474 +#, c-format +msgid "Instance %d" +msgstr "インスタンス%d" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1887 +msgid "Instance manipulation" +msgstr "オブジェクトのインスタンスを操作する" + +#: src/slic3r/GUI/wxExtensions.cpp:358 +msgid "Instances" +msgstr "インスタンス" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:934 src/slic3r/GUI/GUI_ObjectList.cpp:3346 +msgid "Instances to Separated Objects" +msgstr "分離されたオブジェクトのインスタンス" + +#: src/libslic3r/PrintConfig.cpp:1886 +msgid "Interface layers" +msgstr "インターフェースレイヤー" + +#: src/libslic3r/PrintConfig.cpp:1870 +msgid "Interface loops" +msgstr "インターフェースのループ" + +#: src/libslic3r/PrintConfig.cpp:1895 +msgid "Interface pattern spacing" +msgstr "コンタクトレイヤーのピッチ" + +#: src/libslic3r/PrintConfig.cpp:1022 +msgid "Interface shells" +msgstr "中間壁" + +#: src/libslic3r/Zipper.cpp:87 +msgid "internal error" +msgstr "内部エラー" + +#: src/slic3r/GUI/GUI_Preview.cpp:230 src/libslic3r/GCode/PreviewData.cpp:166 +msgid "Internal infill" +msgstr "内部のインフィル" + +#: src/slic3r/GUI/Plater.cpp:2397 +msgid "Invalid data" +msgstr "無効なデータ" + +#: src/slic3r/GUI/BedShapeDialog.cpp:471 src/slic3r/GUI/BedShapeDialog.cpp:520 +#: src/slic3r/GUI/BedShapeDialog.cpp:543 +msgid "Invalid file format." +msgstr "無効なファイル形式。" + +#: src/libslic3r/Zipper.cpp:83 +msgid "invalid filename" +msgstr "無効なファイル名" + +#: src/libslic3r/Zipper.cpp:51 +msgid "invalid header or archive is corrupted" +msgstr "無効なヘッダーまたはアーカイブが破損しています" + +#: src/slic3r/GUI/Field.cpp:150 src/slic3r/GUI/Field.cpp:173 +msgid "Invalid numeric input." +msgstr "無効な数値入力。" + +#: src/libslic3r/Zipper.cpp:81 +msgid "invalid parameter" +msgstr "無効なパラメーター" + +#. TRN "Slic3r _is licensed under the_ License" +#: src/slic3r/GUI/AboutDialog.cpp:94 +msgid "is licensed under the" +msgstr "の下でライセンスされています" + +#: src/slic3r/GUI/Tab.cpp:2779 +msgid "is not compatible with print profile" +msgstr "プリントプロファイルと互換性がない" + +#: src/slic3r/GUI/Tab.cpp:2778 +msgid "is not compatible with printer" +msgstr "プリンターと互換性がありません" + +#: src/slic3r/GUI/MainFrame.cpp:519 +msgid "Iso" +msgstr "アイソメ" + +#: src/slic3r/GUI/MainFrame.cpp:519 +msgid "Iso View" +msgstr "アイソメ表示" + +#: src/slic3r/GUI/Tab.cpp:925 +msgid "It can't be deleted or modified." +msgstr "削除もしくは変更ができません。" + +#: src/libslic3r/PrintConfig.cpp:926 +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/slic3r/GUI/GUI.cpp:142 src/slic3r/GUI/Tab.cpp:2796 +msgid "It's impossible to print multi-part object(s) with SLA technology." +msgstr "SLAではマルチパートオブジェクトのプリントはできません。" + +#: src/slic3r/GUI/Tab.cpp:2177 +msgid "Jerk limits" +msgstr "ジャーク(加加速度)限界" + +#: src/libslic3r/PrintConfig.cpp:1579 +msgid "Jitter" +msgstr "ジッター" + +#: src/libslic3r/PrintConfig.cpp:533 +msgid "Keep fan always on" +msgstr "ファンを常時オン" + +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:194 +msgid "Keep lower part" +msgstr "下側パーツをキープ" + +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:193 +msgid "Keep upper part" +msgstr "上側パーツを保持" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:13 src/slic3r/GUI/MainFrame.cpp:566 +msgid "Keyboard Shortcuts" +msgstr "キーボードショートカット" + +#: src/libslic3r/PrintConfig.cpp:917 +msgid "Label objects" +msgstr "オブジェクトにラベルを付ける" + +#: src/libslic3r/PrintConfig.cpp:2234 +msgid "Landscape" +msgstr "横方向" + +#: src/slic3r/GUI/GUI_App.cpp:524 +msgid "Language" +msgstr "言語" + +#: src/slic3r/GUI/GUI_App.cpp:755 +msgid "Language selection" +msgstr "言語選択" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1770 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1872 +msgid "Last instance of an object cannot be deleted." +msgstr "オブジェクトの最後のインスタンスは削除できません。" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2994 +msgid "Layer" +msgstr "レイヤー" + +#: src/slic3r/GUI/Tab.cpp:998 src/libslic3r/PrintConfig.cpp:55 +msgid "Layer height" +msgstr "積層ピッチ" + +#: src/libslic3r/Print.cpp:1332 +msgid "Layer height can't be greater than nozzle diameter" +msgstr "ノズル径を超えるレイヤー高さには設定できません" + +#: src/slic3r/GUI/Tab.cpp:2260 +msgid "Layer height limits" +msgstr "レイヤー高さ限度" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2109 +msgid "Layer range Settings to modify" +msgstr "修正するレイヤー範囲の設定" + +#: src/libslic3r/PrintConfig.cpp:326 src/libslic3r/PrintConfig.cpp:946 +#: src/libslic3r/PrintConfig.cpp:1435 src/libslic3r/PrintConfig.cpp:1620 +#: src/libslic3r/PrintConfig.cpp:1681 src/libslic3r/PrintConfig.cpp:1844 +#: src/libslic3r/PrintConfig.cpp:1889 +msgid "layers" +msgstr "レイヤー" + +#: src/slic3r/GUI/Tab.cpp:3302 src/slic3r/GUI/Tab.cpp:3393 +msgid "Layers" +msgstr "レイヤー" + +#: src/slic3r/GUI/Tab.cpp:997 src/slic3r/GUI/Tab.cpp:3391 +msgid "Layers and perimeters" +msgstr "レイヤーと外周" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:28 src/slic3r/GUI/GUI_ObjectList.cpp:68 +#: src/slic3r/GUI/GUI_ObjectList.cpp:509 src/libslic3r/PrintConfig.cpp:56 +#: src/libslic3r/PrintConfig.cpp:150 src/libslic3r/PrintConfig.cpp:381 +#: src/libslic3r/PrintConfig.cpp:438 src/libslic3r/PrintConfig.cpp:446 +#: src/libslic3r/PrintConfig.cpp:842 src/libslic3r/PrintConfig.cpp:1026 +#: src/libslic3r/PrintConfig.cpp:1305 src/libslic3r/PrintConfig.cpp:1371 +#: src/libslic3r/PrintConfig.cpp:1552 src/libslic3r/PrintConfig.cpp:1987 +#: src/libslic3r/PrintConfig.cpp:2044 +msgid "Layers and Perimeters" +msgstr "積層ピッチと外壁の設定" + +#: src/slic3r/GUI/GLCanvas3D.cpp:526 +msgid "Layers heights" +msgstr "積層ピッチ" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:189 +msgid "Layers Slider Shortcuts" +msgstr "レイヤースライダーのショートカット" + +#. TRN To be shown in Print Settings "Bottom solid layers" +#: rc/libslic3r/PrintConfig.cpp:149 +msgctxt "Layers" +msgid "Bottom" +msgstr "レイヤー||最下層" + +#. TRN To be shown in Print Settings "Top solid layers" +#: src/libslic3r/PrintConfig.cpp:2043 +msgctxt "Layers" +msgid "Top" +msgstr "レイヤー||トップ" + +#: src/slic3r/GUI/MainFrame.cpp:527 +msgid "Left" +msgstr "左" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1231 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1234 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1235 +msgid "Left click" +msgstr "左クリック" + +#: src/slic3r/GUI/MainFrame.cpp:527 +msgid "Left View" +msgstr "左面" + +#: src/slic3r/GUI/GUI_Preview.cpp:255 +msgid "Legend" +msgstr "凡例" + +#: src/libslic3r/PrintConfig.cpp:1473 src/libslic3r/PrintConfig.cpp:1481 +msgid "Length" +msgstr "長さ" + +#: src/libslic3r/PrintConfig.cpp:293 +msgid "Length of the cooling tube to limit space for cooling moves inside it." +msgstr "ノズルからの溶融樹脂を引抜いた後にフィラメントを凝固させるための冷却用チューブの長さ。" + +#. TRN "Slic3r _is licensed under the_ License" +#: src/slic3r/GUI/AboutDialog.cpp:124 +msgid "License agreements of all following programs (libraries) are part of application license agreement" +msgstr "以下のすべてのプログラム(ライブラリ)のライセンス契約は、アプリケーションライセンス契約の一部です。" + +#: src/libslic3r/PrintConfig.cpp:1491 +msgid "Lift Z" +msgstr "リフトZ" + +#: src/libslic3r/PrintConfig.cpp:801 +msgid "Line" +msgstr "線" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1050 +msgid "Load" +msgstr "ロード" + +#: src/slic3r/GUI/MainFrame.cpp:349 +msgid "Load a model" +msgstr "モデルを読込む" + +#: src/libslic3r/PrintConfig.cpp:3116 +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:3100 +msgid "Load config file" +msgstr "設定ファイルの読込み" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:107 +msgid "Load Config from .ini/amf/3mf/gcode" +msgstr "構成を.ini/amf/3mf/gcodeから読み込む" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:110 +msgid "Load Config from .ini/amf/3mf/gcode and merge" +msgstr "構成を.ini/amf/3mf/gcodeから読み込んでマージ" + +#: src/slic3r/GUI/MainFrame.cpp:354 +msgid "Load configuration from project file" +msgstr "プロジェクトファイルから設定を読み込む" + +#: src/libslic3r/PrintConfig.cpp:3101 +msgid "Load configuration from the specified file. It can be used more than once to load options from multiple files." +msgstr "指定されたファイルから構成をロードします。 複数のファイルからオプションをロードするために複数回使用できます。" + +#: src/slic3r/GUI/MainFrame.cpp:352 +msgid "Load exported configuration file" +msgstr "エクスポートされた構成ファイルを読込む" + +#: src/slic3r/GUI/Plater.cpp:1271 +msgid "Load File" +msgstr "ファイルのロード" + +#: src/slic3r/GUI/Plater.cpp:1275 +msgid "Load Files" +msgstr "複数ファイルのロード" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1585 +msgid "Load Part" +msgstr "パーツの読込み" + +#: src/slic3r/GUI/MainFrame.cpp:357 +msgid "Load presets from a bundle" +msgstr "プリセットをバンドルから読込む" + +#: src/slic3r/GUI/Plater.cpp:3992 +msgid "Load Project" +msgstr "プロジェクト読込み" + +#: src/slic3r/GUI/BedShapeDialog.cpp:97 +msgid "Load shape from STL..." +msgstr "STLから形状を読込み..." + +#: src/slic3r/GUI/BedShapeDialog.cpp:181 src/slic3r/GUI/BedShapeDialog.cpp:249 +msgid "Load..." +msgstr "ロード..." + +#: src/slic3r/GUI/WipeTowerDialog.cpp:235 +msgid "loaded" +msgstr "ロード完了" + +#: src/slic3r/GUI/Plater.cpp:1782 +msgid "Loaded" +msgstr "読み込みました" + +#: src/slic3r/GUI/Plater.cpp:1590 +msgid "Loading" +msgstr "ローディング" + +#: src/slic3r/GUI/GUI_App.cpp:407 +msgid "Loading of a mode view" +msgstr "ビューモードの読込み" + +#: src/slic3r/GUI/GUI_App.cpp:399 +msgid "Loading of current presets" +msgstr "現在のプリセットを取得する" + +#: src/slic3r/Utils/FixModelByWin10.cpp:251 +#: src/slic3r/Utils/FixModelByWin10.cpp:378 +msgid "Loading repaired model" +msgstr "修復モデルを読込み" + +#: src/libslic3r/PrintConfig.cpp:575 +msgid "Loading speed" +msgstr "ローディング速度" + +#: src/libslic3r/PrintConfig.cpp:583 +msgid "Loading speed at the start" +msgstr "ローディング開始時の速度" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:41 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:84 +msgid "Local coordinates" +msgstr "ローカル座標" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:851 +msgid "Lock supports under new islands" +msgstr "新しい台座でのサポートロック" + +#: src/slic3r/GUI/Tab.cpp:3065 +msgid "LOCKED LOCK" +msgstr "ロックしたカギ" + +#: src/slic3r/GUI/Tab.cpp:3360 +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:3376 +msgid "LOCKED LOCK icon indicates that the value is the same as the system (or default) value." +msgstr "カギロック状態のアイコンは、値がシステム(デフォルト)値と同じであることを示します。" + +#: src/libslic3r/PrintConfig.cpp:3119 +msgid "Logging level" +msgstr "ログレベル" + +#: src/libslic3r/PrintConfig.cpp:1625 +msgid "Loops (minimum)" +msgstr "ループ数(最小)" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:172 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:174 +msgid "Lower Layer" +msgstr "最下層レイヤー" + +#: src/slic3r/GUI/Tab.cpp:2136 src/slic3r/GUI/Tab.cpp:2209 +#: src/libslic3r/PrintConfig.cpp:1077 src/libslic3r/PrintConfig.cpp:1087 +#: src/libslic3r/PrintConfig.cpp:1097 src/libslic3r/PrintConfig.cpp:1110 +#: src/libslic3r/PrintConfig.cpp:1121 src/libslic3r/PrintConfig.cpp:1132 +#: src/libslic3r/PrintConfig.cpp:1143 +msgid "Machine limits" +msgstr "機体の限界" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:129 +msgid "Main Shortcuts" +msgstr "メインショートカット" + +#: src/slic3r/GUI/Plater.cpp:143 +msgid "Manifold" +msgstr "モデルOK" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:908 +msgid "Manual editing" +msgstr "マニュアル編集" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:105 +msgid "Masked SLA file exported to %1%" +msgstr "マスクされたSLAファイルが%1%にエクスポートされました" + +#: src/slic3r/GUI/MainFrame.cpp:604 +msgid "Mate&rial Settings Tab" +msgstr "材料設定タブ" + +#: src/slic3r/GUI/Tab.cpp:3300 +msgid "Material" +msgstr "材料" + +#: src/slic3r/GUI/Tab.hpp:391 +msgid "Material Settings" +msgstr "材料設定" + +#: src/slic3r/GUI/Plater.cpp:140 +msgid "Materials" +msgstr "材料" + +#: src/libslic3r/PrintConfig.cpp:1152 src/libslic3r/PrintConfig.cpp:1161 +msgid "Max" +msgstr "最大" + +#: src/libslic3r/PrintConfig.cpp:2470 +msgid "Max bridge length" +msgstr "最長ブリッジ長さ" + +#: src/libslic3r/PrintConfig.cpp:2546 +msgid "Max merge distance" +msgstr "最大結合距離" + +#: src/libslic3r/PrintConfig.cpp:2479 +msgid "Max pillar linking distance" +msgstr "支柱がリンクする最大距離" + +#: src/libslic3r/PrintConfig.cpp:64 +msgid "Max print height" +msgstr "最大のプリント高さ" + +#: src/libslic3r/PrintConfig.cpp:1172 +msgid "Max print speed" +msgstr "最大プリント速度" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:55 +msgid "max slic3r version" +msgstr "slic3rの最大バージョン" + +#: src/libslic3r/PrintConfig.cpp:1203 +msgid "Max volumetric slope negative" +msgstr "最大体積押出し下り勾配" + +#: src/libslic3r/PrintConfig.cpp:1192 +msgid "Max volumetric slope positive" +msgstr "最大体積押出し上り勾配" + +#: src/libslic3r/PrintConfig.cpp:565 src/libslic3r/PrintConfig.cpp:1182 +msgid "Max volumetric speed" +msgstr "最大体積押出し速度" + +#: src/libslic3r/PrintConfig.cpp:2167 +msgid "Maximal bridging distance" +msgstr "ブリッジ最大距離" + +#: src/libslic3r/PrintConfig.cpp:2193 +msgid "Maximal distance between supports on sparse infill sections." +msgstr "中抜きインフィルレイヤーの間隔の最大値。" + +#: src/libslic3r/PrintConfig.cpp:1099 +msgid "Maximum acceleration E" +msgstr "E最大加速度" + +#: src/libslic3r/PrintConfig.cpp:1105 +msgid "Maximum acceleration of the E axis" +msgstr "最大E軸加速度" + +#: src/libslic3r/PrintConfig.cpp:1102 +msgid "Maximum acceleration of the X axis" +msgstr "X軸の最大加速度" + +#: src/libslic3r/PrintConfig.cpp:1103 +msgid "Maximum acceleration of the Y axis" +msgstr "最大Y軸加速度" + +#: src/libslic3r/PrintConfig.cpp:1104 +msgid "Maximum acceleration of the Z axis" +msgstr "Z軸の最大加速度" + +#: src/libslic3r/PrintConfig.cpp:1131 src/libslic3r/PrintConfig.cpp:1133 +msgid "Maximum acceleration when extruding" +msgstr "射出時の最大加速度" + +#: src/libslic3r/PrintConfig.cpp:1158 +msgid "Maximum acceleration when extruding (M204 S)" +msgstr "射出時の最大加速度 (M204 S)" + +#: src/libslic3r/PrintConfig.cpp:1142 src/libslic3r/PrintConfig.cpp:1144 +msgid "Maximum acceleration when retracting" +msgstr "吸込み中の最大加速度" + +#: src/libslic3r/PrintConfig.cpp:1169 +msgid "Maximum acceleration when retracting (M204 T)" +msgstr "吸込み時の最大加速度(M204 T)" + +#: src/libslic3r/PrintConfig.cpp:1096 +msgid "Maximum acceleration X" +msgstr "X軸の最大加速度" + +#: src/libslic3r/PrintConfig.cpp:1097 +msgid "Maximum acceleration Y" +msgstr "Y最大加速度" + +#: src/libslic3r/PrintConfig.cpp:1098 +msgid "Maximum acceleration Z" +msgstr "Zの最大加速度" + +#: src/slic3r/GUI/Tab.cpp:2170 +msgid "Maximum accelerations" +msgstr "最大加速度" + +#: src/libslic3r/PrintConfig.cpp:2424 src/libslic3r/PrintConfig.cpp:2425 +msgid "Maximum exposure time" +msgstr "最長露光時間" + +#: src/libslic3r/PrintConfig.cpp:1081 +msgid "Maximum feedrate E" +msgstr "E最大送り量" + +#: src/libslic3r/PrintConfig.cpp:1087 +msgid "Maximum feedrate of the E axis" +msgstr "E軸の最大送り速度" + +#: src/libslic3r/PrintConfig.cpp:1084 +msgid "Maximum feedrate of the X axis" +msgstr "最大X軸送り速度" + +#: src/libslic3r/PrintConfig.cpp:1085 +msgid "Maximum feedrate of the Y axis" +msgstr "Y軸の最大送り速度" + +#: src/libslic3r/PrintConfig.cpp:1086 +msgid "Maximum feedrate of the Z axis" +msgstr "Z軸最大送り量" + +#: src/libslic3r/PrintConfig.cpp:1078 +msgid "Maximum feedrate X" +msgstr "最大送り速度X" + +#: src/libslic3r/PrintConfig.cpp:1079 +msgid "Maximum feedrate Y" +msgstr "Yの最大送り量" + +#: src/libslic3r/PrintConfig.cpp:1080 +msgid "Maximum feedrate Z" +msgstr "Zの最大送り量" + +#: src/slic3r/GUI/Tab.cpp:2165 +msgid "Maximum feedrates" +msgstr "最大送り速度" + +#: src/libslic3r/PrintConfig.cpp:2447 src/libslic3r/PrintConfig.cpp:2448 +msgid "Maximum initial exposure time" +msgstr "最大初期露光時間" + +#: src/libslic3r/PrintConfig.cpp:1117 +msgid "Maximum jerk E" +msgstr "Eの最大ジャーク" + +#: src/libslic3r/PrintConfig.cpp:1123 +msgid "Maximum jerk of the E axis" +msgstr "E軸最大ジャーク" + +#: src/libslic3r/PrintConfig.cpp:1120 +msgid "Maximum jerk of the X axis" +msgstr "X軸の最大ジャーク" + +#: src/libslic3r/PrintConfig.cpp:1121 +msgid "Maximum jerk of the Y axis" +msgstr "Y軸の最大ジャーク" + +#: src/libslic3r/PrintConfig.cpp:1122 +msgid "Maximum jerk of the Z axis" +msgstr "Z軸最大ジャーク" + +#: src/libslic3r/PrintConfig.cpp:1114 +msgid "Maximum jerk X" +msgstr "Xの最大ジャーク" + +#: src/libslic3r/PrintConfig.cpp:1115 +msgid "Maximum jerk Y" +msgstr "Yの最大ジャーク" + +#: src/libslic3r/PrintConfig.cpp:1116 +msgid "Maximum jerk Z" +msgstr "Zの最大ジャーク" + +#: src/libslic3r/PrintConfig.cpp:566 +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:3053 +msgid "Merge" +msgstr "マージ" + +#: src/libslic3r/PrintConfig.cpp:2432 +msgid "Merging bridges or pillars into another pillars can increase the radius. Zero means no increase, one means full increase." +msgstr "ブリッジまたはピラーを別のピラーに結合すると、半径が大きくなる可能性があります。 値0は増加なし、値1は最大増加を意味します。" + +#: src/libslic3r/SLAPrint.cpp:71 +msgid "Merging slices and calculating statistics" +msgstr "スライスの合成と見積もりの計算" + +#: src/slic3r/Utils/FixModelByWin10.cpp:248 +msgid "Mesh repair failed." +msgstr "メッシュ修復失敗" + +#: src/libslic3r/PrintConfig.cpp:3120 +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 "ログレベル以下の重大度のメッセージが出力されます。 0:トレース、1:デバッグ、2:情報、3:警告、4:エラー、5:致命的" + +#: src/libslic3r/PrintConfig.cpp:1215 src/libslic3r/PrintConfig.cpp:1224 +msgid "Min" +msgstr "最小" + +#: src/libslic3r/PrintConfig.cpp:1233 +msgid "Min print speed" +msgstr "最低プリント速度" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:53 +msgid "min slic3r version" +msgstr "最小のslic3rバージョン" + +#: src/libslic3r/PrintConfig.cpp:2507 +msgid "Minimal distance of the support points" +msgstr "サポートポイントの最小距離" + +#: src/libslic3r/PrintConfig.cpp:1241 +msgid "Minimal filament extrusion length" +msgstr "フィラメント射出の最小値" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:879 +msgid "Minimal points distance" +msgstr "最小ポイント距離" + +#: src/libslic3r/PrintConfig.cpp:635 +msgid "Minimal purge on wipe tower" +msgstr "ワイプタワーの最小パージ量" + +#: src/libslic3r/PrintConfig.cpp:1442 +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 "スライス処理を高速化しメモリ使用量を低減する目的で、入力ファイルを簡素化するために使用される最小の解像度。 高解像度のモデルで多くの場合は、プリンターの能力以上の情報があります。 単純化しないでファイルの完全な解像度で処理するには、ゼロに設定します。" + +#: src/libslic3r/PrintConfig.cpp:2416 src/libslic3r/PrintConfig.cpp:2417 +msgid "Minimum exposure time" +msgstr "最短露光時間" + +#: src/libslic3r/PrintConfig.cpp:1109 src/libslic3r/PrintConfig.cpp:1111 +msgid "Minimum feedrate when extruding" +msgstr "射出中の最小速度" + +#: src/libslic3r/PrintConfig.cpp:1136 +msgid "Minimum feedrate when extruding (M205 S)" +msgstr "射出時の最小送り速度(M205 S)" + +#: src/slic3r/GUI/Tab.cpp:2182 +msgid "Minimum feedrates" +msgstr "最小送り速度" + +#: src/libslic3r/PrintConfig.cpp:2439 src/libslic3r/PrintConfig.cpp:2440 +msgid "Minimum initial exposure time" +msgstr "最小初期露光時間" + +#: src/libslic3r/PrintConfig.cpp:1452 +msgid "Minimum travel after retraction" +msgstr "吸込み後の最小移動量" + +#: src/libslic3r/PrintConfig.cpp:1120 src/libslic3r/PrintConfig.cpp:1122 +msgid "Minimum travel feedrate" +msgstr "最小移動速度" + +#: src/libslic3r/PrintConfig.cpp:1147 +msgid "Minimum travel feedrate (M205 T)" +msgstr "最小移動速度 (M205 T)" + +#: src/slic3r/GUI/Plater.cpp:2946 +msgid "Mirror" +msgstr "ミラー" + +#: src/libslic3r/PrintConfig.cpp:2320 +msgid "Mirror horizontally" +msgstr "水平にミラーリング" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1711 +msgid "Mirror Object" +msgstr "オブジェクトのミラーリング" + +#: src/slic3r/GUI/Plater.cpp:2946 +msgid "Mirror the selected object" +msgstr "選択オブジェクトのミラーリング" + +#: src/slic3r/GUI/Plater.cpp:2939 +msgid "Mirror the selected object along the X axis" +msgstr "選択したオブジェクトをX軸でミラーリングします" + +#: src/slic3r/GUI/Plater.cpp:2941 +msgid "Mirror the selected object along the Y axis" +msgstr "選択オブジェクトをY軸に沿ってミラーリング" + +#: src/slic3r/GUI/Plater.cpp:2943 +msgid "Mirror the selected object along the Z axis" +msgstr "選択したオブジェクトをZ軸に沿ってミラーリングします" + +#: src/libslic3r/PrintConfig.cpp:2327 +msgid "Mirror vertically" +msgstr "垂直にミラーリング" + +#: src/slic3r/Utils/OctoPrint.cpp:69 +#, c-format +msgid "Mismatched type of print host: %s" +msgstr "プリントホストのタイプの不一致:%s" + +#: src/libslic3r/GCode/PreviewData.cpp:176 +msgid "Mixed" +msgstr "ミックス" + +#: src/slic3r/GUI/BedShapeDialog.cpp:87 src/slic3r/GUI/ConfigWizard.cpp:118 +#: src/slic3r/GUI/ConfigWizard.cpp:561 src/slic3r/GUI/ConfigWizard.cpp:575 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:150 +#: src/slic3r/GUI/RammingChart.cpp:81 src/slic3r/GUI/WipeTowerDialog.cpp:84 +#: src/libslic3r/PrintConfig.cpp:59 src/libslic3r/PrintConfig.cpp:66 +#: src/libslic3r/PrintConfig.cpp:75 src/libslic3r/PrintConfig.cpp:210 +#: src/libslic3r/PrintConfig.cpp:285 src/libslic3r/PrintConfig.cpp:293 +#: src/libslic3r/PrintConfig.cpp:343 src/libslic3r/PrintConfig.cpp:353 +#: src/libslic3r/PrintConfig.cpp:473 src/libslic3r/PrintConfig.cpp:484 +#: src/libslic3r/PrintConfig.cpp:502 src/libslic3r/PrintConfig.cpp:680 +#: src/libslic3r/PrintConfig.cpp:1166 src/libslic3r/PrintConfig.cpp:1227 +#: src/libslic3r/PrintConfig.cpp:1245 src/libslic3r/PrintConfig.cpp:1263 +#: src/libslic3r/PrintConfig.cpp:1315 src/libslic3r/PrintConfig.cpp:1325 +#: src/libslic3r/PrintConfig.cpp:1446 src/libslic3r/PrintConfig.cpp:1454 +#: src/libslic3r/PrintConfig.cpp:1495 src/libslic3r/PrintConfig.cpp:1503 +#: src/libslic3r/PrintConfig.cpp:1513 src/libslic3r/PrintConfig.cpp:1521 +#: src/libslic3r/PrintConfig.cpp:1529 src/libslic3r/PrintConfig.cpp:1612 +#: src/libslic3r/PrintConfig.cpp:1828 src/libslic3r/PrintConfig.cpp:1898 +#: src/libslic3r/PrintConfig.cpp:1932 src/libslic3r/PrintConfig.cpp:2125 +#: src/libslic3r/PrintConfig.cpp:2132 src/libslic3r/PrintConfig.cpp:2139 +#: src/libslic3r/PrintConfig.cpp:2169 src/libslic3r/PrintConfig.cpp:2179 +#: src/libslic3r/PrintConfig.cpp:2189 src/libslic3r/PrintConfig.cpp:2297 +#: src/libslic3r/PrintConfig.cpp:2372 src/libslic3r/PrintConfig.cpp:2381 +#: src/libslic3r/PrintConfig.cpp:2390 src/libslic3r/PrintConfig.cpp:2400 +#: src/libslic3r/PrintConfig.cpp:2444 src/libslic3r/PrintConfig.cpp:2454 +#: src/libslic3r/PrintConfig.cpp:2473 src/libslic3r/PrintConfig.cpp:2483 +#: src/libslic3r/PrintConfig.cpp:2492 src/libslic3r/PrintConfig.cpp:2510 +#: src/libslic3r/PrintConfig.cpp:2525 src/libslic3r/PrintConfig.cpp:2539 +#: src/libslic3r/PrintConfig.cpp:2552 src/libslic3r/PrintConfig.cpp:2562 +msgid "mm" +msgstr "mm" + +#: src/libslic3r/PrintConfig.cpp:1477 src/libslic3r/PrintConfig.cpp:1486 +msgid "mm (zero to disable)" +msgstr "mm (0で無効化)" + +#: src/libslic3r/PrintConfig.cpp:847 src/libslic3r/PrintConfig.cpp:992 +#: src/libslic3r/PrintConfig.cpp:1797 +msgid "mm or %" +msgstr "mmまたは%" + +#: src/libslic3r/PrintConfig.cpp:430 src/libslic3r/PrintConfig.cpp:856 +#: src/libslic3r/PrintConfig.cpp:1651 src/libslic3r/PrintConfig.cpp:1702 +#: src/libslic3r/PrintConfig.cpp:1908 src/libslic3r/PrintConfig.cpp:2035 +msgid "mm/s or %" +msgstr "mm/s または %" + +#: src/libslic3r/PrintConfig.cpp:160 src/libslic3r/PrintConfig.cpp:303 +#: src/libslic3r/PrintConfig.cpp:815 src/libslic3r/PrintConfig.cpp:936 +#: src/libslic3r/PrintConfig.cpp:1089 src/libslic3r/PrintConfig.cpp:1134 +#: src/libslic3r/PrintConfig.cpp:1145 src/libslic3r/PrintConfig.cpp:1334 +msgid "mm/s²" +msgstr "mm/s²" + +#: src/libslic3r/PrintConfig.cpp:640 +msgid "mm³" +msgstr "mm³" + +#: src/libslic3r/PrintConfig.cpp:569 src/libslic3r/PrintConfig.cpp:1185 +msgid "mm³/s" +msgstr "mm³/s" + +#: src/libslic3r/PrintConfig.cpp:1197 src/libslic3r/PrintConfig.cpp:1208 +msgid "mm³/s²" +msgstr "mm³/s²" + +#: src/slic3r/GUI/GUI_App.cpp:681 +msgid "Mode" +msgstr "モード" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:58 +msgid "model" +msgstr "モデル" + +#: src/slic3r/GUI/BedShapeDialog.cpp:239 +msgid "Model" +msgstr "モデル" + +#: src/slic3r/Utils/FixModelByWin10.cpp:340 +msgid "Model fixing" +msgstr "モデル修正" + +#: src/slic3r/Utils/FixModelByWin10.cpp:423 +#: src/slic3r/Utils/FixModelByWin10.cpp:426 +msgid "Model Repair by the Netfabb service" +msgstr "Netfabbサービスによるモデル修復" + +#: src/slic3r/Utils/FixModelByWin10.cpp:406 +msgid "Model repair canceled" +msgstr "モデルの修復を中止しました" + +#: src/slic3r/Utils/FixModelByWin10.cpp:426 +msgid "Model repair failed:" +msgstr "モデルの修正に失敗しました:" + +#: src/slic3r/Utils/FixModelByWin10.cpp:400 +msgid "Model repair finished" +msgstr "モデル修正完了" + +#: src/slic3r/Utils/FixModelByWin10.cpp:423 +msgid "Model repaired successfully" +msgstr "モデルの修復完了" + +#: src/slic3r/GUI/Preset.cpp:207 +msgid "modified" +msgstr "変更あり" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2547 +msgid "Modifier" +msgstr "個別条件領域" + +#: src/slic3r/GUI/Tab.cpp:1100 +msgid "Modifiers" +msgstr "個別条件領域" + +#: src/libslic3r/PrintConfig.cpp:719 +msgid "money/kg" +msgstr "コスト/kg" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1240 +msgid "Mouse wheel" +msgstr "マウスホイール" + +#: src/slic3r/GUI/Gizmos/GLGizmoMove.cpp:51 +msgid "Move" +msgstr "移動" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1238 +msgid "Move clipping plane" +msgstr "クリッピングプレーンを移動する" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:183 +msgid "Move current slider thumb Down" +msgstr "現在のスライダーのつまみを下に移動" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:182 +msgid "Move current slider thumb Up" +msgstr "現在のスライダーのつまみを上に移動" + +#: src/slic3r/GUI/GLCanvas3D.cpp:2872 +msgid "Move Object" +msgstr "オブジェクト移動" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1231 +msgid "Move point" +msgstr "移動ポイント" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1183 +msgid "Move support point" +msgstr "サポートポイントの移動" + +#: src/libslic3r/PrintConfig.cpp:2100 +msgid "Multi material printers may need to prime or purge extruders on tool changes. Extrude the excess material into the wipe tower." +msgstr "マルチマテリアルプリンターでは、ツール変更時にエクストルーダーの試し出しまたはパージが必要になる場合があります。 余分な材料をワイプタワーに射出します。" + +#: src/slic3r/GUI/Plater.cpp:1661 src/slic3r/GUI/Plater.cpp:1769 +msgid "Multi-part object detected" +msgstr "マルチパートオブジェクトを検出" + +#: src/slic3r/GUI/FirmwareDialog.cpp:400 src/slic3r/GUI/FirmwareDialog.cpp:436 +#, c-format +msgid "Multiple %s devices found. Please only connect one at a time for flashing." +msgstr "複数の%sデバイスが見つかりました。 更新するには一度に1つずつ接続してください。" + +#: src/slic3r/GUI/Tab.cpp:1118 +msgid "Multiple Extruders" +msgstr "複数のエクストルーダー" + +#: src/slic3r/GUI/Plater.cpp:1766 +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 "" +"マルチマテリアルプリンター用に複数のオブジェクトがロードされました。\n" +"これらは複数のオブジェクトではなく、\n" +"複数のパーツからなる単一のオブジェクトとしますか?" + +#: src/libslic3r/PrintConfig.cpp:3050 +msgid "Multiply copies by creating a grid." +msgstr "グリッドを作成して複数コピーします。" + +#: src/libslic3r/PrintConfig.cpp:3045 +msgid "Multiply copies by this factor." +msgstr "この係数で複数コピーします" + +#: src/slic3r/GUI/Field.cpp:139 +msgid "N/A" +msgstr "N/A" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:176 +msgid "Name" +msgstr "名前" + +#: src/libslic3r/PrintConfig.cpp:1418 +msgid "Name of the printer variant. For example, the printer variants may be differentiated by a nozzle diameter." +msgstr "プリンターバリエーションの名前。 たとえば、プリンターのバリエーションは、ノズル径によって区別されます。" + +#: src/libslic3r/PrintConfig.cpp:1412 +msgid "Name of the printer vendor." +msgstr "プリンターメーカーの名前" + +#: src/libslic3r/PrintConfig.cpp:1009 +msgid "Name of the profile, from which this profile inherits." +msgstr "このプロファイルが継承するプロファイルの名前。" + +#: src/libslic3r/PrintConfig.cpp:1560 +msgid "Nearest" +msgstr "近傍" + +#: src/slic3r/GUI/BonjourDialog.cpp:55 +msgid "Network lookup" +msgstr "ネットワーク調査" + +#: src/slic3r/GUI/Plater.cpp:2056 +msgid "New Project" +msgstr "新プロジェクト" + +#: src/slic3r/GUI/UpdateDialogs.cpp:30 +#, c-format +msgid "New version of %s is available" +msgstr "新バージョン%sがあります" + +#: src/slic3r/GUI/UpdateDialogs.cpp:47 +msgid "New version:" +msgstr "新バージョン:" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3750 +msgid "Next Redo action: %1%" +msgstr "次のやり直し:%1%" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3718 +msgid "Next Undo action: %1%" +msgstr "次の取り消しアクション:%1%" + +#: src/libslic3r/PrintConfig.cpp:912 +msgid "No extrusion" +msgstr "射出なし" + +#: src/slic3r/GUI/MainFrame.cpp:635 +msgid "No previously sliced file." +msgstr "以前にスライスされたファイルはありません。" + +#: src/slic3r/GUI/RammingChart.cpp:23 +msgid "NO RAMMING AT ALL" +msgstr "ラミングなし" + +#: src/libslic3r/PrintConfig.cpp:2509 +msgid "No support points will be placed closer than this threshold." +msgstr "このしきい値よりも近くにサポートポイントは配置されません。" + +#: src/slic3r/GUI/ConfigWizard.cpp:190 src/slic3r/GUI/Plater.cpp:422 +#: src/libslic3r/GCode/PreviewData.cpp:162 +msgid "None" +msgstr "なし" + +#: src/slic3r/GUI/Tab.cpp:2152 +msgid "Normal" +msgstr "ノーマル" + +#: src/slic3r/GUI/Plater.cpp:1073 +msgid "normal mode" +msgstr "通常モード" + +#: src/libslic3r/Zipper.cpp:49 +msgid "not a ZIP archive" +msgstr "ZIPアーカイブではありません" + +#: src/slic3r/Utils/OctoPrint.cpp:90 +msgid "Note: OctoPrint version at least 1.1.0 is required." +msgstr "注:OctoPrintのバージョンは1.1.0以上が必要です" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1213 +msgid "Note: some shortcuts work in (non)editing mode only." +msgstr "注:一部のショートカットは編集モードでは使えません。" + +#: src/slic3r/GUI/Tab.cpp:1193 src/slic3r/GUI/Tab.cpp:1194 +#: src/slic3r/GUI/Tab.cpp:1576 src/slic3r/GUI/Tab.cpp:1577 +#: src/slic3r/GUI/Tab.cpp:1985 src/slic3r/GUI/Tab.cpp:1986 +#: src/slic3r/GUI/Tab.cpp:2079 src/slic3r/GUI/Tab.cpp:2080 +#: src/slic3r/GUI/Tab.cpp:3328 src/slic3r/GUI/Tab.cpp:3329 +msgid "Notes" +msgstr "メモ" + +#: src/slic3r/GUI/GUI.cpp:277 +msgid "Notice" +msgstr "通知" + +#: src/slic3r/GUI/ConfigWizard.cpp:118 +msgid "nozzle" +msgstr "ノズル" + +#: src/libslic3r/PrintConfig.cpp:1261 +msgid "Nozzle diameter" +msgstr "ノズル径" + +#: src/slic3r/GUI/ConfigWizard.cpp:560 +msgid "Nozzle Diameter:" +msgstr "ノズル径:" + +#: src/libslic3r/PrintConfig.cpp:618 +msgid "Number of cooling moves" +msgstr "冷却移動回数" + +#: src/slic3r/GUI/Tab.cpp:1845 +msgid "Number of extruders of the printer." +msgstr "プリンターのエクストルーダー数" + +#: src/libslic3r/PrintConfig.cpp:1888 +msgid "Number of interface layers to insert between the object(s) and support material." +msgstr "オブジェクトとサポート材の間に挿入するインターフェイスレイヤーの数。" + +#: src/libslic3r/PrintConfig.cpp:1627 +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:2214 +msgid "Number of pixels in" +msgstr "ピクセル数" + +#: src/libslic3r/PrintConfig.cpp:2216 +msgid "Number of pixels in X" +msgstr "Xのピクセル数" + +#: src/libslic3r/PrintConfig.cpp:2222 +msgid "Number of pixels in Y" +msgstr "Yのピクセル数" + +#: src/libslic3r/PrintConfig.cpp:151 +msgid "Number of solid layers to generate on bottom surfaces." +msgstr "底部のソリッドレイヤー(塗りつぶし)数" + +#: src/libslic3r/PrintConfig.cpp:1711 +msgid "Number of solid layers to generate on top and bottom surfaces." +msgstr "上部と底部のソリッドレイヤー(塗りつぶし)数" + +#: src/libslic3r/PrintConfig.cpp:2045 +msgid "Number of solid layers to generate on top surfaces." +msgstr "上部に生成するソリッドレイヤー(塗りつぶし)数。" + +#: src/libslic3r/PrintConfig.cpp:2303 +msgid "Number of the layers needed for the exposure time fade from initial exposure time to the exposure time" +msgstr "初期露光時間から露光時間に移行するために必要なレイヤーの数" + +#: src/slic3r/GUI/Plater.cpp:218 +msgid "Number of tool changes" +msgstr "ツールチェンジ回数" + +#: src/libslic3r/PrintConfig.cpp:2489 +msgid "Object elevation" +msgstr "オブジェクトの持ち上げ高" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1858 +msgid "Object manipulation" +msgstr "オブジェクト操作" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:27 +msgid "Object Manipulation" +msgstr "オブジェクト操作" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:78 +msgid "Object name" +msgstr "オブジェクト名" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2412 +msgid "Object or Instance" +msgstr "オブジェクトまたはインスタンス" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1010 +msgid "Object reordered" +msgstr "オブジェクト順序変更" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1868 +msgid "Object Settings to modify" +msgstr "オブジェクト設定を変更" + +#: src/slic3r/GUI/Plater.cpp:1875 +msgid "Object too large?" +msgstr "オブジェクトが大きすぎませんか?" + +#: src/libslic3r/PrintConfig.cpp:2161 +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/slic3r/GUI/Plater.cpp:1018 +msgid "object(s)" +msgstr "オブジェクト" + +#: src/slic3r/GUI/Plater.cpp:1043 src/slic3r/GUI/Plater.cpp:1058 +msgid "objects" +msgstr "オブジェクト" + +#: src/libslic3r/PrintConfig.cpp:402 src/libslic3r/PrintConfig.cpp:808 +msgid "Octagram Spiral" +msgstr "オクタグラムスパイラル" + +#: src/slic3r/GUI/BonjourDialog.cpp:76 +msgid "OctoPrint version" +msgstr "OctoPrintバージョン" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2419 +msgid "of a current Object" +msgstr "現在のオブジェクトの" + +#: src/slic3r/GUI/wxExtensions.cpp:2570 +msgid "One layer mode" +msgstr "1レイヤーモード" + +#: src/libslic3r/Print.cpp:1285 +msgid "One or more object were assigned an extruder that the printer does not have." +msgstr "1つ以上のオブジェクトに、プリンターにないエクストルーダーが割り当てられました。" + +#: src/libslic3r/PrintConfig.cpp:1817 src/libslic3r/PrintConfig.cpp:2425 +msgid "Only create support if it lies on a build plate. Don't create support on a print." +msgstr "ビルドプレート(ベッド)上からのみサポートを作成します。プリントしたモデル上からはサポートを生成しません。" + +#: src/libslic3r/PrintConfig.cpp:978 +msgid "Only infill where needed" +msgstr "必要な場合のみインフィルを付ける" + +#: src/slic3r/GUI/Tab.cpp:2271 +msgid "Only lift Z" +msgstr "Zをリフト" + +#: src/libslic3r/PrintConfig.cpp:1500 +msgid "Only lift Z above" +msgstr "これ以上でリフトZ" + +#: src/libslic3r/PrintConfig.cpp:1509 +msgid "Only lift Z below" +msgstr "Zリフト以下" + +#: src/libslic3r/PrintConfig.cpp:1279 +msgid "Only retract when crossing perimeters" +msgstr "外周をまたぐときだけ吸込み" + +#: src/slic3r/GUI/Tab.cpp:1126 +msgid "Ooze prevention" +msgstr "垂れ出し抑止" + +#: src/libslic3r/Print.cpp:1193 +msgid "Ooze prevention is currently not supported with the wipe tower enabled." +msgstr "垂れ防止機能は、現在のところ、ワイプタワーを有効にした状態では使えません。" + +#: src/slic3r/GUI/MainFrame.cpp:339 +msgid "Open a project file" +msgstr "プロジェクトファイルを開く" + +#: src/slic3r/GUI/Tab.cpp:1745 +msgid "Open CA certificate file" +msgstr "CA証明書ファイルを開く" + +#: src/slic3r/GUI/UpdateDialogs.cpp:63 src/slic3r/GUI/UpdateDialogs.cpp:126 +msgid "Open changelog page" +msgstr "変更ログページを開く" + +#: src/slic3r/GUI/UpdateDialogs.cpp:68 +msgid "Open download page" +msgstr "ダウンロードページを開きます" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:105 +msgid "Open project STL/OBJ/AMF/3MF with config, delete bed" +msgstr "設定とSTL/OBJ/AMF/3MFを開きます(プリント領域を削除します)" + +#: src/slic3r/GUI/MainFrame.cpp:551 +#, c-format +msgid "Open the %s website in your browser" +msgstr "ブラウザで%sウェブサイトを開きます" + +#: src/slic3r/GUI/MainFrame.cpp:542 +msgid "Open the Prusa3D drivers download page in your browser" +msgstr "ブラウザーでPrusa3Dドライバのダウンロードページを開きます" + +#: src/slic3r/GUI/MainFrame.cpp:549 +msgid "Open the software releases page in your browser" +msgstr "ブラウザでソフトウェアリリースページを開きます" + +#: src/slic3r/GUI/Plater.cpp:2994 +msgid "Optimize orientation" +msgstr "向きを最適化する" + +#: src/slic3r/GUI/Plater.cpp:2643 +msgid "Optimize Rotation" +msgstr "回転の最適化" + +#: src/slic3r/GUI/Plater.cpp:2994 +msgid "Optimize the rotation of the object for better print results." +msgstr "プリント結果をよくするために、オブジェクトの回転を最適化します。" + +#: src/libslic3r/PrintConfig.cpp:112 +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 "外周壁との交差が最小限になるように、射出していないときのノズルの移動を最適化します。これは特に垂れやすいボーデン型エクストルーダーで効果があります。ただし、この機能はプリントとGコード生成が遅くなります。" + +#: src/slic3r/GUI/Tab.cpp:1070 +msgid "Options for support material and raft" +msgstr "サポート材とラフトのオプション" + +#: src/slic3r/GUI/Plater.cpp:2251 +msgid "Orientation found." +msgstr "オリエンテーションが見つかりました" + +#: src/slic3r/GUI/Plater.cpp:2768 +msgid "Orientation search canceled." +msgstr "オリエンテーション検索がキャンセルされました。" + +#: src/slic3r/GUI/BedShapeDialog.cpp:79 +msgid "Origin" +msgstr "原点" + +#: src/slic3r/GUI/Tab.cpp:1165 +msgid "Other" +msgstr "その他" + +#: src/libslic3r/PrintConfig.cpp:119 src/libslic3r/PrintConfig.cpp:1977 +msgid "Other layers" +msgstr "他のレイヤー" + +#: src/slic3r/GUI/ConfigWizard.cpp:438 +msgid "Other Vendors" +msgstr "他のベンダー" + +#: src/slic3r/GUI/Tab.cpp:1180 src/slic3r/GUI/Tab.cpp:3440 +msgid "Output file" +msgstr "出力ファイル" + +#: src/libslic3r/PrintConfig.cpp:3104 +msgid "Output File" +msgstr "ファイル出力" + +#: src/libslic3r/PrintConfig.cpp:1294 +msgid "Output filename format" +msgstr "出力ファイル名の形式" + +#: src/libslic3r/PrintConfig.cpp:2992 +msgid "Output Model Info" +msgstr "モデル情報のアウトプット" + +#: src/slic3r/GUI/Tab.cpp:1168 src/slic3r/GUI/Tab.cpp:3439 +msgid "Output options" +msgstr "出力オプション" + +#: src/slic3r/GUI/GUI_Preview.cpp:229 src/libslic3r/GCode/PreviewData.cpp:165 +msgid "Overhang perimeter" +msgstr "オーバーハング外周" + +#: src/libslic3r/PrintConfig.cpp:1955 +msgid "Overhang threshold" +msgstr "オーバハングのしきい値" + +#: src/slic3r/GUI/Tab.cpp:1153 +msgid "Overlap" +msgstr "オーバーラップ" + +#: src/slic3r/GUI/MainFrame.cpp:478 +msgid "P&rint Settings Tab" +msgstr "プリント設定タブ" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:79 src/slic3r/GUI/GUI_ObjectList.cpp:520 +#: src/slic3r/GUI/Tab.cpp:3425 src/slic3r/GUI/Tab.cpp:3426 +#: src/libslic3r/PrintConfig.cpp:2516 src/libslic3r/PrintConfig.cpp:2523 +#: src/libslic3r/PrintConfig.cpp:2537 src/libslic3r/PrintConfig.cpp:2547 +#: src/libslic3r/PrintConfig.cpp:2560 src/libslic3r/PrintConfig.cpp:2569 +msgid "Pad" +msgstr "パッド" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:39 +msgid "Pad and Support" +msgstr "パッドとサポート" + +#: src/libslic3r/PrintConfig.cpp:2732 +msgid "Pad around object" +msgstr "オブジェクト周りにパッド" + +#: src/libslic3r/PrintConfig.cpp:2731 +msgid "Pad object connector penetration" +msgstr "パッドオブジェクトコネクタの貫通" + +#: src/libslic3r/PrintConfig.cpp:2711 +msgid "Pad object connector stride" +msgstr "パッドオブジェクトコネクタのピッチ" + +#: src/libslic3r/PrintConfig.cpp:2721 +msgid "Pad object connector width" +msgstr "パッドオブジェクトの接続幅" + +#: src/libslic3r/PrintConfig.cpp:2700 +msgid "Pad object gap" +msgstr "パッドオブジェクトのギャップ" + +#: src/libslic3r/PrintConfig.cpp:2532 +msgid "Pad wall height" +msgstr "パッド壁の高さ" + +#: src/libslic3r/PrintConfig.cpp:2568 +msgid "Pad wall slope" +msgstr "側壁の傾斜" + +#: src/libslic3r/PrintConfig.cpp:2522 +msgid "Pad wall thickness" +msgstr "台座の壁の厚さ" + +#: src/slic3r/GUI/Field.cpp:108 +msgid "parameter name" +msgstr "パラメータ名" + +#: src/slic3r/GUI/Field.cpp:184 +msgid "Parameter validation" +msgstr "パラメータ検証" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2412 +msgid "Part" +msgstr "パート" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1881 +msgid "Part manipulation" +msgstr "部品操作" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1872 +msgid "Part Settings to modify" +msgstr "変更するパーツ設定" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3449 +msgid "Paste" +msgstr "ペースト" + +#: src/slic3r/GUI/MainFrame.cpp:456 +msgid "Paste clipboard" +msgstr "クリップボードからペースト" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:140 +msgid "Paste from clipboard" +msgstr "クリップボードからペースト" + +#: src/slic3r/GUI/Plater.cpp:4772 +msgid "Paste From Clipboard" +msgstr "クリップボードからの貼り付け" + +#: src/libslic3r/PrintConfig.cpp:1915 +msgid "Pattern" +msgstr "パターン" + +#: src/libslic3r/PrintConfig.cpp:1805 +msgid "Pattern angle" +msgstr "パターン角" + +#: src/libslic3r/PrintConfig.cpp:1929 +msgid "Pattern spacing" +msgstr "パターンの間隔" + +#: src/libslic3r/PrintConfig.cpp:1917 +msgid "Pattern used to generate support material." +msgstr "サポートの生成用のパターン。" + +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:36 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:198 +msgid "Perform cut" +msgstr "カットする" + +#: src/slic3r/GUI/GUI_Preview.cpp:227 src/libslic3r/GCode/PreviewData.cpp:163 +msgid "Perimeter" +msgstr "外周" + +#: src/libslic3r/PrintConfig.cpp:1339 +msgid "Perimeter extruder" +msgstr "外周エクストルーダー" + +#: src/slic3r/GUI/PresetHints.cpp:162 +msgid "perimeters" +msgstr "外周" + +#: src/libslic3r/PrintConfig.cpp:1330 src/libslic3r/PrintConfig.cpp:1348 +#: src/libslic3r/PrintConfig.cpp:1360 src/libslic3r/PrintConfig.cpp:1370 +msgid "Perimeters" +msgstr "外周" + +#: src/slic3r/GUI/ConfigWizard.cpp:440 +#, c-format +msgid "Pick another vendor supported by %s:" +msgstr "%sがサポートする別のメーカーを選択:" + +#: src/libslic3r/PrintConfig.cpp:2430 +msgid "Pillar widening factor" +msgstr "柱の太さ係数" + +#: src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp:32 +msgid "Place on face" +msgstr "ベッド上に配置" + +#: src/slic3r/GUI/MainFrame.cpp:161 +msgid "Plater" +msgstr "プレート" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:156 +msgid "Plater Shortcuts" +msgstr "プレートショートカット" + +#: src/slic3r/GUI/GUI.cpp:143 +msgid "Please check and fix your object list." +msgstr "オブジェクトリストを確認して修正してください。" + +#: src/slic3r/GUI/Tab.cpp:2797 +msgid "Please check your object list before preset changing." +msgstr "プリセットを変更する前にオブジェクトリストを確認してください。" + +#: src/slic3r/GUI/AboutDialog.cpp:39 src/slic3r/GUI/AboutDialog.cpp:286 +msgid "Portions copyright" +msgstr "一部の著作権" + +#: src/libslic3r/PrintConfig.cpp:2235 +msgid "Portrait" +msgstr "ポートレート" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:150 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:193 +msgid "Position" +msgstr "位置" + +#: src/slic3r/GUI/Tab.cpp:2265 +msgid "Position (for multi-extruder printers)" +msgstr "ポジション(マルチエクストルーダーの場合)" + +#: src/slic3r/GUI/Gizmos/GLGizmoMove.cpp:177 +msgid "Position (mm)" +msgstr "位置(mm)" + +#: src/libslic3r/PrintConfig.cpp:1553 +msgid "Position of perimeters starting points." +msgstr "外周プリントの開始点" + +#: src/libslic3r/PrintConfig.cpp:2123 +msgid "Position X" +msgstr "X位置" + +#: src/libslic3r/PrintConfig.cpp:2130 +msgid "Position Y" +msgstr "Yポジション" + +#: src/slic3r/GUI/Tab.cpp:1187 src/libslic3r/PrintConfig.cpp:1383 +msgid "Post-processing scripts" +msgstr "ポストプロセス・スクリプト" + +#: src/slic3r/GUI/MainFrame.cpp:489 +msgid "Pre&view" +msgstr "プレビュー" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:121 src/slic3r/GUI/Preferences.cpp:10 +msgid "Preferences" +msgstr "環境設定" + +#: src/libslic3r/PrintConfig.cpp:1571 +msgid "Preferred direction of the seam" +msgstr "シームの優先方向" + +#: src/libslic3r/PrintConfig.cpp:1582 +msgid "Preferred direction of the seam - jitter" +msgstr "シームの優先方向ージッター" + +#: src/libslic3r/PrintObject.cpp:251 +msgid "Preparing infill" +msgstr "インフィルの準備" + +#: src/slic3r/GUI/Tab.cpp:2758 +#, c-format +msgid "Preset (%s)" +msgstr "プリセット(%s)" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:147 +msgid "" +"Press to activate deselection rectangle\n" +"or to scale or rotate selected objects\n" +"around their own center" +msgstr "クリックして四角形選択解除をアクティブにするか、選択したオブジェクトをその中心基点で縮尺または回転します" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:148 +msgid "Press to activate one direction scaling in Gizmo scale" +msgstr "クリックして一方向だけギズモサイズを変更します" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:146 +#, 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%サイズ変更ステップ\n" +"または1mm刻み" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:147 +msgid "" +"Press to scale selection to fit print volume\n" +"in Gizmo scale" +msgstr "押すと、選択を拡大縮小し、プリントボリュームをギズモサイズに合わせます" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:127 +msgid "Press to select multiple object or move multiple object with mouse" +msgstr "マウスで複数のオブジェクトを選択するか移動します" + +#: src/slic3r/GUI/Tab.cpp:2288 +msgid "Preview" +msgstr "プレビュー" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:176 +msgid "Preview Shortcuts" +msgstr "プレビューのショートカット" + +#: src/slic3r/GUI/MainFrame.cpp:641 +msgid "Previously sliced file (" +msgstr "以前のスライスファイル(" + +#: src/libslic3r/PrintConfig.cpp:1773 +msgid "Prime all printing extruders" +msgstr "全てのエクストルーダーでプライムを実施" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:46 src/slic3r/GUI/Preset.cpp:1274 +msgid "print" +msgstr "プリント" + +#: src/slic3r/GUI/MainFrame.cpp:510 +msgid "Print &Host Upload Queue" +msgstr "プリントサーバーアップロードキュー" + +#: src/libslic3r/PrintConfig.cpp:439 +msgid "Print contour perimeters from the outermost one to the innermost one instead of the default inverse order." +msgstr "デフォルトの順ではなく、外周から始めて内周へとプリントします。" + +#: src/slic3r/GUI/ConfigWizard.cpp:541 +msgid "Print Diameters" +msgstr "各種直径" + +#: src/slic3r/GUI/Tab.cpp:1917 src/slic3r/GUI/Tab.cpp:2074 +msgid "Print Host upload" +msgstr "プリントサーバーアップロード" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:122 +#: src/slic3r/GUI/PrintHostDialogs.cpp:135 +msgid "Print host upload queue" +msgstr "プリントサーバーのアップロードキュー" + +#: src/slic3r/GUI/Tab.hpp:317 src/slic3r/GUI/Tab.hpp:405 +msgid "Print Settings" +msgstr "プリント設定" + +#: src/slic3r/GUI/Plater.cpp:681 +msgid "Print settings" +msgstr "プリント設定" + +#: src/slic3r/GUI/Tab.cpp:1520 +msgid "Print speed override" +msgstr "プリント速度上書き" + +#: src/slic3r/GUI/MainFrame.cpp:483 +msgid "Print&er Settings Tab" +msgstr "プリンター設定タブ" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1438 +msgid "Printable" +msgstr "プリント可" + +#: src/slic3r/GUI/Plater.cpp:685 +msgid "Printer" +msgstr "プリンター" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:48 src/slic3r/GUI/Preset.cpp:1278 +msgid "printer" +msgstr "プリンター" + +#: src/libslic3r/PrintConfig.cpp:2274 src/libslic3r/PrintConfig.cpp:2275 +msgid "Printer absolute correction" +msgstr "絶対的なプリンター補正" + +#: src/libslic3r/PrintConfig.cpp:2282 src/libslic3r/PrintConfig.cpp:2283 +msgid "Printer gamma correction" +msgstr "プリンタガンマ補正" + +#: src/slic3r/GUI/Tab.cpp:926 +msgid "printer model" +msgstr "プリンターモデル" + +#: src/libslic3r/PrintConfig.cpp:1402 +msgid "Printer notes" +msgstr "プリンターメモ" + +#: src/libslic3r/PrintConfig.cpp:2266 src/libslic3r/PrintConfig.cpp:2267 +#: src/libslic3r/PrintConfig.cpp:2268 +msgid "Printer scaling correction" +msgstr "プリンタースケーリング補正" + +#: src/slic3r/GUI/Tab.hpp:368 +msgid "Printer Settings" +msgstr "プリンター設定" + +#: src/libslic3r/PrintConfig.cpp:42 src/libslic3r/PrintConfig.cpp:43 +msgid "Printer technology" +msgstr "プリント方式" + +#: src/libslic3r/PrintConfig.cpp:1396 +msgid "Printer type" +msgstr "プリンタータイプ" + +#: src/libslic3r/PrintConfig.cpp:1417 +msgid "Printer variant" +msgstr "プリンターバリエーション" + +#: src/libslic3r/PrintConfig.cpp:1411 +msgid "Printer vendor" +msgstr "プリンターメーカー" + +#: src/libslic3r/Print.cpp:1294 +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 "異なるノズル直径の複数のエクストルーダーでのプリント。 現在のエクストルーダーでサポートをプリントする場合(support_material_extruder == 0またはsupport_material_interface_extruder == 0)、すべてのノズル径を同じにする必要があります。" + +#. TRN "Processing input_file_basename" +#: src/slic3r/GUI/MainFrame.cpp:715 +#, c-format +msgid "Processing %s" +msgstr "%s実行中" + +#: src/slic3r/GUI/Plater.cpp:1600 +#, c-format +msgid "Processing input file %s" +msgstr "入力ファイル%sを処理中" + +#: src/libslic3r/PrintObject.cpp:110 +msgid "Processing triangulated mesh" +msgstr "ポリゴンメッシュ処理" + +#: src/slic3r/GUI/Tab.cpp:1201 src/slic3r/GUI/Tab.cpp:1585 +#: src/slic3r/GUI/Tab.cpp:1993 src/slic3r/GUI/Tab.cpp:2087 +#: src/slic3r/GUI/Tab.cpp:3337 src/slic3r/GUI/Tab.cpp:3446 +msgid "Profile dependencies" +msgstr "プロファイルの依存関係" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:149 +msgid "Progress" +msgstr "進捗" + +#: src/slic3r/GUI/FirmwareDialog.cpp:779 +msgid "Progress:" +msgstr "進度:" + +#: src/slic3r/GUI/MainFrame.cpp:542 +msgid "Prusa 3D &Drivers" +msgstr "Prusa 3D &ドライバー" + +#: src/slic3r/GUI/ConfigWizard.cpp:1109 +msgid "Prusa FFF Technology Printers" +msgstr "Prusa FFF方式プリンター" + +#: src/slic3r/GUI/ConfigWizard.cpp:1112 +msgid "Prusa MSLA Technology Printers" +msgstr "Prusa MSLA方式プリンター" + +#: src/slic3r/GUI/AboutDialog.cpp:255 +msgid "PrusaSlicer is based on Slic3r by Alessandro Ranellucci and the RepRap community." +msgstr "PrusaSlicerは、Alessandro RanellucciとRepRapコミュニティによるSlic3rをベースにしています。" + +#: src/slic3r/GUI/GUI_App.cpp:297 +#, 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 "OpenGLバージョン%s、レンダー%s、ベンダー%sが検出されました。PrusaSlicerには、OpenGL 2.0が機能するグラフィックドライバーが必要です。 " + +#: src/libslic3r/PrintConfig.cpp:2153 +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/slic3r/GUI/Plater.cpp:456 +msgid "Purging volumes" +msgstr "パージ体積" + +#: src/libslic3r/PrintConfig.cpp:2106 +msgid "Purging volumes - load/unload volumes" +msgstr "パージ量-ロード/アンロード時" + +#: src/libslic3r/PrintConfig.cpp:2113 +msgid "Purging volumes - matrix" +msgstr "パージする量−行列" + +#: src/slic3r/GUI/Tab.cpp:1019 +msgid "Quality (slower slicing)" +msgstr "高品質(スライスが遅くなります)" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:854 src/slic3r/GUI/GUI_ObjectList.cpp:1139 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1145 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1377 +#, c-format +msgid "Quick Add Settings (%s)" +msgstr "クイック追加設定(%s)" + +#: src/slic3r/GUI/MainFrame.cpp:383 +msgid "Quick Slice" +msgstr "高速スライス" + +#: src/slic3r/GUI/MainFrame.cpp:389 +msgid "Quick Slice and Save As" +msgstr "クイックスライスと名前を付けて保存" + +#: src/slic3r/GUI/MainFrame.cpp:409 +#, c-format +msgid "Quit %s" +msgstr "%sを終了" + +#: src/libslic3r/PrintConfig.cpp:479 +msgid "Radius" +msgstr "半径" + +#: src/slic3r/GUI/Tab.cpp:1066 +msgid "Raft" +msgstr "ラフト" + +#: src/libslic3r/PrintConfig.cpp:1431 +msgid "Raft layers" +msgstr "ラフトレイヤー" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:14 +msgid "Ramming customization" +msgstr "ラミングのカスタマイズ" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:40 +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 "" +"ラミングとは、単一エクストルーダーMMプリンターでツールを交換する直前の急速吐出動作を指します。 その目的は、フィラメントを抜く時に新しいフィラメントの挿入を妨げないようにすることと、再挿入のときにエラーにならないよう、フィラメントの先端部を適切な形にすることです。 この処理は重要であり、材料が変わると、良好な先端形状が得られるラミング条件の変更が必要となったりします。 このため、ラミング中の吐出速度は調整できるようになっています。\n" +"\n" +"これはエキスパートレベルの設定です。不適切な調整は、ジャムや、ドライブギアがフィラメントを削ったりする可能性があります。" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:90 +msgid "Ramming line spacing" +msgstr "ラミング線間距離" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:88 +msgid "Ramming line width" +msgstr "ラミング線幅" + +#: src/libslic3r/PrintConfig.cpp:662 +msgid "Ramming parameters" +msgstr "ラミングパラメーター" + +#: src/slic3r/GUI/Tab.cpp:1545 +msgid "Ramming settings" +msgstr "ラミング設定" + +#: src/libslic3r/PrintConfig.cpp:1559 +msgid "Random" +msgstr "ランダム" + +#: src/slic3r/GUI/wxExtensions.cpp:486 +msgid "Range" +msgstr "範囲" + +#: src/libslic3r/SLAPrint.cpp:72 +msgid "Rasterizing layers" +msgstr "レイヤーのラスタライズ" + +#: src/slic3r/GUI/UpdateDialogs.cpp:151 +msgid "Re-configure" +msgstr "再構成" + +#: src/slic3r/GUI/FirmwareDialog.cpp:783 +msgid "Ready" +msgstr "準備完了" + +#: src/slic3r/GUI/Plater.cpp:2406 +msgid "Ready to slice" +msgstr "スライス可能" + +#: src/slic3r/GUI/MainFrame.cpp:526 src/libslic3r/PrintConfig.cpp:1562 +msgid "Rear" +msgstr "背面" + +#: src/slic3r/GUI/MainFrame.cpp:526 +msgid "Rear View" +msgstr "背面" + +#: src/slic3r/GUI/MainFrame.cpp:401 +msgid "Recent projects" +msgstr "最近のプロジェクト" + +#: src/slic3r/GUI/PresetHints.cpp:262 +#, c-format +msgid "Recommended object thin wall thickness for layer height %.2f and" +msgstr "レイヤー高さ%.2fでの推奨オブジェクトの薄壁厚と" + +#: src/slic3r/GUI/PresetHints.cpp:247 +msgid "Recommended object thin wall thickness: Not available due to invalid layer height." +msgstr "推奨されるオブジェクトの薄壁の厚さ:レイヤーの高さが無効なため利用できません。" + +#: src/slic3r/GUI/GUI_App.cpp:386 src/slic3r/GUI/GUI_App.cpp:395 +msgid "Recreating" +msgstr "更新" + +#: src/slic3r/GUI/BedShapeDialog.cpp:68 +msgid "Rectangular" +msgstr "四角形" + +#: src/libslic3r/PrintConfig.cpp:398 src/libslic3r/PrintConfig.cpp:796 +#: src/libslic3r/PrintConfig.cpp:1922 +msgid "Rectilinear" +msgstr "直線的" + +#: src/libslic3r/PrintConfig.cpp:1923 +msgid "Rectilinear grid" +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:3497 +#, c-format +msgid "Redo %1$d Action" +msgid_plural "Redo %1$d Actions" +msgstr[0] "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3479 +msgid "Redo History" +msgstr "やり直し履歴" + +#: src/slic3r/GUI/Tab.cpp:1037 +msgid "Reducing printing time" +msgstr "造形時間短縮" + +#: src/slic3r/GUI/Plater.cpp:2924 +msgid "Reload from Disk" +msgstr "ディスクから再読込み" + +#: src/slic3r/GUI/Plater.cpp:2924 +msgid "Reload the selected file from Disk" +msgstr "選択したファイルをディスクから再読み込みします" + +#: src/slic3r/GUI/Preferences.cpp:36 +msgid "Remember output directory" +msgstr "出力ディレクトリを記憶する" + +#: src/slic3r/GUI/Tab.cpp:2935 +msgid "remove" +msgstr "外す" + +#: src/slic3r/GUI/Tab.cpp:2937 +msgid "Remove" +msgstr "除去" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:859 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:912 +msgid "Remove all points" +msgstr "全てのポイントを削除" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3475 +msgid "Remove instance" +msgstr "インスタンス削除" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:125 +msgid "Remove Instance of the selected object" +msgstr "選択したオブジェクトのインスタンスを削除" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:153 +msgid "Remove layer range" +msgstr "レイヤーの範囲を削除します" + +#: src/slic3r/GUI/Plater.cpp:3518 +msgid "Remove one instance of the selected object" +msgstr "選択したオブジェクトのインスタンスを1つ削除します" + +#: src/slic3r/GUI/GUI_ObjectSettings.cpp:83 +msgid "Remove parameter" +msgstr "パラメータを削除" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1230 +msgid "Remove point" +msgstr "ポイント削除" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1233 +msgid "Remove point from selection" +msgstr "選択からポイントを削除" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:855 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1237 +msgid "Remove selected points" +msgstr "選択したポイントを削除" + +#: src/slic3r/GUI/Plater.cpp:2891 src/slic3r/GUI/Plater.cpp:2909 +msgid "Remove the selected object" +msgstr "選択オブジェクトを削除" + +#: src/slic3r/GUI/ConfigWizard.cpp:305 +msgid "Remove user profiles - install from scratch (a snapshot will be taken beforehand)" +msgstr "ユーザープロファイルの削除-最初からインストールします(スナップショットは事前に作成されます)" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1200 +msgid "Rename" +msgstr "名前の変更" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:493 +msgid "Rename Object" +msgstr "オブジェクト名を変更" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:493 +msgid "Rename Sub-object" +msgstr "サブオブジェクトの名前変更" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2709 +msgid "Renaming" +msgstr "名前の変更" + +#: src/libslic3r/PrintConfig.cpp:3125 +msgid "Render with a software renderer" +msgstr "ソフトウェアでレンダリングする" + +#: src/libslic3r/PrintConfig.cpp:3126 +msgid "Render with a software renderer. The bundled MESA software renderer is loaded instead of the default OpenGL driver." +msgstr "ソフトウェアレンダラーでレンダリングします。 デフォルトのOpenGLドライバーの代わりに、バンドルされたMESAソフトウェアレンダラーがロードされます。" + +#: src/slic3r/GUI/MainFrame.cpp:772 src/libslic3r/PrintConfig.cpp:3058 +msgid "Repair" +msgstr "修復" + +#: src/slic3r/Utils/FixModelByWin10.cpp:387 +msgid "Repaired 3MF file contains more than one object" +msgstr "修復された3MFファイルに複数のオブジェクトが含まれています" + +#: src/slic3r/Utils/FixModelByWin10.cpp:391 +msgid "Repaired 3MF file contains more than one volume" +msgstr "修復された3MFファイルに複数のボリュームが含まれています" + +#: src/slic3r/Utils/FixModelByWin10.cpp:385 +msgid "Repaired 3MF file does not contain any object" +msgstr "修正された3MFファイルにはオブジェクトがありません" + +#: src/slic3r/Utils/FixModelByWin10.cpp:389 +msgid "Repaired 3MF file does not contain any volume" +msgstr "修正された3MFファイルにはソリッドボディがありません" + +#: src/slic3r/Utils/FixModelByWin10.cpp:242 +msgid "Repairing model by the Netfabb service" +msgstr "Netfabbでモデルを修復中" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:113 src/slic3r/GUI/MainFrame.cpp:395 +msgid "Repeat last quick slice" +msgstr "最後のクイックスライスを繰り返す" + +#: src/slic3r/GUI/MainFrame.cpp:395 +msgid "Repeat Last Quick Slice" +msgstr "最後のクイックスライスを繰り返す" + +#: src/slic3r/GUI/MainFrame.cpp:561 +msgid "Report an I&ssue" +msgstr "問題を報告する" + +#: src/slic3r/GUI/MainFrame.cpp:561 +#, c-format +msgid "Report an issue on %s" +msgstr "%sに関する問題を報告する" + +#: src/slic3r/Utils/PresetUpdater.cpp:590 +#, c-format +msgid "requires max. %s" +msgstr "最大%sが必要" + +#: src/slic3r/Utils/PresetUpdater.cpp:588 +#, c-format +msgid "requires min. %s" +msgstr "最小%sが必要" + +#: src/slic3r/Utils/PresetUpdater.cpp:583 +#, c-format +msgid "requires min. %s and max. %s" +msgstr "最小%sと最大%sが必要です" + +#: src/slic3r/GUI/FirmwareDialog.cpp:772 +msgid "Rescan" +msgstr "再走査" + +#: src/slic3r/GUI/Tab.cpp:1879 +msgid "Rescan serial ports" +msgstr "シリアルポートの再捜査" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1239 +msgid "Reset clipping plane" +msgstr "切断面をリセットする" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:928 +msgid "Reset direction" +msgstr "方向のリセット" + +#: src/slic3r/GUI/Plater.cpp:2603 +msgid "Reset Project" +msgstr "プロジェクトのリセット" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:303 +msgid "Reset rotation" +msgstr "回転をリセット" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:328 +msgid "Reset Rotation" +msgstr "回転をリセット" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:285 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:290 +msgid "Reset scale" +msgstr "縮尺をリセット" + +#: src/slic3r/GUI/Tab.cpp:2584 +msgid "Reset to Filament Color" +msgstr "フィラメントの色をリセット" + +#: src/libslic3r/PrintConfig.cpp:1441 +msgid "Resolution" +msgstr "解像度" + +#: src/libslic3r/PrintConfig.cpp:1459 +msgid "Retract amount before wipe" +msgstr "ワイプ前に引き込む" + +#: src/libslic3r/PrintConfig.cpp:1467 +msgid "Retract on layer change" +msgstr "レイヤーチェンジ時の待避" + +#: src/slic3r/GUI/Tab.cpp:2268 +msgid "Retraction" +msgstr "リトラクション" + +#: src/libslic3r/PrintConfig.cpp:1453 +msgid "Retraction is not triggered when travel moves are shorter than this length." +msgstr "移動がこの長さより短い場合、吸込み動作を行いません。" + +#: src/libslic3r/PrintConfig.cpp:1474 +msgid "Retraction Length" +msgstr "材料待避長さ" + +#: src/libslic3r/PrintConfig.cpp:1482 +msgid "Retraction Length (Toolchange)" +msgstr "引込み長(ツールチェンジ)" + +#: src/libslic3r/PrintConfig.cpp:1534 src/libslic3r/PrintConfig.cpp:1535 +msgid "Retraction Speed" +msgstr "引き込み速度" + +#: src/slic3r/GUI/Tab.cpp:2284 +msgid "Retraction when tool is disabled (advanced settings for multi-extruder setups)" +msgstr "非アクティブなツールのフィラメントを待避する(マルチエクストルーダーの高度な設定)" + +#: src/slic3r/GUI/GUI_Preview.cpp:244 +msgid "Retractions" +msgstr "待避" + +#: src/slic3r/GUI/MainFrame.cpp:528 +msgid "Right" +msgstr "右" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:381 +msgid "Right button click the icon to change the object printable property" +msgstr "アイコンを右クリックして、オブジェクトのプリント可プロパティを変更します" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:279 +msgid "Right button click the icon to change the object settings" +msgstr "アイコンを右クリックして、オブジェクトの設定を変更します" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:250 +msgid "Right button click the icon to fix STL through Netfabb" +msgstr "NetfabbでSTLを修正するには、アイコンを右クリックします" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1232 +msgid "Right click" +msgstr "右クリック" + +#: src/slic3r/GUI/MainFrame.cpp:528 +msgid "Right View" +msgstr "右側" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:233 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:253 +#: src/libslic3r/PrintConfig.cpp:3062 +msgid "Rotate" +msgstr "回転" + +#: src/libslic3r/PrintConfig.cpp:3067 +msgid "Rotate around X" +msgstr "X軸周りで回転" + +#: src/libslic3r/PrintConfig.cpp:3072 +msgid "Rotate around Y" +msgstr "Y軸周りの回転" + +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:35 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:195 +msgid "Rotate lower part upwards" +msgstr "回転させて上下をひっくり返します" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:151 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:194 +msgid "Rotation" +msgstr "回転" + +#: src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp:491 +msgid "Rotation (deg)" +msgstr "回転(度)" + +#: src/libslic3r/PrintConfig.cpp:3068 +msgid "Rotation angle around the X axis in degrees." +msgstr "X軸の周りの回転角度(度)。" + +#: src/libslic3r/PrintConfig.cpp:3073 +msgid "Rotation angle around the Y axis in degrees." +msgstr "Y軸を中心とした回転角(度単位)。" + +#: src/libslic3r/PrintConfig.cpp:3063 +msgid "Rotation angle around the Z axis in degrees." +msgstr "Z軸周りの回転角度(度)" + +#: src/slic3r/GUI/ConfigWizard.cpp:298 src/slic3r/GUI/GUI_App.cpp:658 +#, c-format +msgid "Run %s" +msgstr "%s実行" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:85 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:398 +msgid "Running post-processing scripts" +msgstr "ポストプロセス スクリプト実行中" + +#: src/slic3r/GUI/RammingChart.cpp:76 src/slic3r/GUI/RammingChart.cpp:81 +#: src/slic3r/GUI/WipeTowerDialog.cpp:82 src/libslic3r/PrintConfig.cpp:612 +#: src/libslic3r/PrintConfig.cpp:656 src/libslic3r/PrintConfig.cpp:671 +#: src/libslic3r/PrintConfig.cpp:2243 src/libslic3r/PrintConfig.cpp:2252 +#: src/libslic3r/PrintConfig.cpp:2312 src/libslic3r/PrintConfig.cpp:2319 +msgid "s" +msgstr "s" + +#: src/slic3r/GUI/MainFrame.cpp:466 src/slic3r/GUI/MainFrame.cpp:709 +msgid "S&end G-code" +msgstr "Gコードを送信" + +#: src/slic3r/GUI/MainFrame.cpp:709 +msgid "S&end to print" +msgstr "プリントする" + +#. TRN Preset +#: src/slic3r/GUI/Tab.cpp:3264 +#, c-format +msgid "Save %s as:" +msgstr "形式を変更して%sを保存:" + +#: src/slic3r/GUI/MainFrame.cpp:686 +#, c-format +msgid "Save %s file as:" +msgstr "%sファイルを別の名前で保存:" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1023 +msgid "Save changes?" +msgstr "変更を保存しますか?" + +#: src/libslic3r/PrintConfig.cpp:2997 +msgid "Save config file" +msgstr "設定ファイルを保存" + +#: src/slic3r/GUI/MainFrame.cpp:786 +msgid "Save configuration as:" +msgstr "構成ファイルを別名で保存:" + +#: src/libslic3r/PrintConfig.cpp:2998 +msgid "Save configuration to the specified file." +msgstr "指定したファイルに構成を保存します。" + +#. TRN "Save current Settings" +#: src/slic3r/GUI/Tab.cpp:133 +#, c-format +msgid "Save current %s" +msgstr "現在の%sを保存" + +#: src/slic3r/GUI/MainFrame.cpp:341 +msgid "Save current project file" +msgstr "現在のプロジェクトファイルの保存" + +#: src/slic3r/GUI/MainFrame.cpp:343 +msgid "Save current project file as" +msgstr "現在のプロジェクトに名前を付けて保存" + +#: src/slic3r/GUI/Plater.cpp:1938 +msgid "Save file as:" +msgstr "別名で保存 :" + +#: src/slic3r/GUI/Plater.cpp:3433 +msgid "Save G-code file as:" +msgstr "Gコードを別名で保存:" + +#: src/slic3r/GUI/MainFrame.cpp:757 +msgid "Save OBJ file (less prone to coordinate errors than STL) as:" +msgstr "OBJファイルを保存します(STLよりも調整エラーが少ない):" + +#: src/slic3r/GUI/Tab.hpp:417 +msgid "Save preset" +msgstr "プリセット保存" + +#: src/slic3r/GUI/MainFrame.cpp:843 +msgid "Save presets bundle as:" +msgstr "プリセットパッケージを別の名前で保存" + +#: src/slic3r/GUI/MainFrame.cpp:343 +msgid "Save Project &as" +msgstr "プロジェクトを別名で保存" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:109 +msgid "Save project (3MF)" +msgstr "プロジェクトの保存(3MF)" + +#: src/slic3r/GUI/Plater.cpp:3433 +msgid "Save SL1 file as:" +msgstr "SL1ファイルを別名で保存 :" + +#: src/slic3r/GUI/MainFrame.cpp:692 +msgid "Save zip file as:" +msgstr "ZIPファイルを保存:" + +#: 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 "3MFコンテナへのメッシュの保存に失敗しました。" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:152 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:234 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:254 +#: src/libslic3r/PrintConfig.cpp:3077 +msgid "Scale" +msgstr "スケール" + +#: src/slic3r/GUI/Gizmos/GLGizmoScale.cpp:276 +msgid "Scale (%)" +msgstr "スケール(%)" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:195 +msgid "Scale factors" +msgstr "寸法係数" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1440 +msgid "Scale the selected object to fit the print volume" +msgstr "選択したオブジェクトをプリントボリュームに合わせて拡大縮小します" + +#: src/libslic3r/PrintConfig.cpp:3086 +msgid "Scale to Fit" +msgstr "フィットするように縮尺" + +#: src/slic3r/GUI/Selection.cpp:947 +msgid "Scale To Fit" +msgstr "フィットするように縮尺" + +#: src/libslic3r/PrintConfig.cpp:3087 +msgid "Scale to fit the given volume." +msgstr "指定したプリントスペースに合わせてサイズを変更します。" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1440 +msgid "Scale to print volume" +msgstr "プリントエリアに合わせて縮尺する" + +#: src/libslic3r/PrintConfig.cpp:3078 +msgid "Scaling factor or percentage." +msgstr "スケーリング係数または割合" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:409 +msgid "Scheduling upload to `%1%`. See Window -> Print Host Upload Queue" +msgstr "`%1%`へのアップロードスケジュール。 ウィンドウ->プリントホストアップロードキューを参照してください" + +#: src/libslic3r/PrintConfig.cpp:1551 +msgid "Seam position" +msgstr "シーム位置" + +#: src/libslic3r/PrintConfig.cpp:1572 +msgid "Seam preferred direction" +msgstr "シーム優先方向" + +#: src/libslic3r/PrintConfig.cpp:1581 +msgid "Seam preferred direction jitter" +msgstr "シーム優先方向ジッター" + +#: src/slic3r/GUI/BonjourDialog.cpp:218 +msgid "Searching for devices" +msgstr "デバイス検索中" + +#: src/slic3r/GUI/Plater.cpp:2190 +msgid "Searching for optimal orientation" +msgstr "最適方向を探す" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:136 +msgid "Select All objects" +msgstr "全てのオブジェクトを選択" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1236 +msgid "Select all points" +msgstr "全てのポイントを選択" + +#: src/slic3r/GUI/ConfigWizard.cpp:1089 +msgid "Select all standard printers" +msgstr "すべての標準プリンターを選択" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1234 +msgid "Select by rectangle" +msgstr "四角形で選択" + +#: src/slic3r/GUI/MainFrame.cpp:806 src/slic3r/GUI/MainFrame.cpp:870 +msgid "Select configuration to load:" +msgstr "読み込む構成を選択します:" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:60 +msgid "Select coordinate space, in which the transformation will be performed." +msgstr "変換する座標空間を選択します。" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2834 +msgid "Select extruder number for selected objects and/or parts" +msgstr "選択したオブジェクトおよび/またはパーツのエクストルーダー番号を選択します" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2847 +msgid "Select extruder number:" +msgstr "エクストルーダー番号の選択:" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:117 +msgid "Select Filament Settings Tab" +msgstr "フィラメント設定タブを選択" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1251 +msgid "Select new extruder for the object/part" +msgstr "オブジェクト/パーツに新しいエクストルーダーを選択します" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:114 +msgid "Select Plater Tab" +msgstr "プレートタブを選択" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:116 +msgid "Select Print Settings Tab" +msgstr "プリント設定タブを選択" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:118 +msgid "Select Printer Settings Tab" +msgstr "プリンタ設定タブを選択" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:917 +msgid "Select showing settings" +msgstr "表示設定を選択" + +#: src/slic3r/GUI/GUI_App.cpp:524 +msgid "Select the language" +msgstr "言語を選択" + +#: src/slic3r/GUI/Tab.cpp:57 +msgid "Select the print profiles this profile is compatible with." +msgstr "このプロファイルと互換性のあるプリントプロファイルを選択します。" + +#: src/slic3r/GUI/Tab.cpp:51 +msgid "Select the printers this profile is compatible with." +msgstr "このプロファイルと互換性のあるプリンターを選択します。" + +#: src/slic3r/GUI/MainFrame.cpp:744 +msgid "Select the STL file to repair:" +msgstr "修復するSTLファイルを選択 :" + +#: src/slic3r/GUI/Preferences.cpp:207 +msgid "Select toolbar icon size in respect to the default one." +msgstr "デフォルトのツールバーアイコンのサイズを選択します。" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2545 +msgid "Select type of part" +msgstr "パーツのタイプを選択" + +#: src/slic3r/GUI/Plater.cpp:606 +msgid "Select what kind of pad do you need" +msgstr "必要なパッドの種類を選択してください" + +#: src/slic3r/GUI/Plater.cpp:421 +msgid "Select what kind of support do you need" +msgstr "必要なサポートの種類を選択してください" + +#: src/slic3r/GUI/Selection.cpp:146 +msgid "Selection-Add" +msgstr "選択-追加" + +#: src/slic3r/GUI/Selection.cpp:384 +msgid "Selection-Add All" +msgstr "選択-すべて追加" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2875 +msgid "Selection-Add from list" +msgstr "選択-リストから追加" + +#: src/slic3r/GUI/GLCanvas3D.cpp:5623 +msgid "Selection-Add from rectangle" +msgstr "選択-長方形から追加" + +#: src/slic3r/GUI/Selection.cpp:256 +msgid "Selection-Add Instance" +msgstr "選択ーインスタンス追加" + +#: src/slic3r/GUI/Selection.cpp:219 +msgid "Selection-Add Object" +msgstr "選択-オブジェクト追加" + +#: src/slic3r/GUI/Selection.cpp:187 +msgid "Selection-Remove" +msgstr "選択-除去" + +#: src/slic3r/GUI/Selection.cpp:410 +msgid "Selection-Remove All" +msgstr "選択-全て除去" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2867 +msgid "Selection-Remove from list" +msgstr "リストの選択-削除" + +#: src/slic3r/GUI/GLCanvas3D.cpp:5642 +msgid "Selection-Remove from rectangle" +msgstr "選択-四角形から削除" + +#: src/slic3r/GUI/Selection.cpp:275 +msgid "Selection-Remove Instance" +msgstr "選択-インスタンス削除" + +#: src/slic3r/GUI/Selection.cpp:238 +msgid "Selection-Remove Object" +msgstr "選択-オブジェクト削除" + +#: src/slic3r/GUI/MainFrame.cpp:444 +msgid "Selects all objects" +msgstr "全てのオブジェクトを選択" + +#: src/slic3r/GUI/Plater.cpp:3822 +msgid "Send G-code" +msgstr "Gコード送信" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:33 +msgid "Send G-Code to printer host" +msgstr "プリンターサーバーにGコードを送信" + +#: src/slic3r/GUI/MainFrame.cpp:466 +msgid "Send to print current plate as G-code" +msgstr "現在のプレートをプリントするためにGコードとして送信" + +#: src/slic3r/GUI/Plater.cpp:731 src/slic3r/GUI/Plater.cpp:3822 +msgid "Send to printer" +msgstr "プリンターに送信" + +#: src/slic3r/GUI/Tab.cpp:1169 +msgid "Sequential printing" +msgstr "順次プリンティング" + +#: src/slic3r/GUI/Tab.cpp:1874 src/libslic3r/PrintConfig.cpp:1591 +msgid "Serial port" +msgstr "シリアルポート" + +#: src/libslic3r/PrintConfig.cpp:1599 +msgid "Serial port speed" +msgstr "シリアルポートスピード" + +#: src/slic3r/GUI/FirmwareDialog.cpp:769 +msgid "Serial port:" +msgstr "シリアルポート:" + +#: src/slic3r/GUI/BonjourDialog.cpp:74 +msgid "Service name" +msgstr "サービス名" + +#: src/slic3r/GUI/Tab.cpp:1824 src/slic3r/GUI/Tab.cpp:2025 +#: src/slic3r/GUI/Tab.cpp:3008 +msgid "Set" +msgstr "設定" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1192 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1331 +msgid "Set as a Separated Object" +msgstr "分離オブジェクトとして設定" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1331 +msgid "Set as a Separated Objects" +msgstr "分離オブジェクトとして設定" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2833 +msgid "Set extruder for selected items" +msgstr "選択アイテムのエクストルーダーを設定" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:185 +msgid "Set lower thumb to current slider thumb" +msgstr "下のつまみを現在のスライダーのつまみに設定します" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:245 +msgid "Set Mirror" +msgstr "ミラーリング設定" + +#: src/slic3r/GUI/Plater.cpp:3520 +msgid "Set number of instances" +msgstr "インスタンス数の設定" + +#: src/slic3r/GUI/Plater.cpp:4163 +#, c-format +msgid "Set numbers of copies to %d" +msgstr "コピーの数を%dに設定" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:746 +msgid "Set Orientation" +msgstr "方向を設定" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:715 +msgid "Set Position" +msgstr "位置設定" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3759 +msgid "Set Printable" +msgstr "プリント可にする" + +#: src/slic3r/GUI/Selection.cpp:1482 +msgid "Set Printable Instance" +msgstr "プリント可なインスタンスを設定" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:811 +msgid "Set Scale" +msgstr "縮尺をセット" + +#: src/libslic3r/PrintConfig.cpp:2228 +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 "SLAプリンター内の実際のLCDディスプレイの向きを設定します。 ポートレートモードでは、ディスプレイの幅と高さのパラメーターの意味が変わり、出力画像が90度回転します。" + +#: src/slic3r/GUI/ConfigWizard.cpp:527 +msgid "Set the shape of your printer's bed." +msgstr "プリントベッドの形状とサイズを設定します。" + +#: src/libslic3r/PrintConfig.cpp:524 +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 "正の値を設定すると、射出幅のマニュアル設定が有効になります。 値がゼロに設定されている場合、Slic3rはノズル径から射出幅を計算します(外周射出幅、インフィル射出幅などのヘルプを参照)。 値がパーセンテージで入力された場合(例:230%)、レイヤーの高さから計算されます。" + +#: src/libslic3r/PrintConfig.cpp:417 +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 "ゼロ以外の値を入力すると、最外周の射出幅をマニュアル設定できます。 ゼロの場合、設定されていればデフォルトの射出幅が使用され、設定されていない場合はノズル径の1.125倍になります。 パーセンテージ(200%など)で入力された場合、レイヤーの高さに対して計算されます。" + +#: src/libslic3r/PrintConfig.cpp:831 +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 "これをゼロ以外の値にすると、最初のレイヤーの射出幅をマニュアル設定できます。 これを使用して、ベッドとの密着を上げるために、より太い射出幅にできます。 パーセンテージ(例:120%)で入力した場合、最初のレイヤーの高さに対して計算されます。 ゼロに設定すると、デフォルトの射出幅になります。" + +#: src/libslic3r/PrintConfig.cpp:1689 +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 "正の値を入力して、射出幅をマニュアル設定し、ソリッドサーフェスを塗りつぶします。 ゼロのにすると、設定されていればデフォルトの射出幅が使用され、設定されていなければノズル径の1.125倍が適用されます。 パーセンテージ(たとえば、90%)で入力すると、レイヤーの高さから計算されます。" + +#: src/libslic3r/PrintConfig.cpp:2019 +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 "ゼロ以外の値を入力すると、上面インフィル(塗りつぶし)の射出幅をマニュアル設定できます。 細い射出幅に設定して、隙間なく、より滑らかに仕上げることができます。 ゼロのままにすると、設定されていればデフォルトの射出幅となり、設定されていなければノズル径が使用されます。 パーセンテージ(90%など)で入力された場合、レイヤーの高さに対して計算されます。" + +#: src/libslic3r/PrintConfig.cpp:963 +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 "正の値を設定して、インフィル(中塗り)の射出幅をマニュアル調整します。 値がゼロの場合、設定されている場合は標準の射出幅が使用され、設定されていない場合はノズル径の1.125倍になります。 より太い射出幅を使用して、塗りつぶしを高速化し、プリント強度を強化することができます。 パーセンテージ(たとえば、90%)で表される場合、レイヤーの高さから計算されます。" + +#: src/libslic3r/PrintConfig.cpp:1350 +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 "外周の射出幅をマニュアル設定するには、正の値を入力します。 より正確な表面を得るために、より細い射出幅を設定できます。 ゼロが入力されている場合、設定されている場合は標準の射出幅が使用され、設定されていない場合はノズル径の1.125倍が適用されます。 パーセンテージ(200%など)で入力された場合は、レイヤーの高さから計算されます。" + +#: src/libslic3r/PrintConfig.cpp:1862 +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 "これをゼロ以外の値にすると、サポート材の射出幅を設定できます。 ゼロのままですと、デフォルトの射出幅が設定されていればその値が設定され、設定されていない場合はノズル径が設定されます。 パーセンテージ(90%など)で設定された場合は、レイヤーの高さから自動計算されます。" + +#: src/libslic3r/PrintConfig.cpp:480 +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:65 +msgid "Set this to the maximum height that can be reached by your extruder while printing." +msgstr "プリント中にエクストルーダーが到達できる最大の高さを設定します。" + +#: src/libslic3r/PrintConfig.cpp:469 +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 "ノズルチップと(通常)Xキャリッジロッド間の垂直距離を入力します。 つまり、これはエクストルーダーの高さクリアランスで、順次プリントの時にエクストルーダーが他のプリント済みオブジェクトと衝突しないでプリントできる深さの最大値を表します。" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3759 +msgid "Set Unprintable" +msgstr "プリント不可にする" + +#: src/slic3r/GUI/Selection.cpp:1482 +msgid "Set Unprintable Instance" +msgstr "プリントしないインスタンスを設定する" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:184 +msgid "Set upper thumb to current slider thumb" +msgstr "上部のつまみを現在のスライダーのつまみに設定します" + +#: src/slic3r/GUI/BedShapeDialog.cpp:143 +msgid "Settings" +msgstr "設定" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2206 +msgid "Settings for height range" +msgstr "高さ範囲の設定" + +#: src/slic3r/GUI/BedShapeDialog.cpp:60 +msgid "Shape" +msgstr "形状" + +#: src/slic3r/GUI/GUI_Preview.cpp:246 +msgid "Shells" +msgstr "シェル" + +#: src/slic3r/GUI/GUI_Preview.cpp:221 +msgid "Show" +msgstr "表示" + +#: src/slic3r/GUI/MainFrame.cpp:559 +msgid "Show &Configuration Folder" +msgstr "設定フォルダーの表示" + +#: src/slic3r/GUI/MainFrame.cpp:563 +msgid "Show about dialog" +msgstr "アバウトダイヤログを表示" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:339 +msgid "Show advanced settings" +msgstr "高度な設定を表示" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:158 +msgid "Show error message" +msgstr "エラーメッセージの表示" + +#: src/slic3r/GUI/Preferences.cpp:84 +msgid "Show incompatible print and filament presets" +msgstr "互換性のないプリントとフィラメントのプリセットを表示する" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:126 +msgid "Show keyboard shortcuts list" +msgstr "キーボードショートカット一覧を表示" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:339 +msgid "Show simplified settings" +msgstr "簡易設定を表示" + +#: src/slic3r/GUI/MainFrame.cpp:557 +msgid "Show system information" +msgstr "システム情報を表示" + +#: src/slic3r/GUI/MainFrame.cpp:487 +msgid "Show the 3D editing view" +msgstr "3D編集画面の表示" + +#: src/slic3r/GUI/MainFrame.cpp:489 +msgid "Show the 3D slices preview" +msgstr "3Dスライスのプレビューを表示" + +#: src/slic3r/GUI/MainFrame.cpp:480 +msgid "Show the filament settings" +msgstr "フィラメントの設定を表示" + +#: src/libslic3r/PrintConfig.cpp:2983 +msgid "Show the full list of print/G-code configuration options." +msgstr "プリント/ Gコード構成オプションの完全なリストを表示します。" + +#: src/libslic3r/PrintConfig.cpp:2988 +msgid "Show the full list of SLA print configuration options." +msgstr "SLAプリント構成オプションの完全なリストを表示します。" + +#: src/slic3r/GUI/MainFrame.cpp:566 +msgid "Show the list of the keyboard shortcuts" +msgstr "キーボードショートカットのリストを表示する" + +#: src/slic3r/GUI/MainFrame.cpp:471 +msgid "Show the plater" +msgstr "プレート表示" + +#: src/slic3r/GUI/MainFrame.cpp:478 +msgid "Show the print settings" +msgstr "プリント設定を表示する" + +#: src/slic3r/GUI/MainFrame.cpp:483 +msgid "Show the printer settings" +msgstr "プリンター設定を表示する" + +#: src/libslic3r/PrintConfig.cpp:2977 +msgid "Show this help." +msgstr "このヘルプを表示します。" + +#: src/slic3r/GUI/MainFrame.cpp:559 +msgid "Show user configuration folder (datadir)" +msgstr "ユーザー設定フォルダーの表示(datadir)" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:185 +msgid "Show/Hide (L)egend" +msgstr "表示/非表示(L)凡例" + +#: src/slic3r/GUI/GUI_App.cpp:674 src/slic3r/GUI/wxExtensions.cpp:2459 +msgid "Simple" +msgstr "簡易" + +#: src/slic3r/GUI/GUI_App.cpp:674 +msgid "Simple View Mode" +msgstr "簡易ビューモード" + +#: src/slic3r/GUI/Tab.cpp:2231 src/slic3r/GUI/Tab.cpp:2239 +msgid "Single extruder MM setup" +msgstr "シングルエクストルーダーのMM設定" + +#: src/libslic3r/PrintConfig.cpp:1767 +msgid "Single Extruder Multi Material" +msgstr "シングルエクストルーダー・マルチマテリアル" + +#: src/slic3r/GUI/Tab.cpp:2023 +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 "1つのエクストルーダーのマルチマテリアルプリンターが選択されているため、すべてのエクストルーダーの直径が同じでなければなりません。最初のエクストルーダーの直径で、すべてのエクストルーダーノズルの直径を設定しますか?" + +#: src/slic3r/GUI/Tab.cpp:2240 +msgid "Single extruder multimaterial parameters" +msgstr "単一エクストルーダーのマルチマテリアルパラメーター" + +#: src/slic3r/GUI/BedShapeDialog.cpp:72 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:153 src/slic3r/GUI/Plater.cpp:137 +#: src/slic3r/GUI/Tab.cpp:2257 +msgid "Size" +msgstr "サイズ" + +#: src/slic3r/GUI/Tab.cpp:1813 src/slic3r/GUI/Tab.cpp:2014 +msgid "Size and coordinates" +msgstr "サイズと座標" + +#: src/slic3r/GUI/BedShapeDialog.cpp:73 +msgid "Size in X and Y of the rectangular plate." +msgstr "四角形プレートのX、Yサイズ。" + +#: src/slic3r/GUI/GUI_Preview.cpp:235 src/slic3r/GUI/Tab.cpp:1050 +#: src/libslic3r/GCode/PreviewData.cpp:171 +msgid "Skirt" +msgstr "スカート" + +#: src/slic3r/GUI/Tab.cpp:1049 +msgid "Skirt and brim" +msgstr "スカートとブリム" + +#: src/libslic3r/PrintConfig.cpp:1617 +msgid "Skirt height" +msgstr "スカート高さ" + +#: src/libslic3r/PrintConfig.cpp:1626 +msgid "Skirt Loops" +msgstr "スカートのループ数" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1200 +msgid "SLA gizmo keyboard shortcuts" +msgstr "SLAギズモのキーボードショートカット" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1150 +msgid "SLA gizmo turned off" +msgstr "SLAギズモ非表示" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1115 +msgid "SLA gizmo turned on" +msgstr "SLAギズモ表示" + +#: src/slic3r/GUI/Plater.cpp:684 src/slic3r/GUI/Preset.cpp:1277 +msgid "SLA material" +msgstr "SLA材料" + +#: src/slic3r/GUI/Plater.cpp:683 src/slic3r/GUI/Preset.cpp:1276 +msgid "SLA print" +msgstr "SLAプリント" + +#: src/libslic3r/PrintConfig.cpp:2331 +msgid "SLA print material notes" +msgstr "SLAプリント材料メモ" + +#: src/slic3r/GUI/Plater.cpp:690 +msgid "SLA print settings" +msgstr "SLAプリント設定" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:997 +msgid "SLA Support Points" +msgstr "SLAサポートポイント" + +#: src/slic3r/GUI/GLCanvas3D.cpp:722 +msgid "SLA supports outside the print area were detected" +msgstr "プリント範囲外のSLAサポートが検出されました" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1055 +msgid "Slab" +msgstr "平板" + +#: src/libslic3r/PrintConfig.cpp:1268 +msgid "Slic3r can upload G-code files to a printer host. This field must contain the kind of the host." +msgstr "Slic3rはGコードファイルをプリンターサーバーにアップロードできます。 このフィールドには、サーバーの種類を記入する必要があります。" + +#: src/libslic3r/PrintConfig.cpp:89 +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 "Slic3rはGコードファイルをプリンターサーバーにアップロードできます。 このフィールドには、認証に必要なAPIキーまたはパスワードが含まれている必要があります。" + +#: src/libslic3r/PrintConfig.cpp:82 +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 "Slic3rはGコードファイルをプリントサーバーにアップロードできます。 このフィールドには、プリントサーバーのホスト名、IPアドレス、またはURLが含まれている必要があります。" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:45 +msgid "slic3r version" +msgstr "slic3rバージョン" + +#: src/libslic3r/PrintConfig.cpp:1234 +msgid "Slic3r will not scale speed down below this speed." +msgstr "Slic3rはこの速度以下にしません。" + +#: src/libslic3r/PrintConfig.cpp:2970 +msgid "Slice" +msgstr "スライス" + +#: src/slic3r/GUI/MainFrame.cpp:383 +msgid "Slice a file into a G-code" +msgstr "ファイルをスライスしてGコードに入れる" + +#: src/slic3r/GUI/MainFrame.cpp:389 +msgid "Slice a file into a G-code, save as" +msgstr "ファイルをスライスしGコードにして、名前を付けて保存" + +#: src/libslic3r/PrintConfig.cpp:71 +msgid "Slice gap closing radius" +msgstr "スライスギャップを閉じる半径" + +#: src/slic3r/GUI/Plater.cpp:734 src/slic3r/GUI/Plater.cpp:2412 +#: src/slic3r/GUI/Plater.cpp:3618 +msgid "Slice now" +msgstr "スライス実行" + +#: src/libslic3r/PrintConfig.cpp:2944 +msgid "Slice the model and export SLA printing layers as PNG." +msgstr "モデルをスライスし、SLAプリントレイヤーをPNGとしてエクスポートします。" + +#: src/libslic3r/PrintConfig.cpp:2965 +msgid "Slice the model and export toolpaths as G-code." +msgstr "モデルをスライスし、ツールパスをGコードでエクスポートします" + +#: src/libslic3r/PrintConfig.cpp:2971 +msgid "Slice the model as FFF or SLA based on the printer_technology configuration value." +msgstr "printer_technology構成値に基づいて、モデルをFFFまたはSLAとしてスライスします。" + +#: src/slic3r/GUI/Plater.cpp:193 +msgid "Sliced Info" +msgstr "スライス情報" + +#: src/slic3r/GUI/MainFrame.cpp:704 src/slic3r/GUI/Plater.cpp:2412 +#: src/slic3r/GUI/Plater.cpp:3615 src/slic3r/GUI/Tab.cpp:1159 +#: src/slic3r/GUI/Tab.cpp:3436 +msgid "Slicing" +msgstr "スライス中" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:91 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:109 +msgid "Slicing complete" +msgstr "スライス完了" + +#: src/libslic3r/SLAPrint.cpp:1459 +msgid "Slicing done" +msgstr "スライス完了" + +#: src/slic3r/GUI/MainFrame.cpp:729 +msgid "Slicing Done!" +msgstr "スライス完了!" + +#: src/libslic3r/SLAPrint.cpp:759 +msgid "Slicing had to be stopped due to an internal error: Inconsistent slice index." +msgstr "内部エラーのため、スライスを停止しました:一貫性のないスライスインデックス。" + +#: src/libslic3r/SLAPrint.cpp:55 +msgid "Slicing model" +msgstr "モデルをスライス" + +#: src/libslic3r/SLAPrint.cpp:59 +msgid "Slicing supports" +msgstr "サポートのスライス" + +#: src/libslic3r/PrintConfig.cpp:2249 +msgid "Slow" +msgstr "スロー" + +#: src/libslic3r/PrintConfig.cpp:1635 +msgid "Slow down if layer print time is below" +msgstr "スローダウンさせるレイヤーのプリント時間" + +#: src/libslic3r/PrintConfig.cpp:2250 +msgid "Slow tilt" +msgstr "スローチルト" + +#: src/libslic3r/PrintConfig.cpp:1646 +msgid "Small perimeters" +msgstr "短い外周" + +#: src/slic3r/GUI/GUI_App.cpp:697 +msgid "Snapshot name" +msgstr "スナップショット名" + +#: src/slic3r/GUI/MainFrame.cpp:549 +msgid "Software &Releases" +msgstr "ソフトウェアリリース" + +#: src/slic3r/GUI/PresetHints.cpp:181 +msgid "solid infill" +msgstr "ソリッドインフィル" + +#: src/slic3r/GUI/GUI_Preview.cpp:231 src/libslic3r/PrintConfig.cpp:1687 +#: src/libslic3r/PrintConfig.cpp:1697 src/libslic3r/GCode/PreviewData.cpp:167 +msgid "Solid infill" +msgstr "ソリッドインフィル" + +#: src/libslic3r/PrintConfig.cpp:1675 +msgid "Solid infill every" +msgstr "ソリッドインフィルを各" + +#: src/libslic3r/PrintConfig.cpp:1667 +msgid "Solid infill extruder" +msgstr "ソリッドインフィルエクストルーダー" + +#: src/libslic3r/PrintConfig.cpp:1658 +msgid "Solid infill threshold area" +msgstr "ソリッドインフィル領域のしきい値" + +#: src/slic3r/GUI/Tab.cpp:1014 src/libslic3r/PrintConfig.cpp:1710 +msgid "Solid layers" +msgstr "ソリッドレイヤー" + +#: src/libslic3r/PrintConfig.cpp:711 +msgid "Soluble material" +msgstr "溶解性材料" + +#: src/libslic3r/PrintConfig.cpp:712 +msgid "Soluble material is most likely used for a soluble support." +msgstr "水溶性フィラメントが溶解性サポート材としてもっとも使用される。" + +#: src/libslic3r/PrintConfig.cpp:914 +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 "温度制御などを含む一部のG/Mコードコマンドは普遍的ではありません。互換性のある出力を実現するためにプリンターが使用するファームウェアのタイプを選択します。 「押出しなし」コマンドにより、PrusaSlicerは押出しコマンドをエクスポートしなくなります。" + +#: src/slic3r/GUI/GLCanvas3D.cpp:721 +msgid "Some objects are not visible when editing supports" +msgstr "サポート編集時に一部のオブジェクトが表示されない" + +#: src/libslic3r/Print.cpp:1162 +msgid "Some objects are too close; your extruder will collide with them." +msgstr "一部のオブジェクトが接近しすぎています。エクストルーダが接触します。" + +#: src/libslic3r/Print.cpp:1177 +msgid "Some objects are too tall and cannot be printed without extruder collisions." +msgstr "一部のオブジェクトが高すぎて、エクストルーダーの衝突なしでプリントできません。" + +#: src/libslic3r/PrintConfig.cpp:2548 +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 "ある種のオブジェクトでは、単一の大きなパッドではなく、いくつかの小さなパッドの方がうまくいきます。 このパラメーターは、2つの小さなパッドの中心の距離を定義します。 それらが近い場合、それらは1つのパッドにマージされます。" + +#: src/libslic3r/PrintConfig.cpp:2086 +msgid "Some printers or printer setups may have difficulties printing with a variable layer height. Enabled by default." +msgstr "一部のプリンターまたはプリンターのセットアップでは、レイヤー高さ可変のプリントが困難な場合があります。 デフォルトで有効になっています。" + +#: src/libslic3r/PrintConfig.cpp:1897 +msgid "Spacing between interface lines. Set zero to get a solid interface." +msgstr "インターフェイスの塗りの隙間。 ゼロを設定すると、密なインターフェースになります。" + +#: src/libslic3r/PrintConfig.cpp:1931 +msgid "Spacing between support material lines." +msgstr "サポートパターンの線間距離" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:71 src/slic3r/GUI/GUI_ObjectList.cpp:512 +#: src/slic3r/GUI/GUI_Preview.cpp:215 src/slic3r/GUI/Tab.cpp:1084 +#: src/libslic3r/PrintConfig.cpp:199 src/libslic3r/PrintConfig.cpp:426 +#: src/libslic3r/PrintConfig.cpp:871 src/libslic3r/PrintConfig.cpp:999 +#: src/libslic3r/PrintConfig.cpp:1361 src/libslic3r/PrintConfig.cpp:1598 +#: src/libslic3r/PrintConfig.cpp:1647 src/libslic3r/PrintConfig.cpp:1698 +#: src/libslic3r/PrintConfig.cpp:2029 +msgid "Speed" +msgstr "速度" + +#: src/libslic3r/PrintConfig.cpp:1600 +msgid "Speed (baud) of USB/serial port for printer connection." +msgstr "プリンター接続用のUSB /シリアルポートのスピード(ボーレート)。" + +#: src/libslic3r/GCode/PreviewData.cpp:400 +msgid "Speed (mm/s)" +msgstr "速度(mm/s)" + +#: src/libslic3r/PrintConfig.cpp:872 +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/slic3r/GUI/Tab.cpp:1097 +msgid "Speed for non-print moves" +msgstr "移動速度" + +#: src/libslic3r/PrintConfig.cpp:1362 +msgid "Speed for perimeters (contours, aka vertical shells). Set to zero for auto." +msgstr "外周(輪郭、別名:垂直シェル)の速度。 自動の場合はゼロに設定します。" + +#: src/slic3r/GUI/Tab.cpp:1085 +msgid "Speed for print moves" +msgstr "造形速度設定" + +#: src/libslic3r/PrintConfig.cpp:200 +msgid "Speed for printing bridges." +msgstr "ブリッジ形成速度" + +#: src/libslic3r/PrintConfig.cpp:1699 +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 "ソリッド(塗りつぶし)領域(上部/下部/内部水平シェル)のプリント速度。 これは、上記のデフォルトインフィル速度に対する割合(例:80%)で表すことができます。 自動の場合はゼロに設定します。" + +#: src/libslic3r/PrintConfig.cpp:1906 +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%)を入力すると、サポートのプリントスピードから計算されます。" + +#: src/libslic3r/PrintConfig.cpp:1940 +msgid "Speed for printing support material." +msgstr "サポート材造形速度" + +#: src/libslic3r/PrintConfig.cpp:1000 +msgid "Speed for printing the internal fill. Set to zero for auto." +msgstr "内部塗りつぶしのプリント速度。 自動の場合はゼロにします。" + +#: src/libslic3r/PrintConfig.cpp:2030 +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 "上部のソリッドレイヤー(塗りつぶし)のプリント速度(最上部のレイヤーにのみ適用されるもので、内部のソリッドレイヤーには適用されません)。 この速度を遅くすることで、より良い表面に仕上げることができます。 これは、内部のソリッドレイヤー速度に対する割合(例:80%)で入力することができます。 自動の場合はゼロに設定します。" + +#: src/libslic3r/PrintConfig.cpp:2052 +msgid "Speed for travel moves (jumps between distant extrusion points)." +msgstr "移動速度(射出ポイント間のジャンピング)。" + +#: src/libslic3r/PrintConfig.cpp:627 +msgid "Speed of the first cooling move" +msgstr "冷却移動の最初の速度" + +#: src/libslic3r/PrintConfig.cpp:646 +msgid "Speed of the last cooling move" +msgstr "最後の冷却移動の速度" + +#: src/libslic3r/PrintConfig.cpp:585 +msgid "Speed used at the very beginning of loading phase." +msgstr "ロードし始めの最初のスピード。" + +#: src/libslic3r/PrintConfig.cpp:577 +msgid "Speed used for loading the filament on the wipe tower." +msgstr "フィラメントをワイプタワー上でロードする際のスピード。" + +#: src/libslic3r/PrintConfig.cpp:593 +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:602 +msgid "Speed used for unloading the tip of the filament immediately after ramming." +msgstr "ラミング直後にフィラメントの先端を引き抜く速度。" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1055 +msgid "Sphere" +msgstr "球体" + +#: src/libslic3r/PrintConfig.cpp:1717 +msgid "Spiral vase" +msgstr "スパイラル花瓶" + +#: src/slic3r/GUI/Plater.cpp:2971 src/slic3r/GUI/Plater.cpp:2988 +#: src/slic3r/GUI/Plater.cpp:3008 src/libslic3r/PrintConfig.cpp:3082 +msgid "Split" +msgstr "分割" + +#: src/slic3r/GUI/Plater.cpp:2971 +msgid "Split the selected object" +msgstr "選択したオブジェクトを分割します" + +#: src/slic3r/GUI/Plater.cpp:2966 src/slic3r/GUI/Plater.cpp:2988 +msgid "Split the selected object into individual objects" +msgstr "選択したオブジェクトを個々のオブジェクトに分割します" + +#: src/slic3r/GUI/Plater.cpp:2968 src/slic3r/GUI/Plater.cpp:3008 +msgid "Split the selected object into individual sub-parts" +msgstr "選択したオブジェクトを個々のサブパーツに分割します" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3490 +msgid "Split to objects" +msgstr "オブジェクトの分割" + +#: src/slic3r/GUI/Plater.cpp:2796 +msgid "Split to Objects" +msgstr "オブジェクトに分割" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1110 +msgid "Split to parts" +msgstr "パーツの分割" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1904 +msgid "Split to Parts" +msgstr "パーツに分割" + +#: src/libslic3r/PrintConfig.cpp:799 +msgid "Stars" +msgstr "スター型" + +#: src/slic3r/GUI/MainFrame.cpp:376 +msgid "Start a new project" +msgstr "新しいプロジェクトを開始" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:27 +msgid "Start at height" +msgstr "開始高さ" + +#: src/slic3r/GUI/Tab.cpp:1564 src/slic3r/GUI/Tab.cpp:1949 +#: src/libslic3r/PrintConfig.cpp:1736 src/libslic3r/PrintConfig.cpp:1751 +msgid "Start G-code" +msgstr "Gコードの最初" + +#: src/slic3r/GUI/MainFrame.cpp:403 +msgid "Start new slicing process" +msgstr "新しいスライスプロセスを開始する" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:35 +msgid "Start printing after upload" +msgstr "アップロード後にプリント開始" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:150 +msgid "Status" +msgstr "ステータス" + +#: src/slic3r/GUI/FirmwareDialog.cpp:782 +msgid "Status:" +msgstr "状況:" + +#: src/slic3r/GUI/Tab.cpp:2158 +msgid "Stealth" +msgstr "静音" + +#: src/slic3r/GUI/Plater.cpp:1084 +msgid "stealth mode" +msgstr "静音モード" + +#: src/slic3r/GUI/Plater.cpp:3545 +#, c-format +msgid "STL file exported to %s" +msgstr "%sにエクスポートされたSTLファイル" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:27 +msgid "Stop at height" +msgstr "高さで停止" + +#: src/slic3r/GUI/Tab.cpp:1716 src/slic3r/GUI/Tab.cpp:1901 +msgid "Success!" +msgstr "成功!" + +#: src/slic3r/GUI/PresetHints.cpp:200 +msgid "support" +msgstr "サポート" + +#: src/libslic3r/PrintConfig.cpp:2441 +msgid "Support base diameter" +msgstr "サポートベースの直径" + +#: src/libslic3r/PrintConfig.cpp:2451 +msgid "Support base height" +msgstr "サポートベースの高さ" + +#: src/libslic3r/PrintConfig.cpp:2566 +msgid "Support base safety distance" +msgstr "サポートベースの安全距離" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2547 +msgid "Support Blocker" +msgstr "サポート禁止" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2547 +msgid "Support Enforcer" +msgstr "サポート強制" + +#: src/slic3r/GUI/Tab.cpp:3401 +msgid "Support head" +msgstr "サポート先端" + +#: src/libslic3r/PrintConfig.cpp:2369 +msgid "Support head front diameter" +msgstr "サポートチップ径" + +#: src/libslic3r/PrintConfig.cpp:2378 +msgid "Support head penetration" +msgstr "頭部貫通をサポート" + +#: src/libslic3r/PrintConfig.cpp:2387 +msgid "Support head width" +msgstr "サポートの先端幅" + +#: src/slic3r/GUI/PresetHints.cpp:210 +msgid "support interface" +msgstr "サポートの接触部" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:30 src/slic3r/GUI/GUI_ObjectList.cpp:70 +#: src/slic3r/GUI/GUI_ObjectList.cpp:511 src/slic3r/GUI/GUI_Preview.cpp:236 +#: src/slic3r/GUI/Tab.cpp:1059 src/slic3r/GUI/Tab.cpp:1060 +#: src/libslic3r/PrintConfig.cpp:334 src/libslic3r/PrintConfig.cpp:1432 +#: src/libslic3r/PrintConfig.cpp:1780 src/libslic3r/PrintConfig.cpp:1786 +#: src/libslic3r/PrintConfig.cpp:1794 src/libslic3r/PrintConfig.cpp:1806 +#: src/libslic3r/PrintConfig.cpp:1816 src/libslic3r/PrintConfig.cpp:1824 +#: src/libslic3r/PrintConfig.cpp:1839 src/libslic3r/PrintConfig.cpp:1860 +#: src/libslic3r/PrintConfig.cpp:1871 src/libslic3r/PrintConfig.cpp:1887 +#: src/libslic3r/PrintConfig.cpp:1896 src/libslic3r/PrintConfig.cpp:1905 +#: src/libslic3r/PrintConfig.cpp:1916 src/libslic3r/PrintConfig.cpp:1930 +#: src/libslic3r/PrintConfig.cpp:1938 src/libslic3r/PrintConfig.cpp:1939 +#: src/libslic3r/PrintConfig.cpp:1948 src/libslic3r/PrintConfig.cpp:1956 +#: src/libslic3r/PrintConfig.cpp:1970 src/libslic3r/GCode/PreviewData.cpp:172 +msgid "Support material" +msgstr "サポート材" + +#: src/slic3r/GUI/GUI_Preview.cpp:237 src/libslic3r/PrintConfig.cpp:1904 +#: src/libslic3r/GCode/PreviewData.cpp:173 +msgid "Support material interface" +msgstr "サポートのオブジェクトとの接触レイヤー" + +#: src/libslic3r/PrintConfig.cpp:1957 +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 "傾斜角(90°=垂直)がこのしきい値以上のオーバーハングに対しては、サポート材は生成されません。 言いかえるとこの値は、サポート材なしでプリントできる最もキツいオーバーハングのことです。 自動検出の場合はゼロに設定します(ゼロを推奨)。" + +#: src/libslic3r/PrintConfig.cpp:1877 +msgid "Support material/raft interface extruder" +msgstr "コンタクトサポート/ラフトインターフェース用のエクストルーダー" + +#: src/libslic3r/PrintConfig.cpp:1851 +msgid "Support material/raft/skirt extruder" +msgstr "サポート材/ラフト/スカート用エクストルーダー" + +#: src/slic3r/GUI/Plater.cpp:423 src/libslic3r/PrintConfig.cpp:1815 +#: src/libslic3r/PrintConfig.cpp:2423 +msgid "Support on build plate only" +msgstr "サポートをビルドプレート(ベッド)のみに限定する" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:991 +msgid "Support parameter change" +msgstr "サポートパラメータの変更" + +#: src/slic3r/GUI/Tab.cpp:3406 +msgid "Support pillar" +msgstr "支柱" + +#: src/libslic3r/PrintConfig.cpp:2407 +msgid "Support pillar connection mode" +msgstr "サポート支柱の接続モード" + +#: src/libslic3r/PrintConfig.cpp:2397 +msgid "Support pillar diameter" +msgstr "サポート支柱の直径" + +#: src/libslic3r/PrintConfig.cpp:2499 +msgid "Support points density" +msgstr "サポートポイント密度" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1282 +msgid "Support points edit" +msgstr "サポートポイントの編集" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:78 src/slic3r/GUI/GUI_ObjectList.cpp:519 +#: src/slic3r/GUI/Plater.cpp:418 src/slic3r/GUI/Tab.cpp:3397 +#: src/slic3r/GUI/Tab.cpp:3398 src/libslic3r/PrintConfig.cpp:2363 +#: src/libslic3r/PrintConfig.cpp:2370 src/libslic3r/PrintConfig.cpp:2379 +#: src/libslic3r/PrintConfig.cpp:2388 src/libslic3r/PrintConfig.cpp:2398 +#: src/libslic3r/PrintConfig.cpp:2424 src/libslic3r/PrintConfig.cpp:2431 +#: src/libslic3r/PrintConfig.cpp:2442 src/libslic3r/PrintConfig.cpp:2452 +#: src/libslic3r/PrintConfig.cpp:2461 src/libslic3r/PrintConfig.cpp:2471 +#: src/libslic3r/PrintConfig.cpp:2480 src/libslic3r/PrintConfig.cpp:2490 +#: src/libslic3r/PrintConfig.cpp:2500 src/libslic3r/PrintConfig.cpp:2508 +msgid "Supports" +msgstr "サポート" + +#: src/slic3r/GUI/Plater.cpp:1018 +msgid "supports and pad" +msgstr "サポートとパッド" + +#: src/libslic3r/PrintConfig.cpp:1043 +msgid "Supports remaining times" +msgstr "残り時間をサポート" + +#: src/libslic3r/PrintConfig.cpp:1053 +msgid "Supports stealth mode" +msgstr "静音モードサポート" + +#: src/slic3r/GUI/Preferences.cpp:76 +msgid "Suppress \" - default - \" presets" +msgstr "「-デフォルト-」プリセットを非表示" + +#: src/slic3r/GUI/Preferences.cpp:78 +msgid "Suppress \" - default - \" presets in the Print / Filament / Printer selections once there are any other valid presets available." +msgstr "他の有効なプリセットが利用可能になったら、プリント/フィラメント/プリンターの選択で「−デフォルト−」プリセットを非表示にします。" + +#: src/slic3r/GUI/MainFrame.cpp:677 +msgid "SVG" +msgstr "SVG" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:119 +msgid "Switch to 3D" +msgstr "3Dモードに" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1242 +msgid "Switch to editing mode" +msgstr "編集モードに切替え" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:120 +msgid "Switch to Preview" +msgstr "プレビューに切替え" + +#: src/slic3r/GUI/wxExtensions.cpp:2412 +#, c-format +msgid "Switch to the %s mode" +msgstr "%sモードに切替え" + +#: src/slic3r/GUI/GUI_App.cpp:752 +msgid "" +"Switching the language will trigger application restart.\n" +"You will lose content of the plater." +msgstr "言語を切り替えると、アプリケーションが再起動します。プレートの内容が失われます。" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:327 +msgid "" +"Switching to simple settings will discard changes done in the advanced mode!\n" +"\n" +"Do you want to proceed?" +msgstr "" +"簡易な設定に切り替えると、上級モードで行われた変更が破棄されます!\n" +"\n" +"続行しますか?" + +#: src/libslic3r/PrintConfig.cpp:1949 +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:1947 +msgid "Synchronize with object layers" +msgstr "オブジェクトレイヤーと同期する" + +#: src/slic3r/GUI/MainFrame.cpp:557 +msgid "System &Info" +msgstr "システム情報" + +#: src/slic3r/GUI/SysInfoDialog.cpp:44 +msgid "System Information" +msgstr "システム情報" + +#: src/slic3r/GUI/Preset.cpp:930 src/slic3r/GUI/Preset.cpp:970 +#: src/slic3r/GUI/Preset.cpp:1035 src/slic3r/GUI/Preset.cpp:1067 +#: src/slic3r/GUI/PresetBundle.cpp:1488 src/slic3r/GUI/PresetBundle.cpp:1553 +msgid "System presets" +msgstr "システムプリセット" + +#: src/slic3r/GUI/GUI_App.cpp:662 +msgid "Take Configuration &Snapshot" +msgstr "構成スナップショットを撮る" + +#: src/slic3r/GUI/GUI_App.cpp:697 +msgid "Taking configuration snapshot" +msgstr "設定ファイルのスナップショット保存" + +#: src/libslic3r/PrintConfig.cpp:1980 +msgid "Temperature" +msgstr "温度" + +#: src/libslic3r/PrintConfig.cpp:1727 +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:1726 +msgid "Temperature variation" +msgstr "温度変化" + +#: src/slic3r/GUI/ConfigWizard.cpp:592 +msgid "Temperatures" +msgstr "温度" + +#: src/slic3r/GUI/Tab.cpp:1700 src/slic3r/GUI/Tab.cpp:1888 +msgid "Test" +msgstr "テスト" + +#: src/slic3r/GUI/BedShapeDialog.cpp:171 +msgid "Texture" +msgstr "テクスチャー" + +#: src/slic3r/GUI/FirmwareDialog.cpp:530 +#, c-format +msgid "The %s device could not have been found" +msgstr "%sデバイスが見つかりませんでした" + +#: src/slic3r/GUI/FirmwareDialog.cpp:417 +#, 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 "" +"%sデバイスが見つかりませんでした。\n" +"デバイスが接続されている場合は、USBコネクタの横にあるリセットボタンを押してください..." + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:640 +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 "" +"現在操作されているオブジェクトは傾斜しています(回転角度が90°の倍数ではありません)。\n" +"回転がオブジェクト座標に埋め込まれると、傾斜オブジェクトの不均一なスケーリングはワールド座標系でのみ可能になります。" + +#: src/libslic3r/PrintConfig.cpp:2462 +msgid "The default angle for connecting support sticks and junctions." +msgstr "サポートスティックとジャンクションを接続するためのデフォルトの角度。" + +#: src/libslic3r/SLAPrint.cpp:670 +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/PrintConfig.cpp:457 +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:955 +msgid "The extruder to use when printing infill." +msgstr "インフィルに使用するエクストルーダー" + +#: src/libslic3r/PrintConfig.cpp:1341 +msgid "The extruder to use when printing perimeters and brim. First extruder is 1." +msgstr "外周とブリム(縁)をプリントするときに使用するエクストルーダー。 1番目のエクストルーダーは1です。" + +#: src/libslic3r/PrintConfig.cpp:1669 +msgid "The extruder to use when printing solid infill." +msgstr "ソリッドインフィルで使用するエクストルーダー" + +#: src/libslic3r/PrintConfig.cpp:1879 +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は現在のエクストルーダーを使用してツールの変更を最小限に抑える)。 これはラフトにも影響します。" + +#: src/libslic3r/PrintConfig.cpp:1853 +msgid "The extruder to use when printing support material, raft and skirt (1+, 0 to use the current extruder to minimize tool changes)." +msgstr "サポート材料、ラフト(土台)、およびスカート(パーツを囲むアウトライン)をプリントするときに使用するエクストルーダー(1+、0は現在のエクストルーダーを使用してツールの変更を最小限に抑えます)。" + +#: src/libslic3r/PrintConfig.cpp:695 +msgid "The filament material type for use in custom G-codes." +msgstr "カスタムGコードで使用するフィラメント材料タイプ。" + +#: src/libslic3r/PrintConfig.cpp:3105 +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:1054 +msgid "The firmware supports stealth mode" +msgstr "ファームウェアはサイレントモードをサポートします" + +#: src/libslic3r/PrintConfig.cpp:351 +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 "最初のレイヤーは、設定された値によってXY平面で縮小され、1番目のレイヤーのダボつき、つまりエレファントフット効果を補正します。" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2726 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2813 src/slic3r/GUI/Tab.cpp:3279 +msgid "the following characters are not allowed:" +msgstr "次の文字は使用できません:" + +#: src/slic3r/GUI/Tab.cpp:3311 +msgid "the following suffix is not allowed:" +msgstr "以下のサフィックスは許可されていません :" + +#: src/libslic3r/PrintConfig.cpp:2702 +msgid "The gap between the object bottom and the generated pad in zero elevation mode." +msgstr "ゼロリフトモードでのオブジェクトの底面と生成されたパッド間のギャップ。" + +#: src/libslic3r/PrintConfig.cpp:2453 +msgid "The height of the pillar base cone" +msgstr "柱のベースコーンの高さ" + +#: src/libslic3r/PrintConfig.cpp:2481 +msgid "The max distance of two pillars to get linked with each other. A zero value will prohibit pillar cascading." +msgstr "相互接続のための2つの支柱間の最大距離。 値がゼロの場合、柱のカスケードが無効になります。" + +#: src/libslic3r/PrintConfig.cpp:2472 +msgid "The max length of a bridge" +msgstr "最長ブリッジ長さ" + +#: src/libslic3r/PrintConfig.cpp:2569 +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 "モデルからのサポートベースの最小距離(mm)。 パッドの上のゼロリフトモードでは、このパラメーターに応じたギャップがモデルとパッドの間に挿入されます。" + +#: src/libslic3r/PrintConfig.cpp:2176 +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平面で拡大/縮小されます。 これは、穴のサイズを微調整する場合に便利です。" + +#: src/libslic3r/PrintConfig.cpp:1433 +msgid "The object will be raised by this number of layers, and support material will be generated under it." +msgstr "オブジェクトは、このレイヤー数だけ持ち上げられ、その下にサポート材が生成されます。" + +#: src/libslic3r/PrintConfig.cpp:2259 +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" +"プリント領域が指定された値を超える場合、ティルト動作を遅くします。それ以外では-速いティルトとなります" + +#: src/slic3r/GUI/GUI_App.cpp:831 +msgid "The presets on the following tabs were modified" +msgstr "次のタブのプリセットが変更されました" + +#: src/libslic3r/PrintConfig.cpp:1768 +msgid "The printer multiplexes filaments into a single hot end." +msgstr "プリンタは、1つのホットエンドで複数のフィラメントを切り替えます。" + +#: src/slic3r/GUI/BedShapeDialog.cpp:342 +msgid "The selected file contains no geometry." +msgstr "選択したファイルにはジオメトリが含まれていません。" + +#: src/slic3r/GUI/BedShapeDialog.cpp:346 +msgid "The selected file contains several disjoint areas. This is not supported." +msgstr "選択したファイルには、接続していない面がいくつか含まれています。 これはサポートされていません。" + +#: src/slic3r/GUI/Plater.cpp:2271 +msgid "The selected object can't be split because it contains more than one volume/material." +msgstr "選択したオブジェクトには複数のボリューム/マテリアルが含まれているため、分割できません。" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1737 src/slic3r/GUI/Plater.cpp:2279 +msgid "The selected object couldn't be split because it contains only one part." +msgstr "選択したオブジェクトには、1つのパーツしか含まれていないため、分割できませんでした。" + +#: src/slic3r/GUI/MainFrame.cpp:410 +msgid "The selected project is no more available" +msgstr "選択したプロジェクトはもう利用できません" + +#: src/libslic3r/PrintConfig.cpp:2570 +msgid "The slope of the pad wall relative to the bed plane. 90 degrees means straight walls." +msgstr "ベッド平面に対するパッド壁の傾斜。 90度は真っ直ぐな壁を意味します。" + +#: src/libslic3r/PrintConfig.cpp:1544 +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:1536 +msgid "The speed for retractions (it only applies to the extruder motor)." +msgstr "吸込み速度(エクストルーダーモーターにのみ適用)。" + +#: src/libslic3r/Print.cpp:1187 +msgid "The Spiral Vase option can only be used when printing a single object." +msgstr "スパイラル花瓶オプションはオブジェクト一つのプリントに限られます。" + +#: src/libslic3r/Print.cpp:1189 +msgid "The Spiral Vase option can only be used when printing single material objects." +msgstr "スパイラル花瓶オプションは、単一の素材オブジェクトをプリントする場合にのみ使用できます。" + +#: src/slic3r/GUI/Tab.cpp:2900 +msgid "The supplied name is empty. It can't be saved." +msgstr "指定された名前が空白です。 保存できません。" + +#: src/slic3r/GUI/Tab.cpp:3287 +msgid "The supplied name is not available." +msgstr "指定された名前は使用できません。" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2725 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2812 src/slic3r/GUI/Tab.cpp:3278 +#: src/slic3r/GUI/Tab.cpp:3282 +msgid "The supplied name is not valid;" +msgstr "指定された名前は無効です;" + +#: src/libslic3r/Print.cpp:1268 +msgid "The supplied settings will cause an empty print." +msgstr "指定された設定では、何もプリントされません。" + +#: src/libslic3r/PrintConfig.cpp:2524 +msgid "The thickness of the pad and its optional cavity walls." +msgstr "パッドとそのオプションのキャビティ壁の厚さ。" + +#: src/libslic3r/PrintConfig.cpp:1825 +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は最初のオブジェクトレイヤーのブリッジフローと速度を使用しなくなります。" + +#: src/slic3r/GUI/Tab.cpp:2429 +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/libslic3r/Print.cpp:1306 +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 "ツールの変更を行わずに現在のエクストルーダーでプリントする場合、ワイプタワーは今のところ、非溶解性サポートのみをサポートします。 (support_material_extruderとsupport_material_interface_extruderの両方を0に設定する必要があります)。" + +#: src/libslic3r/Print.cpp:1200 +msgid "The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter and Repetier G-code flavors." +msgstr "ワイプタワーは現在、Marlin、RepRap/Sprinter、およびRepetierで生成されたGコードで使用できます。" + +#: src/libslic3r/Print.cpp:1202 +msgid "The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1)." +msgstr "ワイプタワーは現在、相対アドレス指定のエクストルーダー(use_relative_e_distances = 1)で利用できます。" + +#: src/libslic3r/Print.cpp:1225 +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:1227 +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でプリントする場合に利用できます" + +#: src/libslic3r/Print.cpp:1229 +msgid "The Wipe Tower is only supported for multiple objects if they are sliced equally." +msgstr "ワイプタワーは、複数のオブジェクトが均等にスライスされている場合に利用できます。" + +#: src/libslic3r/Print.cpp:1223 +msgid "The Wipe Tower is only supported for multiple objects if they have equal layer heights" +msgstr "ワイプタワーは、複数のオブジェクトのレイヤーの高さが等しい場合に利用できます。" + +#: src/libslic3r/Print.cpp:1155 +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:1258 +msgid "The Wipe tower is only supported if all objects have the same layer height profile" +msgstr "ワイプタワーは、すべてのオブジェクトのレイヤーの高さプロファイルが同じ場合に使用できます。" + +#: src/slic3r/GUI/UpdateDialogs.cpp:127 +#, c-format +msgid "This %s version: %s" +msgstr "この%sのバージョン: %s" + +#: src/libslic3r/PrintConfig.cpp:140 +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 "このコードは、オブジェクト別の順次プリンティングを使用するとき、オブジェクト間に挿入されます。 デフォルトでは、エクストルーダーとベッドの温度は非待機コマンドを使用します(M104/M140)。 ただし、このカスタムコードでM104、M109、M140またはM190が記述された場合、Slic3rは温度コマンドを追加しません。 すべてのSlic3r代替変数を使用できるため、「M109 S [first_layer_temperature]」コマンドを必要な場所に記述できます。" + +#: src/libslic3r/PrintConfig.cpp:2057 +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]." +msgstr "このカスタムコードは、エクストルーダー変更のたびに挿入されます。 ここに何かを記述した場合は、ツールの変更を自分で行う必要があります。PrusaSlicerはフィラメントを変更するためのその他のGコードを出力しません。 すべてのSlic3r設定と[previous_extruder]および[next_extruder]に代替変数を使用できます。 標準のtoolchangeコマンドは、T [next_extruder]としてスクリプト化できます。" + +#: src/libslic3r/PrintConfig.cpp:1032 +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 "このカスタムコードは、レイヤーが変わるたびに、Z移動の直後、エクストルーダがレイヤーの最初のポイントに移動する前に挿入されます。 [layer_num]および[layer_z]と同様に、すべてのSlic3r設定にワイルドカード変数を追加できます。" + +#: src/libslic3r/PrintConfig.cpp:129 +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 "このカスタムコードは、レイヤーが変更されるたびにZ移動の直前に挿入されます。 [layer_num]および[layer_z]と同様に、すべてのSlic3r代替変数が使用できます。" + +#: src/libslic3r/PrintConfig.cpp:2057 +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 "このカスタムコードは、すべてのツール変更の前に挿入されます。 すべてのPrusaSlicer設定と{previous_extruder}および{next_extruder}の代替変数が使用できます。 希望するエクストルーダーに変更するツール変更コマンド(T {next_extruder}など)が含まれている場合、PrusaSlicerが同じコマンドを追加することはありません。 したがって、ツール変更の前後にカスタム動作をスクリプト化することが可能です。" + +#: src/libslic3r/PrintConfig.cpp:380 +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 "この終了手順は、出力ファイルの最後のプリント完了Gコードの前(マルチマテリアルプリンタの場合は現在のフィラメントからのツール変更の前)に挿入されます。 すべてのPrusaSlicer設定に代替変数を使用できます。 複数のエクストルーダーがある場合、Gコードはエクストルーダー順に処理されます。" + +#: src/libslic3r/PrintConfig.cpp:370 +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 "この終了プロシージャは、出力ファイルの最後に挿入されます。 すべてのPrusaSlicer変数を使用できます。" + +#: src/libslic3r/PrintConfig.cpp:1193 src/libslic3r/PrintConfig.cpp:1204 +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 "この試用的な設定は、押出し速度の変化を制限するために使用します。 1.8mm³/s²の値は、1.8mm³/ s(射出幅0.45mm、レイヤー高さ0.2mm、送り速度20mm/s)の押出し速度から5.4mm³/s( 送り速度60 mm/s)への変化に少なくとも2秒かかることを意味します。" + +#: src/libslic3r/PrintConfig.cpp:1183 +msgid "This experimental setting is used to set the maximum volumetric speed your extruder supports." +msgstr "この試用的な設定で、エクストルーダーがサポートする最大の体積押出し速度を設定できます。" + +#: src/libslic3r/PrintConfig.cpp:2061 +msgid "This experimental setting uses G10 and G11 commands to have the firmware handle the retraction. This is only supported in recent Marlin." +msgstr "この試用的な設定で、G10およびG11コマンドを使用して、ファームウェア吸込み(リトラクション)を行うことができます。 これは最近のMarlinでのみサポートされています。" + +#: src/libslic3r/PrintConfig.cpp:2075 +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 "この試用的な設定で、線形ミリメートルではなく立方ミリメートルでE値を定義できます。 ファームウェアでフィラメント径が未定義の場合、開始Gコードに「M200 D [filament_diameter_0] T0」のようなコマンドを入力して、体積押出しモードをオンにし、Slic3rで選択したフィラメントに関連付けられたフィラメント径を使用できます。 これは最近のMarlinのみサポートされています。" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2848 +msgid "This extruder will be set for selected items" +msgstr "このエクストルーダーを選択されたアイテムに設定します" + +#: src/libslic3r/PrintConfig.cpp:188 +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 "この値は、ブリッジが形成されるときに押出すプラスチックの量に影響します。この値をわずかに下げることで、押し出したものを引き戻しブリッジのたるみを減らすことができますが、通常はプリセット値が適切であり、この値よりも冷却(ファンを使用)を試す方をお勧めします。" + +#: src/libslic3r/PrintConfig.cpp:514 +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 "この係数は、流量を比率で変化させます。 この設定を微調整することで、表面をきれいに仕上げ、単一壁の幅を調整できる場合があります。 通常の値は0.9〜1.1です。 さらなるチューニングが必要な場合は、フィラメントの直径とファームウェアのEステップをチェックしてください。" + +#: src/libslic3r/PrintConfig.cpp:178 +msgid "This fan speed is enforced during all bridges and overhangs." +msgstr "設定されたファン速度は、ブリッジとオーバーハングを作成するときに常に使用されます。" + +#: src/libslic3r/PrintConfig.cpp:944 +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:1677 +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 "この機能により、強制的に指定されたレイヤーごとにソリッドレイヤー(塗りつぶし)を生成します。 無効にする場合はゼロ。 任意の値(たとえば、9999)が設定できます。 Slic3rは、ノズル径とレイヤー高さに応じて、組合わせ可能なレイヤーの最大数を自動的に計算します。" + +#: src/libslic3r/PrintConfig.cpp:1718 +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 "この機能は、レイヤーごとにプリントするのではなく、Zを徐々に上げながらオブジェクトの最外周だけを一筆書きで連続してプリントします。 このオプションを使うには、外周は1周で、インフィル(中塗り)なし、上部ソリッドレイヤー(塗りつぶし)なし、サポートなしに設定しなければなりません。 底部ソリッドレイヤーとスカート(パーツを囲むアウトライン)/ブリム(縁)ループの設定はできます。 2つ以上のオブジェクトをプリントする場合は使えません。" + +#: src/slic3r/GUI/Plater.cpp:1712 +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:1658 +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?" +msgstr "このファイルには、複数の高さに配置されたいくつかのオブジェクトが含まれています。 それらを複数のオブジェクトと見なすのではなく、複数のパーツから構成される単一のオブジェクトと見なすべきですか?" + +#: src/slic3r/GUI/FirmwareDialog.cpp:313 +#, 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 "" +"このファームウェアhexファイルは、プリンターモデルと一致しません。\n" +"16進ファイルの対象:%s\n" +"報告されたプリンター:%s\n" +"\n" +"ともかくこのhexファイルでファームウェアの書換えを続けますか?\n" +"絶対に間違いないと確信している場合にのみ続行してください。" + +#: src/libslic3r/PrintConfig.cpp:278 +msgid "This flag enables the automatic cooling logic that adjusts print speed and fan speed according to layer printing time." +msgstr "このフラグは、レイヤーのプリント時間に応じてプリント速度とファン速度を調整する自動冷却プログラムを有効にします。" + +#: src/slic3r/GUI/Plater.cpp:448 +msgid "This flag enables the brim that will be printed around each object on the first layer." +msgstr "このフラグは、1番目のレイヤーの各オブジェクトの外周を拡張してプリントされるブリム(縁)を有効にします。" + +#: src/libslic3r/PrintConfig.cpp:1468 +msgid "This flag enforces a retraction whenever a Z move is done." +msgstr "このオプションは、Z移動が実行されるたびに樹脂の吸引を行います。" + +#: src/libslic3r/PrintConfig.cpp:2093 +msgid "This flag will move the nozzle while retracting to minimize the possible blob on leaky extruders." +msgstr "このフラグは、待避中にノズルを動かして、垂れやすいエクストルーダーで起こりうるブロブの発生を最小限に抑えます。" + +#: src/slic3r/GUI/Tab.cpp:921 +msgid "This is a default preset." +msgstr "これはデフォルトのプリセットです。" + +#: src/libslic3r/PrintConfig.cpp:2501 +msgid "This is a relative measure of support points density." +msgstr "サポートポイント密度の相対値です。" + +#: src/slic3r/GUI/Tab.cpp:2528 +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:923 +msgid "This is a system preset." +msgstr "これはシステムプリセットです。" + +#: src/libslic3r/PrintConfig.cpp:491 src/libslic3r/PrintConfig.cpp:551 +msgid "This is only used in the Slic3r interface as a visual help." +msgstr "これはSlic3rのみで使用されるイラストです。" + +#: src/libslic3r/PrintConfig.cpp:300 +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:158 +msgid "This is the acceleration your printer will use for bridges. Set zero to disable acceleration control for bridges." +msgstr "ブリッジを作成するときのプリンターアクセラレーションを設定します。 ブリッジの加速制御を無効にするには、ゼロを入力します。" + +#: src/libslic3r/PrintConfig.cpp:813 +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:934 +msgid "This is the acceleration your printer will use for infill. Set zero to disable acceleration control for infill." +msgstr "これはインフィル生成時のプリンタの加速度です。 インフィルの加速制御をオフにするには、ゼロに設定します。" + +#: src/libslic3r/PrintConfig.cpp:1331 +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 "これは、外周プリントに使用する加速度です。 ハードウェアが対応している場合、9000のような高い値で良い結果をもたらします。 外周プリント用の加速制御を無効にするには、ゼロを設定します。" + +#: src/libslic3r/PrintConfig.cpp:1262 +msgid "This is the diameter of your extruder nozzle (for example: 0.5, 0.35 etc.)" +msgstr "エクストルーダーノズルの内径(例:0.5, 0.35など)" + +#: src/libslic3r/PrintConfig.cpp:1162 +#, 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%です。 0に設定すると、レイヤーの高さはノズル径の75%に制限されます。" + +#: src/libslic3r/PrintConfig.cpp:1225 +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 "このエクストルーダーの最小プリント可能なレイヤー高さ。 可変レイヤー高の解像度を制限します。一般的な値は0.05mmと0.1mmの間です。" + +#: src/libslic3r/PrintConfig.cpp:2139 +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/slic3r/GUI/GUI_ObjectManipulation.cpp:643 +msgid "" +"This operation is irreversible.\n" +"Do you want to proceed?" +msgstr "" +"この操作は元に戻せません。\n" +"続行しますか?" + +#: src/libslic3r/PrintConfig.cpp:1372 +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 "このオプションは、各レイヤーに対して生成する外周数を設定します。 拡張外周オプションが有効になっている場合、Slic3rは、この外周数ではカバーできない傾斜面を検出すると、自動的にこの数よりも多くの外周が生成されます。" + +#: src/libslic3r/PrintConfig.cpp:1287 +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:980 +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 "このオプションは、天井形成を可能にするために最低限必要な領域のみインフィル(中塗り)を行います(内部サポート材料として機能します)。 このオプションを有効にすると、複数のチェックによりGコードの生成が遅くなります。" + +#: src/libslic3r/PrintConfig.cpp:973 +msgid "This option will switch the print order of perimeters and infill, making the latter first." +msgstr "このオプションは、外周とインフィル(中塗り)のプリント順序を切替え、インフィルからプリントします。" + +#: src/libslic3r/PrintConfig.cpp:427 +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 "この個別の設定は、最外周のプリント速度に影響します(モデルの露出部分)。 パーセンテージで入力された場合(例:80%)、上記で設定された外周プリント速度から計算されます。 0を入力すると自動計算になります。" + +#: src/libslic3r/PrintConfig.cpp:1648 +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 "この個別の設定は、半径<= 6.5mm(通常は穴)の外周プリントの速度に影響します。 パーセンテージ(例:80%)で入力された場合、上記の外周速度設定で計算されます。 自動の場合はゼロに設定します。" + +#: src/libslic3r/PrintConfig.cpp:989 +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 "この設定は、インフィル(中塗り)と外周の間に追加でオーバーラップ(重なり)させて、接続性を改善します。 理論的にこれは必要ありませんが、機械的な遊びの影響によりギャップが生じる可能性がある場合に有効です。 パーセンテージ(例:15%)で表される場合、外周の射出幅から計算されます。" + +#: src/libslic3r/PrintConfig.cpp:57 +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 "この設定は、スライス/レイヤーの高さ(および合計数)を制御します。 レイヤーが薄いほど精度は上がりますが、プリントに時間がかかります。" + +#: src/libslic3r/PrintConfig.cpp:1153 +msgid "This setting represents the maximum speed of your fan." +msgstr "ファンの最大速度を設定します。" + +#: src/libslic3r/PrintConfig.cpp:1216 +msgid "This setting represents the minimum PWM your fan needs to work." +msgstr "この設定は、ファンが回転するために必要な最小PWMです。" + +#: src/libslic3r/PrintConfig.cpp:1801 +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 "この開始プロシージャーは、プリンタがGコードを開始した後(およびマルチマテリアルプリンタの場合はこのフィラメントにツールを変更した後)の最初に挿入されます。 これは、特定のフィラメントの設定をオーバーライドするために使用されます。カスタムコードでM104またはM190が書かれている場合には、同種のコマンドが自動的に追加されることはありませんので、加熱コマンドやその他のカスタムアクションの順序を自由にカスタマイズできます。 全てのPrusaSlicer変数を使用できますので、「M109 S [first_layer_temperature]」コマンドをご希望の場所に配置できます。複数のエクストルーダーがある場合、Gコードはエクストルーダーの順に処理されます。" + +#: src/libslic3r/PrintConfig.cpp:1786 +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 "この開始プロシージャーは、ベッドが目標温度に達し、エクストルーダーが加熱を開始した直後、およびエクストルーダーが加熱を完了する前のところに挿入されます。 カスタムコードでM104またはM190が書かれている場合には、同種のコマンドが自動的に追加されることはありませんので、加熱コマンドやその他のカスタムアクションの順序を自由にカスタマイズできます。 全てのPrusaSlicer変数を使用できますので、「M109 S [first_layer_temperature]」コマンドをご希望の場所に配置できます。" + +#: src/libslic3r/PrintConfig.cpp:664 +msgid "This string is edited by RammingDialog and contains ramming specific parameters." +msgstr "この文字列はラミングダイアログで編集され、ラミング固有のパラメーターが含まれています。" + +#: src/libslic3r/PrintConfig.cpp:2185 +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座標に対して加算/減算されます。これによって Zエンドストップの位置を補正できます。例えば、エンドストップで0のとき、実際にはノズルがベッド面から0.3mm離れる場合、これを-0.3に設定します(もしくはエンドストップ位置を修正します)。" + +#: src/libslic3r/PrintConfig.cpp:2132 +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/slic3r/GUI/UpdateDialogs.cpp:118 +#, 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 "このバージョン%sは、現在インストールされているセットアップパッケージと互換性がありません。これは、新しいバージョンを使用した後に古いバージョンの%sを実行したことが原因である可能性があります。 %sを終了して新しいバージョンで再試行するか、再起動してデフォルト構成をロードしてください。 このバージョン%sと互換性のある設定をインストールする前に、現在の構成のバックアップが作成されます。" + +#: src/libslic3r/PrintConfig.cpp:2284 +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 "ラスター2Dポリゴンにガンマ補正を適用します。 ガンマ値ゼロは、しきい値を中央に設定することを意味します。 この動作により、ポリゴンの穴を損なうことなくアンチエイリアスが除去されます。" + +#: src/libslic3r/PrintConfig.cpp:1994 +msgid "Threads" +msgstr "スレッド" + +#: src/libslic3r/PrintConfig.cpp:1995 +msgid "Threads are used to parallelize long-running tasks. Optimal threads number is slightly above the number of available cores/processors." +msgstr "スレッドは、長時間実行されるタスクを並列化するために使用されます。スレッド数は、使用可能なコア/プロセッサーの数をわずかに超えたところが最適となります。" + +#: src/slic3r/GUI/Tab.cpp:2052 +msgid "Tilt" +msgstr "チルト" + +#: src/slic3r/GUI/Tab.cpp:2053 +msgid "Tilt time" +msgstr "チルト時間" + +#: src/slic3r/GUI/RammingChart.cpp:76 +msgid "Time" +msgstr "時間" + +#: src/libslic3r/PrintConfig.cpp:655 +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 "ツールの変更中(Tコードの実行時)にプリンターファームウェア(またはMulti Material Unit 2.0)が新しいフィラメントをロードする時間。 この時間は、Gコード時間推定プログラムによって合計プリント時間に追加されます。" + +#: src/libslic3r/PrintConfig.cpp:670 +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 "ツールチェンジ中(Tコードの実行時)にプリンターファームウェア(またはMulti Material Unit 2.0)がフィラメントをアンロードする時間。 この時間は、Gコード時間予測プログラムによって合計プリント予測時間に追加されます。" + +#: src/libslic3r/PrintConfig.cpp:2242 +msgid "Time of the fast tilt" +msgstr "高速チルトの時間" + +#: src/libslic3r/PrintConfig.cpp:2251 +msgid "Time of the slow tilt" +msgstr "スローチルトの時間" + +#: src/libslic3r/PrintConfig.cpp:610 +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/slic3r/GUI/Tab.cpp:916 +msgid "To do that please specify a new name for the preset." +msgstr "これを行うには、プリセットの新しい名前を指定してください。" + +#: src/slic3r/GUI/Plater.cpp:2966 +msgid "To objects" +msgstr "オブジェクト" + +#: src/slic3r/GUI/Plater.cpp:2968 +msgid "To parts" +msgstr "パーツへ" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:212 +#, c-format +msgid "Toggle %c axis mirroring" +msgstr "%c軸のミラーリングを切替え" + +#: src/libslic3r/Zipper.cpp:37 +msgid "too many files" +msgstr "ファイルが多すぎます" + +#: src/slic3r/GUI/GUI_Preview.cpp:217 src/slic3r/GUI/GUI_Preview.cpp:315 +#: src/slic3r/GUI/GUI_Preview.cpp:481 src/slic3r/GUI/GUI_Preview.cpp:537 +#: src/slic3r/GUI/GUI_Preview.cpp:713 src/libslic3r/GCode/PreviewData.cpp:404 +msgid "Tool" +msgstr "ツール" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:240 +msgid "Tool #" +msgstr "ツール#" + +#: src/slic3r/GUI/Tab.cpp:1973 src/libslic3r/PrintConfig.cpp:2006 +msgid "Tool change G-code" +msgstr "ツールチェンジ用のGコード" + +#: src/slic3r/GUI/Tab.cpp:1530 +msgid "Toolchange parameters with single extruder MM printers" +msgstr "単一エクストルーダーMMプリンターのツールチェンジパラメーター" + +#. TRN To be shown in the main menu View->Top +#: src/slic3r/GUI/MainFrame.cpp:522 +msgid "Top" +msgstr "トップ" + +#: src/libslic3r/PrintConfig.cpp:388 +msgid "Top fill pattern" +msgstr "トップ塗りつぶしパターン" + +#: src/slic3r/GUI/PresetHints.cpp:189 +msgid "top solid infill" +msgstr "最上層のソリッドインフィル" + +#: src/slic3r/GUI/GUI_Preview.cpp:232 src/libslic3r/PrintConfig.cpp:2017 +#: src/libslic3r/PrintConfig.cpp:2028 src/libslic3r/GCode/PreviewData.cpp:168 +msgid "Top solid infill" +msgstr "トップソリッドインフィル" + +#: src/libslic3r/PrintConfig.cpp:2046 +msgid "Top solid layers" +msgstr "上部ソリッドレイヤー" + +#: src/slic3r/GUI/MainFrame.cpp:522 +msgid "Top View" +msgstr "上面" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:247 +msgid "Total purging volume is calculated by summing two values below, depending on which tools are loaded/unloaded." +msgstr "総パージ量は、ロード/アンロードされるツールに応じて、以下の2つの値を合計して計算されます。" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:84 +msgid "Total rammed volume" +msgstr "合計ラミング容積" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:82 +msgid "Total ramming time" +msgstr "トータルラミング時間" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:252 +msgid "Translate" +msgstr "移動" + +#: src/slic3r/GUI/GUI_Preview.cpp:243 src/libslic3r/PrintConfig.cpp:2051 +msgid "Travel" +msgstr "移動" + +#: src/libslic3r/PrintConfig.cpp:798 +msgid "Triangles" +msgstr "三角形" + +#: src/libslic3r/PrintConfig.cpp:3059 +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:1397 +msgid "Type of the printer." +msgstr "プリンターのタイプ" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2549 +msgid "Type:" +msgstr "タイプ:" + +#: src/libslic3r/Zipper.cpp:35 +msgid "undefined error" +msgstr "未定義エラー" + +#: 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:3497 +#, c-format +msgid "Undo %1$d Action" +msgid_plural "Undo %1$d Actions" +msgstr[0] "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3479 +msgid "Undo History" +msgstr "履歴を元に戻す" + +#: src/libslic3r/Zipper.cpp:59 +msgid "unexpected decompressed size" +msgstr "予期しない解凍サイズ" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:27 +msgid "Unknown" +msgstr "不明" + +#: src/slic3r/Utils/Duet.cpp:84 src/slic3r/Utils/Duet.cpp:154 +msgid "Unknown error occured" +msgstr "不明なエラーが発生" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:234 +msgid "unloaded" +msgstr "アンロード済" + +#: src/libslic3r/PrintConfig.cpp:591 +msgid "Unloading speed" +msgstr "アップロードスピード" + +#: src/libslic3r/PrintConfig.cpp:600 +msgid "Unloading speed at the start" +msgstr "最初のアンロードスピード" + +#: src/slic3r/GUI/Tab.cpp:3069 +msgid "UNLOCKED LOCK" +msgstr "開いたカギ" + +#: src/slic3r/GUI/Tab.cpp:3362 +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 "" +"カギが開いたアイコンは、一部の設定が変更され、現在のオプショングループのシステム(またはデフォルト)値と等しくないことを示します。\n" +"クリックすると、現在のオプショングループのすべての設定がシステム(またはデフォルト)値にリセットされます。" + +#: src/slic3r/GUI/Tab.cpp:3377 +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/GUI_Preview.cpp:245 +msgid "Unretractions" +msgstr "待避からの復帰" + +#: src/slic3r/GUI/Tab.cpp:2785 +msgid "Unsaved Changes" +msgstr "変更の未保存" + +#: src/slic3r/GUI/GUI_App.cpp:790 +msgid "Unsaved Presets" +msgstr "未保存のプリセット" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:154 +msgid "Unselect gizmo / Clear selection" +msgstr "ギズモ選択を解除/選択を解除" + +#: src/libslic3r/Zipper.cpp:63 +msgid "unsupported central directory size" +msgstr "サポートされていない中心ディレクトリのサイズ" + +#: src/libslic3r/Zipper.cpp:43 +msgid "unsupported encryption" +msgstr "サポートされていない暗号化" + +#: src/libslic3r/Zipper.cpp:45 +msgid "unsupported feature" +msgstr "サポートされていない機能" + +#: src/libslic3r/Zipper.cpp:41 +msgid "unsupported method" +msgstr "サポートされていない方法" + +#: src/libslic3r/Zipper.cpp:53 +msgid "unsupported multidisk archive" +msgstr "サポートされていないマルチディスクアーカイブ" + +#: src/slic3r/GUI/GUI_App.cpp:305 +msgid "Unsupported OpenGL version" +msgstr "サポートされていないOpenGLバージョン" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2414 +msgid "Unsupported selection" +msgstr "サポートしないところの選択" + +#: src/libslic3r/GCode/PreviewData.cpp:495 +#, c-format +msgid "up to %.2f mm" +msgstr "最大%.2f mm" + +#: src/slic3r/GUI/UpdateDialogs.cpp:30 +msgid "Update available" +msgstr "アップデート可能" + +#: src/slic3r/GUI/ConfigWizard.cpp:419 src/slic3r/GUI/Preferences.cpp:69 +msgid "Update built-in Presets automatically" +msgstr "組込みプリセットを自動的に更新する" + +#: src/slic3r/GUI/ConfigWizard.cpp:401 +msgid "Updates" +msgstr "アップデート情報" + +#: src/slic3r/GUI/ConfigWizard.cpp:426 +msgid "Updates are never applied without user's consent and never overwrite user's customized settings." +msgstr "更新プログラムはユーザーの知らないうちにインストールされることはなく、カスタマイズされた設定を上書きすることもありません。" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:18 +msgid "Upgrade" +msgstr "アップグレード" + +#: src/slic3r/GUI/GUI_App.cpp:685 +msgid "Upload a firmware image into an Arduino based printer" +msgstr "ファームウェアイメージをArduinoベースのプリンターにアップロードする" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:33 +msgid "Upload to Printer Host with the following filename:" +msgstr "次のファイル名でプリンターサーバーにアップロードします:" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:229 +msgid "Uploading" +msgstr "アップロード" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:171 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:173 +msgid "Upper Layer" +msgstr "上のレイヤー" + +#: src/slic3r/GUI/Tab.cpp:1873 +msgid "USB/Serial connection" +msgstr "USB/シリアル 接続" + +#: src/libslic3r/PrintConfig.cpp:1592 +msgid "USB/serial port for printer connection." +msgstr "プリンター接続用USB/シリアルポート" + +#: src/slic3r/GUI/Preferences.cpp:117 +msgid "Use custom size for toolbar icons" +msgstr "ツールバーアイコンにカスタムサイズを使用する" + +#: src/libslic3r/PrintConfig.cpp:2060 +msgid "Use firmware retraction" +msgstr "ファームウェア吸込みを使用" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:42 +msgid "Use forward slashes ( / ) as a directory separator if needed." +msgstr "必要に応じて、ディレクトリ区切り文字としてスラッシュ(/)を使用してください。" + +#: src/libslic3r/PrintConfig.cpp:2515 +msgid "Use pad" +msgstr "台座を使用" + +#: src/slic3r/GUI/Preferences.cpp:110 +msgid "Use perspective camera" +msgstr "パースカメラを使用" + +#: src/libslic3r/PrintConfig.cpp:2067 +msgid "Use relative E distances" +msgstr "E相対距離モードを使用する" + +#: src/slic3r/GUI/Preferences.cpp:103 +msgid "Use Retina resolution for the 3D scene" +msgstr "3DシーンにRetina解像度を使用する" + +#: src/libslic3r/PrintConfig.cpp:508 +msgid "Use this option to set the axis letter associated to your printer's extruder (usually E but some printers use A)." +msgstr "このオプションを使用して、プリンターのエクストルーダーに関連付けられている軸ラベルを設定します(通常はEですが、一部のプリンターはAを使用します)。" + +#: src/libslic3r/PrintConfig.cpp:1807 +msgid "Use this setting to rotate the support material pattern on the horizontal plane." +msgstr "この設定を使用して、水平面上でサポート材料パターンを回転します。" + +#: src/libslic3r/PrintConfig.cpp:2074 +msgid "Use volumetric E" +msgstr "体積押出しEを使用" + +#: src/slic3r/GUI/Plater.cpp:214 +msgid "Used Filament (g)" +msgstr "使用フィラメント(g)" + +#: src/slic3r/GUI/Plater.cpp:212 src/slic3r/GUI/Plater.cpp:1041 +msgid "Used Filament (m)" +msgstr "使用フィラメント(m)" + +#: src/slic3r/GUI/Plater.cpp:213 +msgid "Used Filament (mm³)" +msgstr "使用フィラメント (mm³)" + +#: src/slic3r/GUI/Plater.cpp:1015 +msgid "Used Material (ml)" +msgstr "使用材料(ml)" + +#: src/slic3r/GUI/Plater.cpp:215 +msgid "Used Material (unit)" +msgstr "使用材料(単位)" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:24 +msgid "User" +msgstr "ユーザー" + +#: src/slic3r/GUI/Preset.cpp:974 src/slic3r/GUI/Preset.cpp:1071 +#: src/slic3r/GUI/PresetBundle.cpp:1558 +msgid "User presets" +msgstr "ユーザープリセット" + +#: src/libslic3r/Zipper.cpp:93 +msgid "validation failed" +msgstr "検証が失敗しました" + +#: src/slic3r/GUI/ButtonsDescription.cpp:41 +msgid "Value is the same as the system value" +msgstr "システム値と同じ値です" + +#: src/slic3r/GUI/ButtonsDescription.cpp:58 +msgid "Value was changed and is not equal to the system value or the last saved preset" +msgstr "値が変更されており、システム値または最後に保存されたプリセットとは異なっています" + +#: src/slic3r/GUI/Tab.cpp:2151 +msgid "Values in this column are for Normal mode" +msgstr "この列の値は通常モード用です" + +#: src/slic3r/GUI/Tab.cpp:2157 +msgid "Values in this column are for Stealth mode" +msgstr "この列の値はサイレントモード用です" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:58 +msgid "variants" +msgstr "バリアント" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:52 src/slic3r/GUI/Tab.cpp:921 +msgid "vendor" +msgstr "メーカー" + +#: src/libslic3r/PrintConfig.cpp:880 +msgid "Verbose G-code" +msgstr "コメント付きGコード" + +#: src/slic3r/GUI/AboutDialog.cpp:67 src/slic3r/GUI/MainFrame.cpp:53 +msgid "Version" +msgstr "バージョン" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:52 +msgid "version" +msgstr "バージョン" + +#: src/slic3r/GUI/Tab.cpp:1002 +msgid "Vertical shells" +msgstr "外壁設定" + +#: src/slic3r/GUI/GUI_Preview.cpp:209 +msgid "View" +msgstr "ビュー" + +#: src/libslic3r/SLAPrint.cpp:857 src/libslic3r/SLAPrint.cpp:867 +#: src/libslic3r/SLAPrint.cpp:915 +msgid "Visualizing supports" +msgstr "サポートの視覚化" + +#: src/slic3r/GUI/Plater.cpp:138 +msgid "Volume" +msgstr "ボリューム" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:248 +msgid "Volume to purge (mm³) when the filament is being" +msgstr "フィラメントをロード/アンロードするときにパージする量(mm³)" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1010 +msgid "Volumes in Object reordered" +msgstr "オブジェクトのボリュームが並べ替えられました" + +#: src/slic3r/GUI/PresetHints.cpp:216 +msgid "Volumetric" +msgstr "体積押出し" + +#: src/slic3r/GUI/Tab.cpp:1800 +msgid "Volumetric flow hints not available" +msgstr "体積押出し流量のヒントは利用できません" + +#: src/slic3r/GUI/GUI_Preview.cpp:216 +msgid "Volumetric flow rate" +msgstr "体積押出し流量" + +#: src/libslic3r/GCode/PreviewData.cpp:402 +msgid "Volumetric flow rate (mm3/s)" +msgstr "体積押出し量 (mm³/s)" + +#: src/slic3r/GUI/RammingChart.cpp:81 +msgid "Volumetric speed" +msgstr "体積押出し速度" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1168 src/slic3r/GUI/GUI.cpp:283 +#: src/slic3r/GUI/WipeTowerDialog.cpp:44 src/slic3r/GUI/WipeTowerDialog.cpp:328 +msgid "Warning" +msgstr "注意" + +#: src/slic3r/GUI/ConfigWizard.cpp:294 +msgid "Welcome" +msgstr "ようこそ" + +#: src/slic3r/GUI/ConfigWizard.cpp:296 +#, c-format +msgid "Welcome to the %s Configuration Assistant" +msgstr "%s構成アシスタントへようこそ" + +#: src/slic3r/GUI/ConfigWizard.cpp:298 +#, c-format +msgid "Welcome to the %s Configuration Wizard" +msgstr "%s構成ウィザードへようこそ" + +#: src/slic3r/GUI/Preferences.cpp:86 +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/PresetHints.cpp:223 +msgid "when printing" +msgstr "プリントするとき" + +#: src/libslic3r/PrintConfig.cpp:217 +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 "マルチマテリアルオブジェクトをプリントする場合、この設定により、Slic3rは重なり合うオブジェクト部分を1つずつカットします(2番目の部分は1番目、3番目の部分は1番目、2番目などでカットされます)。" + +#: src/libslic3r/PrintConfig.cpp:269 +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は警告を発するとともに、エクストルーダーの衝突を防ごうとしますが、注意が必要です。" + +#: src/libslic3r/PrintConfig.cpp:843 +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 "非常に薄いレイヤーでプリントする場合、プリントエリア内でのノズルービルドプレート(ベッド)間のバラツキによりベッドとの密着が損なわれないよう1番目のレイヤーは他のレイヤーより厚めにプリントして対策します。 1番目のレイヤーの厚みを、絶対値またはパーセンテージ(例:150%)で入力します。" + +#: src/libslic3r/PrintConfig.cpp:1483 +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:1475 +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:1347 +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 "ゼロに設定すると、ロード中にフィラメントがパーキング位置から押し出される距離は、アンロード中に戻った距離と同一になります。 正の場合、その分多くロードされ、逆に負の場合は、ロード距離はアンロードよりも短くなります。" + +#: src/libslic3r/PrintConfig.cpp:1173 +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 "他の速度設定を0にすると、Slic3rはエクストルーダー押圧を一定に保つために最適な速度を自動計算します。 この試用的な設定は、許容できる最高のプリント速度を設定するために用意されています。" + +#: src/libslic3r/PrintConfig.cpp:1527 +msgid "When the retraction is compensated after changing tool, the extruder will push this additional amount of filament." +msgstr "ツールの交換後に吸込み分が補正されると、エクストルーダーはこの追加量のフィラメントを押し出します。" + +#: src/libslic3r/PrintConfig.cpp:1519 +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/slic3r/GUI/Tab.cpp:3076 +msgid "WHITE BULLET" +msgstr "白丸" + +#: src/slic3r/GUI/Tab.cpp:3365 +msgid "WHITE BULLET icon indicates a non system (or non default) preset." +msgstr "白丸アイコンは、システム(またはデフォルト)プリセットでないことを示します。" + +#: src/slic3r/GUI/Tab.cpp:3111 +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:3126 +msgid "WHITE BULLET icon indicates that the value is the same as in the last saved preset." +msgstr "白丸アイコンは、値が最後に保存されたプリセットと同じであることを示します。" + +#: src/slic3r/GUI/GUI_Preview.cpp:214 src/libslic3r/PrintConfig.cpp:2137 +msgid "Width" +msgstr "幅" + +#: src/libslic3r/GCode/PreviewData.cpp:398 +msgid "Width (mm)" +msgstr "幅(mm)" + +#: src/libslic3r/PrintConfig.cpp:2389 +msgid "Width from the back sphere center to the front sphere center" +msgstr "後部ボールの中心から前部ボールの中心までの幅" + +#: src/libslic3r/PrintConfig.cpp:2138 +msgid "Width of a wipe tower" +msgstr "ワイプタワーの幅" + +#: src/libslic3r/PrintConfig.cpp:2761 +msgid "Width of the connector sticks which connect the object and the generated pad." +msgstr "オブジェクトと生成されたパッドを接続するコネクタスティックの幅。" + +#: src/libslic3r/PrintConfig.cpp:2203 +msgid "Width of the display" +msgstr "ディスプレイの幅" + +#: src/slic3r/GUI/PresetHints.cpp:47 +msgid "will always run at %1%%%" +msgstr "常に%1%%%で実行されます" + +#: src/slic3r/GUI/PresetHints.cpp:52 +msgid "will be turned off." +msgstr "オフになります。" + +#: src/libslic3r/PrintConfig.cpp:2276 +msgid "Will inflate or deflate the sliced 2D polygons according to the sign of the correction." +msgstr "修正の符号に従って、スライスされた2Dポリゴンを膨張または収縮させます。" + +#: src/libslic3r/PrintConfig.cpp:2160 +msgid "Wipe into this object" +msgstr "このオブジェクトにワイプを含める" + +#: src/libslic3r/PrintConfig.cpp:2152 +msgid "Wipe into this object's infill" +msgstr "このオブジェクトのインフィルにワイプを含める" + +#: 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 +msgid "Wipe options" +msgstr "ワイプオプション" + +#: src/slic3r/GUI/GUI_Preview.cpp:238 src/slic3r/GUI/Tab.cpp:1130 +#: src/libslic3r/GCode/PreviewData.cpp:174 +msgid "Wipe tower" +msgstr "ワイプタワー" + +#: src/slic3r/GUI/Plater.cpp:1043 src/slic3r/GUI/Plater.cpp:1058 +msgid "wipe tower" +msgstr "ワイプタワー" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:141 +msgid "Wipe tower - Purging volume adjustment" +msgstr "ワイプタワー-パージ量調整" + +#: src/slic3r/GUI/Tab.cpp:1664 +msgid "Wipe tower parameters" +msgstr "ワイプタワーのパラメータ" + +#: src/libslic3r/PrintConfig.cpp:2144 +msgid "Wipe tower rotation angle" +msgstr "ワイプタワーの回転角" + +#: src/libslic3r/PrintConfig.cpp:2170 +msgid "Wipe tower rotation angle with respect to x-axis." +msgstr "x軸に対するワイプタワーの回転角度。" + +#: src/libslic3r/PrintConfig.cpp:2092 +msgid "Wipe while retracting" +msgstr "吸込み中にワイプ" + +#: src/slic3r/GUI/PresetHints.cpp:224 +msgid "with a volumetric rate" +msgstr "体積押出し率で" + +#: src/libslic3r/PrintConfig.cpp:1460 +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:1969 +msgid "With sheath around the support" +msgstr "サポートの周りに覆いを付ける" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:40 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:83 +msgid "World coordinates" +msgstr "ワールド座標" + +#: src/slic3r/GUI/UpdateDialogs.cpp:76 +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 "インストールしますか?まず完全な設定スナップショットが作成されます。 新しいバージョンに問題がある場合はいつでも復元できます。アップデートされた設定に含まれるもの:" + +#: src/libslic3r/Zipper.cpp:95 +msgid "write calledback failed" +msgstr "コールバックの書込に失敗しました" + +#: src/libslic3r/PrintConfig.cpp:2993 +msgid "Write information about the model to the console." +msgstr "コンソールにモデル情報をリストします。" + +#: src/slic3r/Utils/Duet.cpp:148 +msgid "Wrong password" +msgstr "パスワードが違います" + +#: src/libslic3r/PrintConfig.cpp:2124 +msgid "X coordinate of the left front corner of a wipe tower" +msgstr "ワイプタワー前面左端のX座標" + +#: src/libslic3r/PrintConfig.cpp:1793 +msgid "XY separation between an object and its support" +msgstr "XY面でのサポートとモデルの隙間" + +#: src/libslic3r/PrintConfig.cpp:1795 +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 "レイヤー内のオブジェクトとサポート間の隙間。 パーセンテージ(たとえば、50%)で表された場合、最外周の射出幅から計算されます。" + +#: src/libslic3r/PrintConfig.cpp:2174 +msgid "XY Size Compensation" +msgstr "XYサイズ補正" + +#: src/libslic3r/PrintConfig.cpp:2131 +msgid "Y coordinate of the left front corner of a wipe tower" +msgstr "ワイプタワー前面左端のY座標" + +#: src/slic3r/GUI/Plater.cpp:992 +msgid "Yes" +msgstr "はい" + +#: src/libslic3r/PrintConfig.cpp:1252 +msgid "You can put here your personal notes. This text will be added to the G-code header comments." +msgstr "ここにメモを書いておくことができます。 このテキストは、Gコードヘッダーのコメントに追加されます。" + +#: src/libslic3r/PrintConfig.cpp:557 +msgid "You can put your notes regarding the filament here." +msgstr "フィラメントに対してノートをここで書けます。" + +#: src/libslic3r/PrintConfig.cpp:1403 +msgid "You can put your notes regarding the printer here." +msgstr "プリンタに関するメモをここに入力できます。" + +#: src/libslic3r/PrintConfig.cpp:2332 +msgid "You can put your notes regarding the SLA print material here." +msgstr "SLAプリント材料に関するメモをここに記入できます。" + +#: src/libslic3r/PrintConfig.cpp:324 +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:1295 +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 "このテンプレート内では、すべての構成オプションを変数として使用できます。例:[layer_height]、[fill_density]など。[timestamp]、[year]、[month]、[day]、[hour]、[minute]、[second]、[version]、[input_filename]、[input_filename_base]も使用できます。" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2538 +msgid "You can't change a type of the last solid part of the object." +msgstr "オブジェクトの最後のソリッドパーツのタイプを変更することはできません。" + +#: src/slic3r/GUI/Plater.cpp:2243 +msgid "You can't load SLA project if there is at least one multi-part object on the bed" +msgstr "プリント領域にマルチパートオブジェクトがある場合、SLAプロジェクトをロードできません" + +#: src/slic3r/GUI/Plater.cpp:1746 +#, 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 "オブジェクトの1つまたはいくつかはマルチパートであるため、%sからオブジェクトを追加できません" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:565 +msgid "You cannot use non-uniform scaling mode for multiple objects/parts selection" +msgstr "複数のオブジェクト/パーツの選択で軸別のスケールモードを使用することはできません" + +#: src/slic3r/GUI/GUI_App.cpp:300 +msgid "You may need to update your graphics card driver." +msgstr "グラフィックカードドライバを更新する必要がある場合があります。" + +#: src/slic3r/GUI/Preferences.cpp:130 +#, c-format +msgid "You need to restart %s to make the changes effective." +msgstr "変更を有効にするには、%sを再起動する必要があります。" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2415 +#, c-format +msgid "You started your selection with %s Item." +msgstr "%sアイテムで選択を開始しました。" + +#: src/slic3r/GUI/MainFrame.cpp:772 +msgid "Your file was repaired." +msgstr "ファイルが修復されました。" + +#: src/slic3r/GUI/Plater.cpp:1874 +msgid "Your object appears to be too large, so it was automatically scaled down to fit your print bed." +msgstr "オブジェクトが大きすぎて、プリントベッドに収まるように自動縮小することができません。" + +#: src/libslic3r/PrintConfig.cpp:2184 +msgid "Z offset" +msgstr "Zオフセット" + +#: src/libslic3r/PrintConfig.cpp:2416 +msgid "Zig-Zag" +msgstr "ジグザグ" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:152 +msgid "Zoom in" +msgstr "ズームイン" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:153 +msgid "Zoom out" +msgstr "縮小" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:150 +msgid "Zoom to all objects in scene, if none selected" +msgstr "選択されていない場合、シーン内のすべてのオブジェクトがズームします" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:149 +msgid "Zoom to Bed" +msgstr "ベッドの大きさにズーム" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:151 +msgid "Zoom to selected object" +msgstr "選択したオブジェクトにズーム" + +#: src/libslic3r/PrintConfig.cpp:171 src/libslic3r/PrintConfig.cpp:733 +#: src/libslic3r/PrintConfig.cpp:1570 src/libslic3r/PrintConfig.cpp:1580 +#: src/libslic3r/PrintConfig.cpp:1808 src/libslic3r/PrintConfig.cpp:1962 +#: src/libslic3r/PrintConfig.cpp:2146 src/libslic3r/PrintConfig.cpp:2463 +msgid "°" +msgstr "°" + +#: src/slic3r/GUI/ConfigWizard.cpp:613 src/slic3r/GUI/ConfigWizard.cpp:627 +msgid "°C" +msgstr "°C" diff --git a/resources/localization/list.txt b/resources/localization/list.txt index 82f109df4..7f0b67252 100644 --- a/resources/localization/list.txt +++ b/resources/localization/list.txt @@ -1,9 +1,11 @@ src/slic3r/GUI/AboutDialog.cpp +src/slic3r/GUI/AppConfig.cpp src/slic3r/GUI/BackgroundSlicingProcess.cpp src/slic3r/GUI/BedShapeDialog.cpp src/slic3r/GUI/BedShapeDialog.hpp src/slic3r/GUI/BonjourDialog.cpp src/slic3r/GUI/ButtonsDescription.cpp +src/slic3r/GUI/ConfigManipulation.cpp src/slic3r/GUI/ConfigSnapshotDialog.cpp src/slic3r/GUI/ConfigWizard.cpp src/slic3r/GUI/Field.cpp @@ -26,6 +28,7 @@ src/slic3r/GUI/GUI_ObjectSettings.cpp src/slic3r/GUI/GUI_Preview.cpp src/slic3r/GUI/KBShortcutsDialog.cpp src/slic3r/GUI/MainFrame.cpp +src/slic3r/GUI/Mouse3DController.cpp src/slic3r/GUI/MsgDialog.cpp src/slic3r/GUI/OptionsGroup.cpp src/slic3r/GUI/Plater.cpp @@ -43,12 +46,18 @@ 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/PresetUpdater.cpp src/slic3r/Utils/FixModelByWin10.cpp +src/libslic3r/SLA/SLAPad.cpp src/libslic3r/Zipper.cpp -src/libslic3r/SLA/SLASupportTree.cpp +src/libslic3r/GCode.cpp +src/libslic3r/ExtrusionEntity.cpp +src/libslic3r/Format/3mf.cpp +src/libslic3r/Format/AMF.cpp src/libslic3r/Print.cpp src/libslic3r/SLAPrint.cpp src/libslic3r/PrintBase.cpp diff --git a/resources/localization/pt_br/PrusaSlicer.mo b/resources/localization/pt_br/PrusaSlicer.mo new file mode 100644 index 000000000..a2f3e9789 Binary files /dev/null and b/resources/localization/pt_br/PrusaSlicer.mo differ diff --git a/resources/localization/pt_br/PrusaSlicer_pt_br.po b/resources/localization/pt_br/PrusaSlicer_pt_br.po new file mode 100644 index 000000000..e54f17ec3 --- /dev/null +++ b/resources/localization/pt_br/PrusaSlicer_pt_br.po @@ -0,0 +1,9386 @@ +# 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" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-09-09 16:39+0200\n" +"PO-Revision-Date: 2019-11-18 16:39-0300\n" +"Language-Team: \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.2.4\n" +"Last-Translator: \n" +"Language: pt_BR\n" + +#: src/slic3r/GUI/AboutDialog.cpp:39 src/slic3r/GUI/AboutDialog.cpp:291 +msgid "Portions copyright" +msgstr "Direitos autorais das partes" + +#: src/slic3r/GUI/AboutDialog.cpp:127 src/slic3r/GUI/AboutDialog.cpp:256 +msgid "Copyright" +msgstr "Direitos autorais" + +#. 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 "" +"Os contratos de licença de todos os seguintes programas (bibliotecas) são " +"parte do contrato de licença de aplicativo" + +#: src/slic3r/GUI/AboutDialog.cpp:199 +#, c-format +msgid "About %s" +msgstr "Sobre %s" + +#: src/slic3r/GUI/AboutDialog.cpp:231 src/slic3r/GUI/MainFrame.cpp:62 +msgid "Version" +msgstr "Versão" + +#. TRN "Slic3r _is licensed under the_ License" +#: src/slic3r/GUI/AboutDialog.cpp:258 +msgid "is licensed under the" +msgstr "está licenciado sobre o(a)" + +#: src/slic3r/GUI/AboutDialog.cpp:259 +msgid "GNU Affero General Public License, version 3" +msgstr "Licensa GNU Affero General Public, versão 3" + +#: src/slic3r/GUI/AboutDialog.cpp:260 +msgid "" +"PrusaSlicer is based on Slic3r by Alessandro Ranellucci and the RepRap " +"community." +msgstr "" +"PrusaSlicer é baseado no Slic3r criado por Alessandro Ranellucci e a " +"comunidade RepRap." + +#: 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 "" +"Contribuições por Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, " +"Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik e " +"outros." + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:92 +msgid "" +"Copying of the temporary G-code to the output G-code failed. Maybe the SD " +"card is write locked?" +msgstr "" +"A cópia do G-código provisório G-código falhou na saída. Talvez o cartão SD " +"está bloqueado para escrita?" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:93 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:415 +msgid "Running post-processing scripts" +msgstr "Aplicando scripts de pós-processamento" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:95 +msgid "G-code file exported to %1%" +msgstr "Arquivo G-code exportado para %1%" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:99 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:117 +msgid "Slicing complete" +msgstr "Fatiamento completo" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:113 +msgid "Masked SLA file exported to %1%" +msgstr "Arquivo SLA mascarado exportado para %1%" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:155 +#, 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 encontrou um erro. Provavelmente foi causado por ficar sem memória. Se " +"você tem certeza que você tem RAM suficiente em seu sistema, isso também " +"pode ser um bug e nós estaríamos contentes se você relatou." + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:417 +msgid "Copying of the temporary G-code to the output G-code failed" +msgstr "A cópia do G-código provisório G-código falhou na saída" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:426 +msgid "Scheduling upload to `%1%`. See Window -> Print Host Upload Queue" +msgstr "Agendando upload para ` %1%` . Veja a aba -> Print Host Upload Queue" + +#: src/slic3r/GUI/BedShapeDialog.cpp:65 +msgid "Shape" +msgstr "Forma" + +#: src/slic3r/GUI/BedShapeDialog.cpp:72 +msgid "Rectangular" +msgstr "Retangular" + +#: src/slic3r/GUI/BedShapeDialog.cpp:76 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:393 src/slic3r/GUI/Plater.cpp:145 +#: src/slic3r/GUI/Tab.cpp:2524 +msgid "Size" +msgstr "Tamanho" + +#: src/slic3r/GUI/BedShapeDialog.cpp:77 +msgid "Size in X and Y of the rectangular plate." +msgstr "Tamanho no X e Y na mesa retangular." + +#: src/slic3r/GUI/BedShapeDialog.cpp:83 +msgid "Origin" +msgstr "Origem" + +#: src/slic3r/GUI/BedShapeDialog.cpp:84 +msgid "" +"Distance of the 0,0 G-code coordinate from the front left corner of the " +"rectangle." +msgstr "" +"Distância do ponto 0,0 da coordenada do G-code do canto esquerdo do " +"retângulo." + +#: src/slic3r/GUI/BedShapeDialog.cpp:88 +msgid "Circular" +msgstr "Circular" + +#: src/slic3r/GUI/BedShapeDialog.cpp:91 src/slic3r/GUI/ConfigWizard.cpp:123 +#: src/slic3r/GUI/ConfigWizard.cpp:576 src/slic3r/GUI/ConfigWizard.cpp:590 +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:135 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:390 +#: src/slic3r/GUI/WipeTowerDialog.cpp:84 src/slic3r/GUI/wxExtensions.cpp:509 +#: 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:2510 src/libslic3r/PrintConfig.cpp:2519 +#: src/libslic3r/PrintConfig.cpp:2528 src/libslic3r/PrintConfig.cpp:2538 +#: src/libslic3r/PrintConfig.cpp:2582 src/libslic3r/PrintConfig.cpp:2592 +#: src/libslic3r/PrintConfig.cpp:2604 src/libslic3r/PrintConfig.cpp:2624 +#: src/libslic3r/PrintConfig.cpp:2634 src/libslic3r/PrintConfig.cpp:2644 +#: src/libslic3r/PrintConfig.cpp:2662 src/libslic3r/PrintConfig.cpp:2677 +#: src/libslic3r/PrintConfig.cpp:2691 src/libslic3r/PrintConfig.cpp:2704 +#: src/libslic3r/PrintConfig.cpp:2742 src/libslic3r/PrintConfig.cpp:2752 +#: src/libslic3r/PrintConfig.cpp:2761 src/libslic3r/PrintConfig.cpp:2771 +msgid "mm" +msgstr "mm" + +#: src/slic3r/GUI/BedShapeDialog.cpp:92 src/libslic3r/PrintConfig.cpp:692 +msgid "Diameter" +msgstr "Diâmetro" + +#: src/slic3r/GUI/BedShapeDialog.cpp:93 +msgid "" +"Diameter of the print bed. It is assumed that origin (0,0) is located in the " +"center." +msgstr "" +"Diâmetro da mesa de impressão. Se assume que a origem (0,0) seja localizado " +"no centro." + +#: src/slic3r/GUI/BedShapeDialog.cpp:97 src/slic3r/GUI/GUI_Preview.cpp:247 +#: src/libslic3r/GCode/PreviewData.cpp:159 +msgid "Custom" +msgstr "Customizado" + +#: src/slic3r/GUI/BedShapeDialog.cpp:101 +msgid "Load shape from STL..." +msgstr "Carregar forma do STL..." + +#: src/slic3r/GUI/BedShapeDialog.cpp:154 +msgid "Settings" +msgstr "config." + +#: src/slic3r/GUI/BedShapeDialog.cpp:171 +msgid "Texture" +msgstr "Textura" + +#: src/slic3r/GUI/BedShapeDialog.cpp:181 src/slic3r/GUI/BedShapeDialog.cpp:249 +msgid "Load..." +msgstr "Carregar..." + +#: src/slic3r/GUI/BedShapeDialog.cpp:189 src/slic3r/GUI/BedShapeDialog.cpp:257 +#: src/slic3r/GUI/Tab.cpp:3286 +msgid "Remove" +msgstr "Remover" + +#: src/slic3r/GUI/BedShapeDialog.cpp:239 +msgid "Model" +msgstr "Modelo" + +#: src/slic3r/GUI/BedShapeDialog.cpp:464 +msgid "Choose an STL file to import bed shape from:" +msgstr "Escolha um arquivo STL para importar o formato da mesa:" + +#: src/slic3r/GUI/BedShapeDialog.cpp:471 src/slic3r/GUI/BedShapeDialog.cpp:520 +#: src/slic3r/GUI/BedShapeDialog.cpp:543 +msgid "Invalid file format." +msgstr "Formato de arquivo inválido." + +#: src/slic3r/GUI/BedShapeDialog.cpp:482 +msgid "Error! Invalid model" +msgstr "Erro! Modelo inválido" + +#: src/slic3r/GUI/BedShapeDialog.cpp:490 +msgid "The selected file contains no geometry." +msgstr "O arquivo selecionado não contém geometria." + +#: src/slic3r/GUI/BedShapeDialog.cpp:494 +msgid "" +"The selected file contains several disjoint areas. This is not supported." +msgstr "O arquivo selecionado contém áreas não juntas. Isso não é suportado." + +#: src/slic3r/GUI/BedShapeDialog.cpp:509 +msgid "Choose a file to import bed texture from (PNG/SVG):" +msgstr "Escolher um arquivo para importar a textura da mesa (PNG/SVG):" + +#: src/slic3r/GUI/BedShapeDialog.cpp:532 +msgid "Choose an STL file to import bed model from:" +msgstr "Escolha um arquivo STL para importar o modelo da mesa:" + +#: src/slic3r/GUI/BedShapeDialog.hpp:59 src/slic3r/GUI/ConfigWizard.cpp:535 +msgid "Bed Shape" +msgstr "Formato da mesa" + +#: src/slic3r/GUI/BonjourDialog.cpp:55 +msgid "Network lookup" +msgstr "Pesquisa de rede" + +#: src/slic3r/GUI/BonjourDialog.cpp:72 +msgid "Address" +msgstr "Endereço" + +#: src/slic3r/GUI/BonjourDialog.cpp:73 +msgid "Hostname" +msgstr "Nome do Host" + +#: src/slic3r/GUI/BonjourDialog.cpp:74 +msgid "Service name" +msgstr "Nome de serviços" + +#: src/slic3r/GUI/BonjourDialog.cpp:76 +msgid "OctoPrint version" +msgstr "Versão do OctoPrint" + +#: src/slic3r/GUI/BonjourDialog.cpp:218 +msgid "Searching for devices" +msgstr "Procurando por dispositivos" + +#: src/slic3r/GUI/BonjourDialog.cpp:225 +msgid "Finished" +msgstr "Finalizado" + +#: src/slic3r/GUI/ButtonsDescription.cpp:16 +msgid "Buttons And Text Colors Description" +msgstr "Descrição dos botões e cores de texto" + +#: src/slic3r/GUI/ButtonsDescription.cpp:36 +msgid "Value is the same as the system value" +msgstr "O valor é o mesmo que o valor do sistema" + +#: src/slic3r/GUI/ButtonsDescription.cpp:53 +msgid "" +"Value was changed and is not equal to the system value or the last saved " +"preset" +msgstr "" +"O valor foi mudado e não é igual ao valor do sistema ou da última config. " +"salva" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:18 +msgid "Upgrade" +msgstr "Atualização" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:20 +msgid "Downgrade" +msgstr "Desatualização" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:22 +msgid "Before roll back" +msgstr "Antes de reverter" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:24 +msgid "User" +msgstr "Usuário" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:27 +msgid "Unknown" +msgstr "Desconhecido" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:39 +msgid "Active" +msgstr "Ativar" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:45 +msgid "slic3r version" +msgstr "versão do slic3r" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:46 src/slic3r/GUI/Preset.cpp:1311 +msgid "print" +msgstr "impressão" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:47 +msgid "filaments" +msgstr "filamentos" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:48 src/slic3r/GUI/Preset.cpp:1315 +msgid "printer" +msgstr "impressora" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:52 src/slic3r/GUI/Tab.cpp:961 +msgid "vendor" +msgstr "fornecedor" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:52 +msgid "version" +msgstr "versão" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:53 +msgid "min slic3r version" +msgstr "versão mínima do slic3r" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:55 +msgid "max slic3r version" +msgstr "versão máxima do slic3r" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:58 +msgid "model" +msgstr "modelo" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:58 +msgid "variants" +msgstr "variantes" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:70 +#, c-format +msgid "Incompatible with this %s" +msgstr "Incompatível com isso %s" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:73 +msgid "Activate" +msgstr "Ativar" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:99 +msgid "Configuration Snapshots" +msgstr "config. das versões" + +#: src/slic3r/GUI/ConfigWizard.cpp:123 +msgid "nozzle" +msgstr "bico de impressão" + +#: src/slic3r/GUI/ConfigWizard.cpp:127 +msgid "Alternate nozzles:" +msgstr "Alternar bicos:" + +#: src/slic3r/GUI/ConfigWizard.cpp:193 +msgid "All standard" +msgstr "Todos padrão" + +#: src/slic3r/GUI/ConfigWizard.cpp:194 src/slic3r/GUI/Tab.cpp:3336 +msgid "All" +msgstr "Todos" + +#: src/slic3r/GUI/ConfigWizard.cpp:195 src/slic3r/GUI/Plater.cpp:469 +#: src/slic3r/GUI/Plater.cpp:607 src/libslic3r/GCode/PreviewData.cpp:146 +msgid "None" +msgstr "Nenhum" + +#: src/slic3r/GUI/ConfigWizard.cpp:301 +#, c-format +msgid "Welcome to the %s Configuration Assistant" +msgstr "Bem-vindo ao %s Assistente de config." + +#: src/slic3r/GUI/ConfigWizard.cpp:303 +#, c-format +msgid "Welcome to the %s Configuration Wizard" +msgstr "Bem-vindo ao %s Assistente de config." + +#: src/slic3r/GUI/ConfigWizard.cpp:305 +msgid "Welcome" +msgstr "Bem-vindo(a)" + +#: src/slic3r/GUI/ConfigWizard.cpp:309 src/slic3r/GUI/GUI_App.cpp:793 +#, c-format +msgid "Run %s" +msgstr "Executar %s" + +#: src/slic3r/GUI/ConfigWizard.cpp:311 +#, 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 "" +"Olá, bem-vindo ao %s! Isso %s te ajuda com a config. inicial; com apenas " +"algumas config. e você estará pronto para imprimir." + +#: src/slic3r/GUI/ConfigWizard.cpp:316 +msgid "" +"Remove user profiles - install from scratch (a snapshot will be taken " +"beforehand)" +msgstr "" +"Remover perfis de usuário - instalar do zero (uma snapshot será salva antes)" + +#: src/slic3r/GUI/ConfigWizard.cpp:347 +#, c-format +msgid "%s Family" +msgstr "%s Família" + +#: src/slic3r/GUI/ConfigWizard.cpp:384 +msgid "Custom Printer Setup" +msgstr "config. da impressora customizada" + +#: src/slic3r/GUI/ConfigWizard.cpp:384 +msgid "Custom Printer" +msgstr "Impressora customizada" + +#: src/slic3r/GUI/ConfigWizard.cpp:386 +msgid "Define a custom printer profile" +msgstr "Definir uma config. para a impressora customizada" + +#: src/slic3r/GUI/ConfigWizard.cpp:388 +msgid "Custom profile name:" +msgstr "Nome customizado da config.:" + +#: src/slic3r/GUI/ConfigWizard.cpp:412 +msgid "Automatic updates" +msgstr "Atualizações automáticas" + +#: src/slic3r/GUI/ConfigWizard.cpp:412 +msgid "Updates" +msgstr "Atualizações" + +#: src/slic3r/GUI/ConfigWizard.cpp:420 src/slic3r/GUI/Preferences.cpp:69 +msgid "Check for application updates" +msgstr "Verificar atualizações nas aplicações" + +#: src/slic3r/GUI/ConfigWizard.cpp:424 +#, 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 "" +"Se ativada, %s verifica se há novas versões do aplicativo online. Quando uma " +"nova versão se torna disponível, uma notificação é exibida na próxima " +"inicialização do aplicativo (nunca durante o uso do programa). Este é apenas " +"um mecanismos de notificação, nenhuma instalação automática é feita." + +#: src/slic3r/GUI/ConfigWizard.cpp:430 src/slic3r/GUI/Preferences.cpp:77 +msgid "Update built-in Presets automatically" +msgstr "Atualizar predefinições incorporadas automaticamente" + +#: src/slic3r/GUI/ConfigWizard.cpp:434 +#, 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 "" +"Se ativada, %s baixa atualizações de predefinições de sistema incorporadas " +"em segundo plano. Essas atualizações são baixadas em um local temporário " +"separado. Quando uma nova versão predefinida se torna disponível, ela é " +"oferecida na inicialização do aplicativo." + +#: src/slic3r/GUI/ConfigWizard.cpp:437 +msgid "" +"Updates are never applied without user's consent and never overwrite user's " +"customized settings." +msgstr "" +"Atualizações nunca são aplicadas sem a permissão do usuário e nunca sobre " +"escrevem as config. do usuário." + +#: src/slic3r/GUI/ConfigWizard.cpp:442 +msgid "" +"Additionally a backup snapshot of the whole configuration is created before " +"an update is applied." +msgstr "" +"Além disso, uma captura de backup de toda a config. é criado antes que uma " +"atualização seja aplicada." + +#: src/slic3r/GUI/ConfigWizard.cpp:449 +msgid "Other Vendors" +msgstr "Outros fornecedores" + +#: src/slic3r/GUI/ConfigWizard.cpp:451 +#, c-format +msgid "Pick another vendor supported by %s:" +msgstr "Escolha outro fornecedor suportado por %s:" + +#: src/slic3r/GUI/ConfigWizard.cpp:497 +msgid "Firmware Type" +msgstr "Tipo de Firmware" + +#: src/slic3r/GUI/ConfigWizard.cpp:497 src/slic3r/GUI/Tab.cpp:2149 +msgid "Firmware" +msgstr "Firmware" + +#: src/slic3r/GUI/ConfigWizard.cpp:501 +msgid "Choose the type of firmware used by your printer." +msgstr "Escolha o tipo de firmware utilizado na sua impressora." + +#: src/slic3r/GUI/ConfigWizard.cpp:535 +msgid "Bed Shape and Size" +msgstr "Forma e tamanho da mesa" + +#: src/slic3r/GUI/ConfigWizard.cpp:538 +msgid "Set the shape of your printer's bed." +msgstr "Insira o formato da mesa de impressão." + +#: src/slic3r/GUI/ConfigWizard.cpp:558 +msgid "Filament and Nozzle Diameters" +msgstr "Diâmetro do bico e do filamento" + +#: src/slic3r/GUI/ConfigWizard.cpp:558 +msgid "Print Diameters" +msgstr "Diâmetros de impressão" + +#: src/slic3r/GUI/ConfigWizard.cpp:572 +msgid "Enter the diameter of your printer's hot end nozzle." +msgstr "Insira o diâmetro do bico de impressão." + +#: src/slic3r/GUI/ConfigWizard.cpp:575 +msgid "Nozzle Diameter:" +msgstr "Diâmetro do bico:" + +#: src/slic3r/GUI/ConfigWizard.cpp:585 +msgid "Enter the diameter of your filament." +msgstr "Coloque o diâmetro do seu filamento." + +#: src/slic3r/GUI/ConfigWizard.cpp:586 +msgid "" +"Good precision is required, so use a caliper and do multiple measurements " +"along the filament, then compute the average." +msgstr "" +"É necessário uma boa precisão, utilize um paquímetro e realize várias " +"medições ao longo do filamento, faça uma média." + +#: src/slic3r/GUI/ConfigWizard.cpp:589 +msgid "Filament Diameter:" +msgstr "Diâmetro do filamento:" + +#: src/slic3r/GUI/ConfigWizard.cpp:623 +msgid "Extruder and Bed Temperatures" +msgstr "Temperaturas da mesa e da extrusora" + +#: src/slic3r/GUI/ConfigWizard.cpp:623 +msgid "Temperatures" +msgstr "Temperaturas" + +#: src/slic3r/GUI/ConfigWizard.cpp:639 +msgid "Enter the temperature needed for extruding your filament." +msgstr "Coloque a temperatura necessária para extrusar seu filamento." + +#: src/slic3r/GUI/ConfigWizard.cpp:640 +msgid "A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS." +msgstr "A regra de ouro é 160 à 230°C para PLA, e 215 à 250°C para ABS." + +#: src/slic3r/GUI/ConfigWizard.cpp:643 +msgid "Extrusion Temperature:" +msgstr "Temperatura de extrusão:" + +#: src/slic3r/GUI/ConfigWizard.cpp:644 src/slic3r/GUI/ConfigWizard.cpp:658 +msgid "°C" +msgstr "°C" + +#: src/slic3r/GUI/ConfigWizard.cpp:653 +msgid "" +"Enter the bed temperature needed for getting your filament to stick to your " +"heated bed." +msgstr "" +"Coloque a temperatura da mesa necessária para fazer com que seu filamento " +"grude na mesa." + +#: src/slic3r/GUI/ConfigWizard.cpp:654 +msgid "" +"A rule of thumb is 60 °C for PLA and 110 °C for ABS. Leave zero if you have " +"no heated bed." +msgstr "" +"A regra de ouro é 60°C para PLA, e 110°C para ABS. Deixe em zero se não há " +"mesa aquecida." + +#: src/slic3r/GUI/ConfigWizard.cpp:657 +msgid "Bed Temperature:" +msgstr "Temperatura da mesa:" + +#: src/slic3r/GUI/ConfigWizard.cpp:1138 +msgid "Select all standard printers" +msgstr "Selecione todas as impressoras padrão" + +#: src/slic3r/GUI/ConfigWizard.cpp:1141 +msgid "< &Back" +msgstr "< &Voltar" + +#: src/slic3r/GUI/ConfigWizard.cpp:1142 +msgid "&Next >" +msgstr "&Próximo >" + +#: src/slic3r/GUI/ConfigWizard.cpp:1143 +msgid "&Finish" +msgstr "&Final" + +#: src/slic3r/GUI/ConfigWizard.cpp:1144 src/slic3r/GUI/FirmwareDialog.cpp:151 +#: src/slic3r/GUI/ProgressStatusBar.cpp:27 +msgid "Cancel" +msgstr "Cancelar" + +#: src/slic3r/GUI/ConfigWizard.cpp:1158 +msgid "Prusa FFF Technology Printers" +msgstr "Impressoras de tecnologia Prusa FFF" + +#: src/slic3r/GUI/ConfigWizard.cpp:1161 +msgid "Prusa MSLA Technology Printers" +msgstr "Impressoras de tecnologia Prusa MSLA" + +#: src/slic3r/GUI/ConfigWizard.cpp:1230 +msgid "Configuration Assistant" +msgstr "Assistente de config." + +#: src/slic3r/GUI/ConfigWizard.cpp:1231 +msgid "Configuration &Assistant" +msgstr "Assistente &de config." + +#: src/slic3r/GUI/ConfigWizard.cpp:1233 +msgid "Configuration Wizard" +msgstr "Assistente de config." + +#: src/slic3r/GUI/ConfigWizard.cpp:1234 +msgid "Configuration &Wizard" +msgstr "Assistente &de config." + +#: src/slic3r/GUI/Field.cpp:125 +msgid "default value" +msgstr "valor padrão" + +#: src/slic3r/GUI/Field.cpp:128 +msgid "parameter name" +msgstr "nome do parâmetro" + +#: src/slic3r/GUI/Field.cpp:139 src/slic3r/GUI/OptionsGroup.cpp:569 +msgid "N/A" +msgstr "N/D" + +#: src/slic3r/GUI/Field.cpp:158 +#, c-format +msgid "%s doesn't support percentage" +msgstr "%s não suporta porcentagem" + +#: src/slic3r/GUI/Field.cpp:174 src/slic3r/GUI/Field.cpp:197 +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:337 +msgid "Invalid numeric input." +msgstr "Entrada numérica não válida." + +#: src/slic3r/GUI/Field.cpp:179 +msgid "Input value is out of range" +msgstr "Valor de entrada está fora do limite" + +#: src/slic3r/GUI/Field.cpp:206 +#, 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 "" +"Você quer dizer %s%% ao invés de %s %s?\n" +"Selecione SIM se quiser trocar esse valor para %s%%, \n" +"ou NÃO se você tem certeza que %s %s é o valor correto." + +#: src/slic3r/GUI/Field.cpp:209 +msgid "Parameter validation" +msgstr "Validação do parâmetro" + +#: src/slic3r/GUI/FirmwareDialog.cpp:150 +msgid "Flash!" +msgstr "Atualizando!" + +#: src/slic3r/GUI/FirmwareDialog.cpp:152 +msgid "Flashing in progress. Please do not disconnect the printer!" +msgstr "Atualização em progresso. Favor não desconectar sua impressora!" + +#: src/slic3r/GUI/FirmwareDialog.cpp:199 +msgid "Flashing failed" +msgstr "A atualização falhou" + +#: src/slic3r/GUI/FirmwareDialog.cpp:282 +msgid "Flashing succeeded!" +msgstr "Atualizado com sucesso!" + +#: src/slic3r/GUI/FirmwareDialog.cpp:283 +msgid "Flashing failed. Please see the avrdude log below." +msgstr "A atualização falhou. Favor verificar os registros abaixo." + +#: src/slic3r/GUI/FirmwareDialog.cpp:284 +msgid "Flashing cancelled." +msgstr "Atualização cancelada." + +#: 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 "" +"O arquivo hex do firmware não é o mesmo utilizado no modelo da impressora.\n" +"O arquivo hex desejado para: %s\n" +"Impressora relatada: %s\n" +"\n" +"Você gostaria de continuar e atualizar o arquivo hex mesmo assim?\n" +"Favor continuar se tiver certeza que é a coisa certa a se fazer." + +#: 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 "" +"Múltiplos %s dispositivos encontrados. Favor conectar um de cada vez para " +"atualização." + +#: 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 "" +"O %s dispositivo não foi encontrado.\n" +"Se o dispositivo está conectado, favor utilizar o botão de Reset perto do " +"conector USB ..." + +#: src/slic3r/GUI/FirmwareDialog.cpp:548 +#, c-format +msgid "The %s device could not have been found" +msgstr "O %s dispositivo não pode ser encontrado" + +#: src/slic3r/GUI/FirmwareDialog.cpp:645 +#, c-format +msgid "Error accessing port at %s: %s" +msgstr "Erro ao acessa a porta em %s: %s" + +#: src/slic3r/GUI/FirmwareDialog.cpp:647 +#, c-format +msgid "Error: %s" +msgstr "Erro: %s" + +#: src/slic3r/GUI/FirmwareDialog.cpp:777 +msgid "Firmware flasher" +msgstr "Atualizador de Firmware" + +#: src/slic3r/GUI/FirmwareDialog.cpp:802 +msgid "Firmware image:" +msgstr "Imagem do Firmware:" + +#: src/slic3r/GUI/FirmwareDialog.cpp:805 src/slic3r/GUI/Tab.cpp:1870 +#: src/slic3r/GUI/Tab.cpp:1926 +msgid "Browse" +msgstr "Procurar" + +#: src/slic3r/GUI/FirmwareDialog.cpp:807 +msgid "Serial port:" +msgstr "Porte Serial:" + +#: src/slic3r/GUI/FirmwareDialog.cpp:809 +msgid "Autodetected" +msgstr "Auto detectado" + +#: src/slic3r/GUI/FirmwareDialog.cpp:810 +msgid "Rescan" +msgstr "Reescanear" + +#: src/slic3r/GUI/FirmwareDialog.cpp:817 +msgid "Progress:" +msgstr "Progresso:" + +#: src/slic3r/GUI/FirmwareDialog.cpp:820 +msgid "Status:" +msgstr "Status:" + +#: src/slic3r/GUI/FirmwareDialog.cpp:821 +msgid "Ready" +msgstr "Pronto" + +#: src/slic3r/GUI/FirmwareDialog.cpp:841 +msgid "Advanced: Output log" +msgstr "Avançado: log de Saída" + +#: src/slic3r/GUI/FirmwareDialog.cpp:852 +#: src/slic3r/GUI/PrintHostDialogs.cpp:161 +msgid "Close" +msgstr "Fechar" + +#: src/slic3r/GUI/FirmwareDialog.cpp:903 +msgid "" +"Are you sure you want to cancel firmware flashing?\n" +"This could leave your printer in an unusable state!" +msgstr "" +"Você tem certeza que gostaria de cancelar a atualização de Firmware? \n" +"Isso poderia deixar a sua impressora inutilizável!" + +#: src/slic3r/GUI/FirmwareDialog.cpp:904 +msgid "Confirmation" +msgstr "Confirmação" + +#: src/slic3r/GUI/FirmwareDialog.cpp:907 +msgid "Cancelling..." +msgstr "Cancelando..." + +#: src/slic3r/GUI/GLCanvas3D.cpp:534 +msgid "Layers heights" +msgstr "Altura de camada" + +#: src/slic3r/GUI/GLCanvas3D.cpp:631 +msgid "An object outside the print area was detected" +msgstr "Um objeto foi detectado fora da área de impressão" + +#: src/slic3r/GUI/GLCanvas3D.cpp:632 +msgid "A toolpath outside the print area was detected" +msgstr "Há movimentos fora da área de impressão" + +#: src/slic3r/GUI/GLCanvas3D.cpp:633 +msgid "SLA supports outside the print area were detected" +msgstr "Suportes de SLA foram detectados fora da área de impressão" + +#: src/slic3r/GUI/GLCanvas3D.cpp:634 +msgid "Some objects are not visible when editing supports" +msgstr "Alguns objetos não são visíveis quando editando suportes" + +#: src/slic3r/GUI/GLCanvas3D.cpp:636 +msgid "" +"An object outside the print area was detected\n" +"Resolve the current problem to continue slicing" +msgstr "" +"Um objeto foi encontrado fora da área de impressão\n" +"Resolva o problema atual para continuar o fatiamento" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1733 +msgid "Mirror Object" +msgstr "Espelhar objeto" + +#: src/slic3r/GUI/GLCanvas3D.cpp:2970 +msgid "Move Object" +msgstr "Mover objeto" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3506 +msgid "Undo History" +msgstr "Desfazer histórico" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3506 +msgid "Redo History" +msgstr "Refazer histórico" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3524 +#, c-format +msgid "Undo %1$d Action" +msgid_plural "Undo %1$d Actions" +msgstr[0] "Desfazer ação de %1$d" +msgstr[1] "Desfazer ações de %1$d" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3524 +#, c-format +msgid "Redo %1$d Action" +msgid_plural "Redo %1$d Actions" +msgstr[0] "Refazer ação de %1$d" +msgstr[1] "Refazer ações de %1$d" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3571 +msgid "Add..." +msgstr "Adicionar..." + +#: src/slic3r/GUI/GLCanvas3D.cpp:3579 src/slic3r/GUI/GUI_ObjectList.cpp:1501 +#: src/slic3r/GUI/Plater.cpp:3520 src/slic3r/GUI/Plater.cpp:3539 +#: src/slic3r/GUI/Tab.cpp:3286 +msgid "Delete" +msgstr "Deletar" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3588 src/slic3r/GUI/Plater.cpp:4172 +msgid "Delete all" +msgstr "Deletar todos" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3597 src/slic3r/GUI/KBShortcutsDialog.cpp:137 +#: src/slic3r/GUI/Plater.cpp:2681 +msgid "Arrange" +msgstr "Arranjar" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3597 src/slic3r/GUI/KBShortcutsDialog.cpp:138 +msgid "Arrange selection" +msgstr "Arranjar seleção" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3609 +msgid "Copy" +msgstr "Copiar" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3618 +msgid "Paste" +msgstr "Colar" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3630 src/slic3r/GUI/Plater.cpp:3400 +#: src/slic3r/GUI/Plater.cpp:3412 src/slic3r/GUI/Plater.cpp:3526 +msgid "Add instance" +msgstr "Adicionar instância" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3641 src/slic3r/GUI/Plater.cpp:3528 +msgid "Remove instance" +msgstr "Remover instância" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3654 +msgid "Split to objects" +msgstr "Dividir em objetos" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3664 src/slic3r/GUI/GUI_ObjectList.cpp:1340 +msgid "Split to parts" +msgstr "Dividir em partes" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3677 src/slic3r/GUI/GUI_ObjectList.cpp:2203 +msgid "Height ranges" +msgstr "Limites de altura" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3728 src/slic3r/GUI/MainFrame.cpp:570 +msgid "Undo" +msgstr "Desfazer" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3728 src/slic3r/GUI/GLCanvas3D.cpp:3761 +msgid "Click right mouse button to open History" +msgstr "Clique no botão direito para abrir o Histórico" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3745 +msgid "Next Undo action: %1%" +msgstr "Próxima ação de desfazer: %1%" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3761 src/slic3r/GUI/MainFrame.cpp:573 +msgid "Redo" +msgstr "Refazer" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3777 +msgid "Next Redo action: %1%" +msgstr "Próxima ação de refazer: %1%" + +#: src/slic3r/GUI/GLCanvas3D.cpp:5555 +msgid "Selection-Add from rectangle" +msgstr "Seleção-Adicionar do retângulo" + +#: src/slic3r/GUI/GLCanvas3D.cpp:5574 +msgid "Selection-Remove from rectangle" +msgstr "Seleção-remover do retângulo" + +#: src/slic3r/GUI/GLCanvas3DManager.cpp:273 +#, 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 requer drivers capazes de executar OpenGL 2.0, \n" +"enquanto a versão do OpenGL %s, renderização %s, fornecedor %s foi detectada." + +#: src/slic3r/GUI/GLCanvas3DManager.cpp:276 +msgid "You may need to update your graphics card driver." +msgstr "Você pode ter que atualizar os drivers da sua placa de vídeo." + +#: src/slic3r/GUI/GLCanvas3DManager.cpp:279 +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 "" +"Como solução alternativa, você pode executar o PrusaSlicer com um software " +"renderizando gráficos 3D por executar Prusa-slicer.exe com o parâmetro--" +"sw_renderer." + +#: src/slic3r/GUI/GLCanvas3DManager.cpp:281 +msgid "Unsupported OpenGL version" +msgstr "Versão do OpenGL não suportada" + +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:40 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:145 src/libslic3r/PrintConfig.cpp:3212 +msgid "Cut" +msgstr "Cortar" + +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:150 +msgid "Keep upper part" +msgstr "Manter parte superior" + +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:151 +msgid "Keep lower part" +msgstr "Manter parte inferior" + +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:152 +msgid "Rotate lower part upwards" +msgstr "Rotacione as partes inferiores para cima" + +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:155 +msgid "Perform cut" +msgstr "Aplicar o corte" + +#: src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp:45 +msgid "Place on face" +msgstr "Colocar em uma face" + +#: src/slic3r/GUI/Gizmos/GLGizmoMove.cpp:48 +msgid "Move" +msgstr "Mover" + +#: src/slic3r/GUI/Gizmos/GLGizmoMove.cpp:177 +msgid "Position (mm)" +msgstr "Posição (mm)" + +#: src/slic3r/GUI/Gizmos/GLGizmoMove.cpp:177 +msgid "Displacement (mm)" +msgstr "Deslocamento (mm)" + +#: src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp:449 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:477 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:496 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:514 +#: src/libslic3r/PrintConfig.cpp:3261 +msgid "Rotate" +msgstr "Rotacionar" + +#: src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp:482 +msgid "Rotation (deg)" +msgstr "Rotacionar (graus)" + +#: src/slic3r/GUI/Gizmos/GLGizmoScale.cpp:47 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:392 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:497 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:515 +#: src/libslic3r/PrintConfig.cpp:3276 +msgid "Scale" +msgstr "Escala" + +#: src/slic3r/GUI/Gizmos/GLGizmoScale.cpp:292 +msgid "Scale (%)" +msgstr "Escala (%)" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:44 +msgid "Head diameter" +msgstr "Diâmetro da cabeça" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:45 +msgid "Lock supports under new islands" +msgstr "Travar suportes debaixo de novas ilhas" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:46 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1449 +msgid "Remove selected points" +msgstr "Remover pontos selecionados" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:47 +msgid "Remove all points" +msgstr "Remover todos os pontos" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:48 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1452 +msgid "Apply changes" +msgstr "Aplicar mudanças" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:49 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1453 +msgid "Discard changes" +msgstr "Descartar mudanças" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:50 +msgid "Minimal points distance" +msgstr "Distância mínima entre pontos" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:51 +#: src/libslic3r/PrintConfig.cpp:2651 +msgid "Support points density" +msgstr "Densidade dos pontos de suporte" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:52 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1455 +msgid "Auto-generate points" +msgstr "Pontos gerados automaticamente" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:53 +msgid "Manual editing" +msgstr "Edição manual" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:54 +msgid "Clipping of view" +msgstr "Recorte de vista" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:55 +msgid "Reset direction" +msgstr "Restabelecer direção" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:531 +msgid "Add support point" +msgstr "Adicionar ponto de suporte" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:719 +msgid "Delete support point" +msgstr "Deletar ponto de suporte" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:920 +msgid "Change point head diameter" +msgstr "Mudar o diâmetro do ponto da cabeça" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:986 +msgid "Support parameter change" +msgstr "Mudança de parâmetro de suporte" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1094 +msgid "SLA Support Points" +msgstr "Pontos de suporte SLA" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1115 +msgid "SLA gizmo turned on" +msgstr "Gizmo de SLA ligado" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1137 +msgid "Do you want to save your manually edited support points?" +msgstr "Você deseja salvar os pontos manualmente editados?" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1138 +msgid "Save changes?" +msgstr "Salvar mudanças?" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1150 +msgid "SLA gizmo turned off" +msgstr "Gizmo de SLA desligado" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1187 +msgid "Move support point" +msgstr "Mover pontos de suporte" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1286 +msgid "Support points edit" +msgstr "Edição de pontos de suporte" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1355 +msgid "" +"Autogeneration will erase all manually edited points.\n" +"\n" +"Are you sure you want to do it?\n" +msgstr "" +"Gerar automaticamente irá apagar todos os pontos manualmente editados. Tem " +"certeza que quer gerar?\n" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1357 src/slic3r/GUI/GUI.cpp:289 +#: src/slic3r/GUI/WipeTowerDialog.cpp:44 src/slic3r/GUI/WipeTowerDialog.cpp:328 +msgid "Warning" +msgstr "Aviso" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1360 +msgid "Autogenerate support points" +msgstr "Pontos de suporte gerados automaticamente" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1412 +msgid "SLA gizmo keyboard shortcuts" +msgstr "Atalhos no teclado para gizmo SLA" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1423 +msgid "Note: some shortcuts work in (non)editing mode only." +msgstr "Nota: alguns atalhos funcionam somente em modos que não editam." + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1441 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1444 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1445 +msgid "Left click" +msgstr "Clique esquerdo" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1441 +msgid "Add point" +msgstr "Adicionar ponto" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1442 +msgid "Right click" +msgstr "Clique direito" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1442 +msgid "Remove point" +msgstr "Remover ponto" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1443 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1446 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1447 +msgid "Drag" +msgstr "Arrastar" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1443 +msgid "Move point" +msgstr "Mover ponto" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1444 +msgid "Add point to selection" +msgstr "Adicionar ponto à seleção" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1445 +msgid "Remove point from selection" +msgstr "Remover ponto da seleção" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1446 +msgid "Select by rectangle" +msgstr "Selecionar por retângulo" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1447 +msgid "Deselect by rectangle" +msgstr "Desselecionar por retângulo" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1448 +msgid "Select all points" +msgstr "Selecionar todos os pontos" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1450 +msgid "Mouse wheel" +msgstr "Scroll do mouse" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1450 +msgid "Move clipping plane" +msgstr "Mover plano de recorte" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1451 +msgid "Reset clipping plane" +msgstr "Restabelecer plano de recorte" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1454 +msgid "Switch to editing mode" +msgstr "Alterar para modo de edição" + +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:569 +msgid "Gizmo-Place on Face" +msgstr "Gizmo-Colocar em uma face" + +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:641 +msgid "Gizmo-Move" +msgstr "Gizmo-Mover" + +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:646 +msgid "Gizmo-Scale" +msgstr "Gizmo-Escala" + +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:651 +msgid "Gizmo-Rotate" +msgstr "Gizmo-Rotacionar" + +#: src/slic3r/GUI/GUI.cpp:141 src/slic3r/GUI/Tab.cpp:3145 +msgid "It's impossible to print multi-part object(s) with SLA technology." +msgstr "" +"É impossível imprimir objetos com múltiplas partes com a tecnologia SLA." + +#: src/slic3r/GUI/GUI.cpp:142 +msgid "Please check and fix your object list." +msgstr "Favor verificar e concertar sua lista de objetos." + +#: src/slic3r/GUI/GUI.cpp:143 src/slic3r/GUI/Plater.cpp:2246 +#: src/slic3r/GUI/Tab.cpp:3147 +msgid "Attention!" +msgstr "Atenção!" + +#: src/slic3r/GUI/GUI.cpp:283 +msgid "Notice" +msgstr "Aviso" + +#: src/slic3r/GUI/GUI_App.cpp:132 +#, 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 encontrou um erro. Provavelmente foi causado por ficar sem memória. Se " +"você tem certeza que você tem RAM suficiente em seu sistema, isso também " +"pode ser um bug e nós estaríamos contentes se você relatou.\n" +"\n" +"O aplicativo será encerrado agora." + +#: src/slic3r/GUI/GUI_App.cpp:135 +msgid "Fatal error" +msgstr "Erro fatal" + +#: src/slic3r/GUI/GUI_App.cpp:442 +msgid "Changing of an application language" +msgstr "Alteração de um idioma do aplicativo" + +#: src/slic3r/GUI/GUI_App.cpp:450 src/slic3r/GUI/GUI_App.cpp:459 +msgid "Recreating" +msgstr "Recriando" + +#: src/slic3r/GUI/GUI_App.cpp:463 +msgid "Loading of current presets" +msgstr "Carregando presets" + +#: src/slic3r/GUI/GUI_App.cpp:471 +msgid "Loading of a mode view" +msgstr "Carregamento de um modelo de vista" + +#: src/slic3r/GUI/GUI_App.cpp:551 +msgid "Choose one file (3MF/AMF):" +msgstr "Escolha um arquivo (3MF/AMF):" + +#: src/slic3r/GUI/GUI_App.cpp:563 +msgid "Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):" +msgstr "Escolha um ou mais arquivos (STL/OBJ/AMF/3MF/PRUSA):" + +#: src/slic3r/GUI/GUI_App.cpp:625 +msgid "Select the language" +msgstr "Selecione a linguagem" + +#: src/slic3r/GUI/GUI_App.cpp:625 +msgid "Language" +msgstr "Linguagem" + +#: src/slic3r/GUI/GUI_App.cpp:796 +msgid "&Configuration Snapshots" +msgstr "&Captura das config." + +#: src/slic3r/GUI/GUI_App.cpp:796 +msgid "Inspect / activate configuration snapshots" +msgstr "Inspecionar / ativar capturas de config." + +#: src/slic3r/GUI/GUI_App.cpp:797 +msgid "Take Configuration &Snapshot" +msgstr "Capturar &config." + +#: src/slic3r/GUI/GUI_App.cpp:797 +msgid "Capture a configuration snapshot" +msgstr "Capturar uma config." + +#: src/slic3r/GUI/GUI_App.cpp:800 +msgid "&Preferences" +msgstr "&Preferências" + +#: src/slic3r/GUI/GUI_App.cpp:806 +msgid "Application preferences" +msgstr "Preferências de aplicação" + +#: src/slic3r/GUI/GUI_App.cpp:809 src/slic3r/GUI/wxExtensions.cpp:3043 +msgid "Simple" +msgstr "Simples" + +#: src/slic3r/GUI/GUI_App.cpp:809 +msgid "Simple View Mode" +msgstr "Modo simples de visualização" + +#: src/slic3r/GUI/GUI_App.cpp:810 src/slic3r/GUI/GUI_ObjectList.cpp:97 +#: src/slic3r/GUI/GUI_ObjectList.cpp:620 src/slic3r/GUI/Tab.cpp:1061 +#: src/slic3r/GUI/Tab.cpp:1076 src/slic3r/GUI/Tab.cpp:1174 +#: src/slic3r/GUI/Tab.cpp:1177 src/slic3r/GUI/Tab.cpp:1685 +#: src/slic3r/GUI/Tab.cpp:2169 src/slic3r/GUI/Tab.cpp:3785 +#: src/slic3r/GUI/wxExtensions.cpp:3044 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 +msgid "Advanced" +msgstr "Avançado" + +#: src/slic3r/GUI/GUI_App.cpp:810 +msgid "Advanced View Mode" +msgstr "Modo avançado de visualização" + +#: src/slic3r/GUI/GUI_App.cpp:811 src/slic3r/GUI/wxExtensions.cpp:3045 +msgid "Expert" +msgstr "Especialista" + +#: src/slic3r/GUI/GUI_App.cpp:811 +msgid "Expert View Mode" +msgstr "Modo especialista de visualização" + +#: src/slic3r/GUI/GUI_App.cpp:816 +msgid "Mode" +msgstr "Modo" + +#: src/slic3r/GUI/GUI_App.cpp:816 +#, c-format +msgid "%s View Mode" +msgstr "%s Modo de visualização" + +#: src/slic3r/GUI/GUI_App.cpp:818 +msgid "Change Application &Language" +msgstr "Mudar &idioma" + +#: src/slic3r/GUI/GUI_App.cpp:820 +msgid "Flash printer &firmware" +msgstr "Atualizar firmware &da impressora" + +#: src/slic3r/GUI/GUI_App.cpp:820 +msgid "Upload a firmware image into an Arduino based printer" +msgstr "Atualizar o firmware para uma impressora baseada em Arduino" + +#: src/slic3r/GUI/GUI_App.cpp:832 +msgid "Taking configuration snapshot" +msgstr "Capturando a config." + +#: src/slic3r/GUI/GUI_App.cpp:832 +msgid "Snapshot name" +msgstr "Nome da captura" + +#: src/slic3r/GUI/GUI_App.cpp:875 +msgid "" +"Switching the language will trigger application restart.\n" +"You will lose content of the plater." +msgstr "" +"Alterar a linguagem fará com que o aplicativo reinicie.\n" +"Você irá perder conteúdo no prato." + +#: src/slic3r/GUI/GUI_App.cpp:877 +msgid "Do you want to proceed?" +msgstr "Você quer prosseguir?" + +#: src/slic3r/GUI/GUI_App.cpp:878 +msgid "Language selection" +msgstr "Seleção de linguagem" + +#: src/slic3r/GUI/GUI_App.cpp:901 +msgid "&Configuration" +msgstr "&Configuração" + +#: src/slic3r/GUI/GUI_App.cpp:923 +msgid "The presets on the following tabs were modified" +msgstr "Os presets seguintes foram modificados" + +#: src/slic3r/GUI/GUI_App.cpp:923 src/slic3r/GUI/Tab.cpp:3133 +msgid "Discard changes and continue anyway?" +msgstr "Descartar mudanças e continuar assim mesmo?" + +#: src/slic3r/GUI/GUI_App.cpp:926 +msgid "Unsaved Presets" +msgstr "config. não salvas" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:27 +msgid "Start at height" +msgstr "Começar na altura" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:27 +msgid "Stop at height" +msgstr "Parar na altura" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:27 src/slic3r/GUI/Tab.cpp:1033 +#: src/libslic3r/PrintConfig.cpp:66 +msgid "Layer height" +msgstr "Altura da camada" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:153 +msgid "Remove layer range" +msgstr "Remover limite da camada" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:162 +msgid "Add layer range" +msgstr "Adicionar limite da camada" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:34 src/slic3r/GUI/GUI_ObjectList.cpp:88 +#: src/slic3r/GUI/GUI_ObjectList.cpp:611 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 +msgid "Layers and Perimeters" +msgstr "Camadas e perímetros" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:35 src/slic3r/GUI/GUI_ObjectList.cpp:89 +#: src/slic3r/GUI/GUI_ObjectList.cpp:612 src/slic3r/GUI/Plater.cpp:497 +#: src/slic3r/GUI/Tab.cpp:1065 src/slic3r/GUI/Tab.cpp:1066 +#: 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 "Preenchimento" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:36 src/slic3r/GUI/GUI_ObjectList.cpp:90 +#: src/slic3r/GUI/GUI_ObjectList.cpp:613 src/slic3r/GUI/GUI_Preview.cpp:244 +#: src/slic3r/GUI/Tab.cpp:1094 src/slic3r/GUI/Tab.cpp:1095 +#: 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:156 +msgid "Support material" +msgstr "Material de suporte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:39 src/slic3r/GUI/GUI_ObjectList.cpp:94 +#: src/slic3r/GUI/GUI_ObjectList.cpp:617 src/libslic3r/PrintConfig.cpp:2202 +#: src/libslic3r/PrintConfig.cpp:2210 +msgid "Wipe options" +msgstr "Opções de limpeza" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:45 +msgid "Pad and Support" +msgstr "Bloco e suporte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:51 +msgid "Add part" +msgstr "Adicionar parte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:52 +msgid "Add modifier" +msgstr "Adicionar modificador" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:53 +msgid "Add support enforcer" +msgstr "Adicionar reforço de suporte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:54 +msgid "Add support blocker" +msgstr "Adicionar bloqueador de suporte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:91 src/slic3r/GUI/GUI_ObjectList.cpp:614 +#: src/slic3r/GUI/GUI_Preview.cpp:223 src/slic3r/GUI/Tab.cpp:1119 +#: 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 +msgid "Speed" +msgstr "Velocidade" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:92 src/slic3r/GUI/GUI_ObjectList.cpp:615 +#: src/slic3r/GUI/Tab.cpp:1154 src/slic3r/GUI/Tab.cpp:2043 +#: 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 +msgid "Extruders" +msgstr "Exrtrusoras" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:93 src/slic3r/GUI/GUI_ObjectList.cpp:616 +#: 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 +msgid "Extrusion Width" +msgstr "Espessura da extrusão" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:99 src/slic3r/GUI/GUI_ObjectList.cpp:622 +#: src/slic3r/GUI/Plater.cpp:465 src/slic3r/GUI/Tab.cpp:3737 +#: src/slic3r/GUI/Tab.cpp:3738 src/libslic3r/PrintConfig.cpp:2501 +#: src/libslic3r/PrintConfig.cpp:2508 src/libslic3r/PrintConfig.cpp:2517 +#: src/libslic3r/PrintConfig.cpp:2526 src/libslic3r/PrintConfig.cpp:2536 +#: src/libslic3r/PrintConfig.cpp:2562 src/libslic3r/PrintConfig.cpp:2569 +#: src/libslic3r/PrintConfig.cpp:2580 src/libslic3r/PrintConfig.cpp:2590 +#: src/libslic3r/PrintConfig.cpp:2599 src/libslic3r/PrintConfig.cpp:2612 +#: src/libslic3r/PrintConfig.cpp:2622 src/libslic3r/PrintConfig.cpp:2631 +#: src/libslic3r/PrintConfig.cpp:2641 src/libslic3r/PrintConfig.cpp:2652 +#: src/libslic3r/PrintConfig.cpp:2660 +msgid "Supports" +msgstr "Suportes" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:100 src/slic3r/GUI/GUI_ObjectList.cpp:623 +#: src/slic3r/GUI/Plater.cpp:603 src/slic3r/GUI/Tab.cpp:3769 +#: src/slic3r/GUI/Tab.cpp:3770 src/libslic3r/PrintConfig.cpp:2668 +#: src/libslic3r/PrintConfig.cpp:2675 src/libslic3r/PrintConfig.cpp:2689 +#: src/libslic3r/PrintConfig.cpp:2699 src/libslic3r/PrintConfig.cpp:2721 +#: src/libslic3r/PrintConfig.cpp:2732 src/libslic3r/PrintConfig.cpp:2739 +#: src/libslic3r/PrintConfig.cpp:2750 src/libslic3r/PrintConfig.cpp:2759 +#: src/libslic3r/PrintConfig.cpp:2768 +msgid "Pad" +msgstr "Bloco" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:260 +msgid "Name" +msgstr "Nome" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:271 src/slic3r/GUI/GUI_ObjectList.cpp:373 +msgid "Editing" +msgstr "Edição" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:318 +#, c-format +msgid "Auto-repaired (%d errors):\n" +msgstr "Auto reparando (%d erros):\n" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:325 +msgid "degenerate facets" +msgstr "facetas degeneradas" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:326 +msgid "edges fixed" +msgstr "arestas fixadas" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:327 +msgid "facets removed" +msgstr "facetas removidas" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:328 +msgid "facets added" +msgstr "facetas adicionadas" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:329 +msgid "facets reversed" +msgstr "facetas reversidas" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:330 +msgid "backwards edges" +msgstr "arestas viradas para trás" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:338 +msgid "Right button click the icon to fix STL through Netfabb" +msgstr "" +"Clique com o botão direito no ícone para arrumar STL através do Netfabb" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:375 +msgid "Right button click the icon to change the object settings" +msgstr "Clique com o botão direito no ícone para mudar as config. do objeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:377 +msgid "Click the icon to change the object settings" +msgstr "Clique no ícone para mudar as config. do objeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:381 +msgid "Right button click the icon to change the object printable property" +msgstr "" +"Clique com o botão direito no ícone para mudar a propriedade de impressão do " +"objeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:383 +msgid "Click the icon to change the object printable property" +msgstr "Clique no ícone para mudar a propriedade de impressão do objeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:428 src/slic3r/GUI/GUI_ObjectList.cpp:449 +#: src/slic3r/GUI/GUI_ObjectList.cpp:461 src/slic3r/GUI/GUI_ObjectList.cpp:3642 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3652 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3684 src/slic3r/GUI/wxExtensions.cpp:603 +#: src/slic3r/GUI/wxExtensions.cpp:660 src/slic3r/GUI/wxExtensions.cpp:685 +#: src/slic3r/GUI/wxExtensions.cpp:893 +msgid "default" +msgstr "padrão" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:433 src/slic3r/GUI/Tab.cpp:1649 +#: src/libslic3r/PrintConfig.cpp:470 +msgid "Extruder" +msgstr "Extrusora" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:546 +msgid "Rename Object" +msgstr "Renomear objeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:546 +msgid "Rename Sub-object" +msgstr "Renomear sub-objeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:987 src/slic3r/GUI/GUI_ObjectList.cpp:3464 +msgid "Instances to Separated Objects" +msgstr "Instâncias para separar objetos" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1005 +msgid "Volumes in Object reordered" +msgstr "Volume reorganizados no objeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1005 +msgid "Object reordered" +msgstr "Objeto reorganizado" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1060 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1376 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1382 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1623 +#, c-format +msgid "Quick Add Settings (%s)" +msgstr "Adicionar config. rapidamente (%s)" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1137 +msgid "Select showing settings" +msgstr "Selecionar config. mostradas" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1186 +msgid "Add Settings for Layers" +msgstr "Adicionar config. para camadas" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1187 +msgid "Add Settings for Sub-object" +msgstr "Adicionar config. para sub-objetos" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1188 +msgid "Add Settings for Object" +msgstr "Adicionar config. para objetos" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1249 +msgid "Add Settings Bundle for Height range" +msgstr "Adicionar pacote de config. para intervalo de altura" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1250 +msgid "Add Settings Bundle for Sub-object" +msgstr "Adicionar pacote de config. para subobjeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1251 +msgid "Add Settings Bundle for Object" +msgstr "Adicionar pacote de config. para objeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1290 +msgid "Load" +msgstr "Carregar" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1295 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1320 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1323 +msgid "Box" +msgstr "Caixa" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1295 +msgid "Cylinder" +msgstr "Cilindro" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1295 +msgid "Sphere" +msgstr "Esfera" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1295 +msgid "Slab" +msgstr "Placa" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1347 +msgid "Height range Modifier" +msgstr "Modificador de intervalo de altura" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1355 +msgid "Add settings" +msgstr "Adicionar config." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1422 +msgid "Change type" +msgstr "Mudar o tipo" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1429 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1577 +msgid "Set as a Separated Object" +msgstr "Configurar como objeto separado" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1435 +msgid "Printable" +msgstr "Imprimível" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1442 +msgid "Rename" +msgstr "Renomear" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1453 +msgid "Fix through the Netfabb" +msgstr "Arrumar através do Netfabb" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1463 src/slic3r/GUI/Plater.cpp:3552 +msgid "Export as STL" +msgstr "Exportar como STL" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1470 +msgid "Change extruder" +msgstr "Mudar extrusora" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1489 src/libslic3r/PrintConfig.cpp:309 +msgid "Default" +msgstr "Padrão" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1495 +msgid "Select new extruder for the object/part" +msgstr "Selecionar nova extrusora para objeto/parte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1507 +msgid "Scale to print volume" +msgstr "Escalar para volume de impressão" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1507 +msgid "Scale the selected object to fit the print volume" +msgstr "Escale o objeto selecionado para se adequar ao volume de impressão" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1577 +msgid "Set as a Separated Objects" +msgstr "Definir como objetos separados" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1652 +msgid "Load Part" +msgstr "Carregar parte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1687 +msgid "Error!" +msgstr "Erro!" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1732 +msgid "Add Generic Subobject" +msgstr "Adicionar sub-objeto genérico" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1739 +msgid "Generic" +msgstr "Genérico" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1843 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1945 +msgid "Last instance of an object cannot be deleted." +msgstr "A última instância de um objeto não pode ser excluída." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1855 +msgid "Delete Settings" +msgstr "Deletar config." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1879 +msgid "Delete All Instances from Object" +msgstr "Excluir todas as instâncias do objeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1895 +msgid "Delete Height Range" +msgstr "Excluir limite de altura" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1926 +msgid "From Object List You can't delete the last solid part from object." +msgstr "" +"Na lista de objetos não é possível excluir a última parte sólida do objeto." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1930 +msgid "Delete Subobject" +msgstr "Deletar sub-objeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1949 +msgid "Delete Instance" +msgstr "Deletar instância" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1973 src/slic3r/GUI/Plater.cpp:2838 +msgid "" +"The selected object couldn't be split because it contains only one part." +msgstr "O seguinte objeto não pode ser dividido pois contém uma parte." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1977 +msgid "Split to Parts" +msgstr "Dividir em partes" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2025 +msgid "Add Layers" +msgstr "Adicionar camadas" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2150 +msgid "Group manipulation" +msgstr "Manipulação de grupos" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2162 +msgid "Object manipulation" +msgstr "Manipulação de objetos" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2175 +msgid "Object Settings to modify" +msgstr "config. do objeto para modificar" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2179 +msgid "Part Settings to modify" +msgstr "config. da parte para modificar" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2184 +msgid "Layer range Settings to modify" +msgstr "config. de intervalo de camada para modificar" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2190 +msgid "Part manipulation" +msgstr "Manipulação da parte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2196 +msgid "Instance manipulation" +msgstr "Manipulação da instância" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2203 +msgid "Settings for height range" +msgstr "config. para intervalo de altura" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2388 +msgid "Delete Selected Item" +msgstr "Excluir item selecionado" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2525 +msgid "Delete Selected" +msgstr "Excluir seleção" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2584 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2613 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2631 +msgid "Add Height Range" +msgstr "Adicionar intervalo de altura" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2690 +msgid "Edit Height Range" +msgstr "Editar intervalo de altura" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2974 +msgid "Selection-Remove from list" +msgstr "Seleção-Remover da lista" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2982 +msgid "Selection-Add from list" +msgstr "Seleção-Adicionar da lista" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3100 +msgid "Object or Instance" +msgstr "Objeto ou instância" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3101 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3234 +msgid "Part" +msgstr "Parte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3101 +msgid "Layer" +msgstr "Camada" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3103 +msgid "Unsupported selection" +msgstr "Seleção não suportada" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3104 +#, c-format +msgid "You started your selection with %s Item." +msgstr "Você iniciou sua seleção com o item de %s." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3105 +#, c-format +msgid "In this mode you can select only other %s Items%s" +msgstr "Neste modo, você pode selecionar apenas outros %s itens%s" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3108 +msgid "of a current Object" +msgstr "de um objeto atual" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3113 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3188 src/slic3r/GUI/Plater.cpp:126 +msgid "Info" +msgstr "Informação" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3229 +msgid "You can't change a type of the last solid part of the object." +msgstr "Não é possível alterar um tipo da última parte sólida do objeto." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3234 +msgid "Modifier" +msgstr "Modificador" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3234 +msgid "Support Enforcer" +msgstr "Reforçador de suporte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3234 +msgid "Support Blocker" +msgstr "Bloqueador de suporte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3236 +msgid "Type:" +msgstr "Tipo:" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3236 +msgid "Select type of part" +msgstr "Selecione o tipo de parte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3241 +msgid "Change Part Type" +msgstr "Mudar o tipo da parte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3486 +msgid "Enter new name" +msgstr "Insira o novo nome" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3486 +msgid "Renaming" +msgstr "Renomeando" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3502 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3608 src/slic3r/GUI/Tab.cpp:3618 +#: src/slic3r/GUI/Tab.cpp:3622 +msgid "The supplied name is not valid;" +msgstr "O nome inserido não é valido;" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3503 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3609 src/slic3r/GUI/Tab.cpp:3619 +msgid "the following characters are not allowed:" +msgstr "os seguintes caracteres não são permitidos:" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3632 +msgid "Set extruder for selected items" +msgstr "Definir extrusora para itens selecionados" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3633 +msgid "Select extruder number for selected objects and/or parts" +msgstr "Selecione o número da extrusora para objetos e/ou peças selecionados" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3646 +msgid "Select extruder number:" +msgstr "Selecione o número da extrusora:" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3647 +msgid "This extruder will be set for selected items" +msgstr "Esta extrusora será ajustada para artigos selecionados" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3759 src/slic3r/GUI/Selection.cpp:1473 +msgid "Set Printable" +msgstr "Definir como imprimível" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3759 src/slic3r/GUI/Selection.cpp:1473 +msgid "Set Unprintable" +msgstr "Definir não imprimível" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:62 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:105 +msgid "World coordinates" +msgstr "Coordenadas mundiais" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:63 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:106 +msgid "Local coordinates" +msgstr "Coordenadas locais" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:82 +msgid "Select coordinate space, in which the transformation will be performed." +msgstr "" +"Selecione o espaço de coordenadas, no qual a transformação será executada." + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:125 +msgid "Object Manipulation" +msgstr "Manipulação de objeto" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:178 +msgid "Object name" +msgstr "Nome do objeto" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:214 +#, c-format +msgid "Toggle %c axis mirroring" +msgstr "Ativar espelhamento do eixo %c" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:247 +msgid "Set Mirror" +msgstr "Definir espelhamento" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:287 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:292 +msgid "Reset scale" +msgstr "Restabelecer escala" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:305 +msgid "Reset rotation" +msgstr "Restabelecer rotação" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:330 +msgid "Reset Rotation" +msgstr "Restabelecer Rotação" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:342 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:357 +msgid "Drop to bed" +msgstr "Soltar na mesa" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:390 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:454 +msgid "Position" +msgstr "Posição" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:391 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:455 +msgid "Rotation" +msgstr "Rotação" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:456 +msgid "Scale factors" +msgstr "Fatores de escala" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:513 +msgid "Translate" +msgstr "Tradução" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:565 +msgid "" +"You cannot use non-uniform scaling mode for multiple objects/parts selection" +msgstr "" +"Não é possível usar o modo de dimensionamento não uniforme para vários " +"objetos/seleção de peças" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:735 +msgid "Set Position" +msgstr "Definir posição" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:766 +msgid "Set Orientation" +msgstr "Definir orientação" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:831 +msgid "Set Scale" +msgstr "Definir escala" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:915 +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 "" +"O objeto atualmente manipulado é inclinado (os ângulos de rotação não são " +"múltiplos de 90 °).\n" +"O dimensionamento não uniforme de objetos inclinados só é possível no " +"sistema de coordenadas do mundo,\n" +"uma vez que a rotação é incorporada nas coordenadas do objeto." + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:918 +msgid "" +"This operation is irreversible.\n" +"Do you want to proceed?" +msgstr "" +"Esta operação é irreversível.\n" +"Você quer prosseguir?" + +#: src/slic3r/GUI/GUI_ObjectSettings.cpp:59 +msgid "Additional Settings" +msgstr "config. Adicionais" + +#: src/slic3r/GUI/GUI_ObjectSettings.cpp:95 +msgid "Remove parameter" +msgstr "Remover parâmetro" + +#: src/slic3r/GUI/GUI_ObjectSettings.cpp:101 +#, c-format +msgid "Delete Option %s" +msgstr "Excluir opção %s" + +#: src/slic3r/GUI/GUI_ObjectSettings.cpp:146 +#, c-format +msgid "Change Option %s" +msgstr "Alterar opção %s" + +#: src/slic3r/GUI/GUI_Preview.cpp:217 +msgid "View" +msgstr "Vista" + +#: src/slic3r/GUI/GUI_Preview.cpp:220 src/slic3r/GUI/GUI_Preview.cpp:569 +#: src/libslic3r/GCode/PreviewData.cpp:378 +msgid "Feature type" +msgstr "Tipo de recurso" + +#: src/slic3r/GUI/GUI_Preview.cpp:221 src/libslic3r/PrintConfig.cpp:483 +msgid "Height" +msgstr "Altura" + +#: src/slic3r/GUI/GUI_Preview.cpp:222 src/libslic3r/PrintConfig.cpp:2188 +msgid "Width" +msgstr "Espessura" + +#: src/slic3r/GUI/GUI_Preview.cpp:224 +msgid "Volumetric flow rate" +msgstr "Taxa de fluxo volumétrico" + +#: src/slic3r/GUI/GUI_Preview.cpp:225 src/slic3r/GUI/GUI_Preview.cpp:333 +#: src/slic3r/GUI/GUI_Preview.cpp:515 src/slic3r/GUI/GUI_Preview.cpp:568 +#: src/slic3r/GUI/GUI_Preview.cpp:774 src/libslic3r/GCode/PreviewData.cpp:388 +msgid "Tool" +msgstr "Ferramenta" + +#: src/slic3r/GUI/GUI_Preview.cpp:226 src/slic3r/GUI/GUI_Preview.cpp:566 +#: src/libslic3r/GCode/PreviewData.cpp:390 +msgid "Color Print" +msgstr "Impressão colorida" + +#: src/slic3r/GUI/GUI_Preview.cpp:229 +msgid "Show" +msgstr "Mostrar" + +#: src/slic3r/GUI/GUI_Preview.cpp:232 src/slic3r/GUI/GUI_Preview.cpp:233 +msgid "Feature types" +msgstr "Tipos de características" + +#: src/slic3r/GUI/GUI_Preview.cpp:235 src/libslic3r/GCode/PreviewData.cpp:147 +msgid "Perimeter" +msgstr "Perímetro" + +#: src/slic3r/GUI/GUI_Preview.cpp:236 src/libslic3r/GCode/PreviewData.cpp:148 +msgid "External perimeter" +msgstr "Perímetro externo" + +#: src/slic3r/GUI/GUI_Preview.cpp:237 src/libslic3r/GCode/PreviewData.cpp:149 +msgid "Overhang perimeter" +msgstr "Perímetro de angulação" + +#: src/slic3r/GUI/GUI_Preview.cpp:238 src/libslic3r/GCode/PreviewData.cpp:150 +msgid "Internal infill" +msgstr "Preenchimento interno" + +#: src/slic3r/GUI/GUI_Preview.cpp:239 src/libslic3r/PrintConfig.cpp:1736 +#: src/libslic3r/PrintConfig.cpp:1746 src/libslic3r/GCode/PreviewData.cpp:151 +msgid "Solid infill" +msgstr "Preenchimento sólido" + +#: src/slic3r/GUI/GUI_Preview.cpp:240 src/libslic3r/PrintConfig.cpp:2068 +#: src/libslic3r/PrintConfig.cpp:2079 src/libslic3r/GCode/PreviewData.cpp:152 +msgid "Top solid infill" +msgstr "Preenchimento do sólido do topo" + +#: src/slic3r/GUI/GUI_Preview.cpp:241 src/libslic3r/GCode/PreviewData.cpp:153 +msgid "Bridge infill" +msgstr "Preenchimento de pontes" + +#: src/slic3r/GUI/GUI_Preview.cpp:242 src/libslic3r/PrintConfig.cpp:895 +#: src/libslic3r/GCode/PreviewData.cpp:154 +msgid "Gap fill" +msgstr "Preenchimento de vão" + +#: src/slic3r/GUI/GUI_Preview.cpp:243 src/slic3r/GUI/Tab.cpp:1085 +#: src/libslic3r/GCode/PreviewData.cpp:155 +msgid "Skirt" +msgstr "Saia" + +#: src/slic3r/GUI/GUI_Preview.cpp:245 src/libslic3r/PrintConfig.cpp:1954 +#: src/libslic3r/GCode/PreviewData.cpp:157 +msgid "Support material interface" +msgstr "Interface do material de suporte" + +#: src/slic3r/GUI/GUI_Preview.cpp:246 src/slic3r/GUI/Tab.cpp:1165 +#: src/libslic3r/GCode/PreviewData.cpp:158 +msgid "Wipe tower" +msgstr "Torre de limpeza" + +#: src/slic3r/GUI/GUI_Preview.cpp:251 src/libslic3r/PrintConfig.cpp:2102 +msgid "Travel" +msgstr "Viagem" + +#: src/slic3r/GUI/GUI_Preview.cpp:252 +msgid "Retractions" +msgstr "Retrações" + +#: src/slic3r/GUI/GUI_Preview.cpp:253 +msgid "Unretractions" +msgstr "Retorno da retração" + +#: src/slic3r/GUI/GUI_Preview.cpp:254 +msgid "Shells" +msgstr "Paredes" + +#: src/slic3r/GUI/GUI_Preview.cpp:255 +msgid "Legend" +msgstr "Legenda" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:14 src/slic3r/GUI/MainFrame.cpp:683 +msgid "Keyboard Shortcuts" +msgstr "Atalhos do teclado" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:107 +msgid "Open project STL/OBJ/AMF/3MF with config, delete bed" +msgstr "Abra o projeto STL/OBJ/AMF/3MF com config., excluir mesa" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:108 +msgid "Import STL/OBJ/AMF/3MF without config, keep bed" +msgstr "Importação STL/OBJ/AMF/3MF sem config., manter a mesa" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:109 +msgid "Load Config from .ini/amf/3mf/gcode" +msgstr "Carregar config. de um .ini/AMF/3mf/Gcode" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:110 src/slic3r/GUI/Plater.cpp:837 +#: src/slic3r/GUI/Plater.cpp:4822 src/libslic3r/PrintConfig.cpp:3163 +msgid "Export G-code" +msgstr "Exportar G-code" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:111 +msgid "Save project (3MF)" +msgstr "Salvar projeto (3MF)" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:112 +msgid "Load Config from .ini/amf/3mf/gcode and merge" +msgstr "Carregar config. de um. ini/AMF/3mf/Gcode e mesclar" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:113 +msgid "(Re)slice" +msgstr "(Re)fatiar" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:116 +msgid "Select Plater Tab" +msgstr "Selecione a guia de prato" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:118 +msgid "Select Print Settings Tab" +msgstr "Selecione a guia config. de impressão" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:119 +msgid "Select Filament Settings Tab" +msgstr "Selecione a guia config. de filamento" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:120 +msgid "Select Printer Settings Tab" +msgstr "Selecione a guia config. da impressora" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:121 +msgid "Switch to 3D" +msgstr "Mude para 3D" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:122 +msgid "Switch to Preview" +msgstr "Mudar para pré-visualização" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:123 src/slic3r/GUI/Preferences.cpp:10 +msgid "Preferences" +msgstr "Preferências" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:124 +#: src/slic3r/GUI/PrintHostDialogs.cpp:136 +msgid "Print host upload queue" +msgstr "Fila de carregamento do host de impressão" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:125 +msgid "Camera view" +msgstr "Vista da câmera" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:126 +msgid "Add Instance of the selected object" +msgstr "Adicionar instância do objeto selecionado" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:127 +msgid "Remove Instance of the selected object" +msgstr "Remover instância do objeto selecionado" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:128 +msgid "Show keyboard shortcuts list" +msgstr "Mostrar lista dos atalhos no teclado" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:129 +msgid "Press to select multiple object or move multiple object with mouse" +msgstr "" +"Aperte para selecionar múltiplos objetos ou mover múltiplos objetos com o " +"mouse" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:131 +msgid "Main Shortcuts" +msgstr "Atalhos principais" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:139 +msgid "Select All objects" +msgstr "Selecionar todos os objetos" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:140 +msgid "Delete selected" +msgstr "Deletar seleção" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:141 +msgid "Delete All" +msgstr "Deletar todos" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:142 +msgid "Copy to clipboard" +msgstr "Copiar para a área de transferência" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:143 +msgid "Paste from clipboard" +msgstr "Colar da área de transferência" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:144 +msgid "Gizmo move" +msgstr "Gizmo-Mover" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:145 +msgid "Gizmo scale" +msgstr "Gizmo-Escala" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:146 +msgid "Gizmo rotate" +msgstr "Gizmo-Rotacionar" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:147 +msgid "Gizmo cut" +msgstr "Gizmo-Cortar" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:148 +msgid "Gizmo Place face on bed" +msgstr "Colocar face do Gizmo na mesa" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:149 +msgid "Gizmo SLA support points" +msgstr "Pontos de suporte do Gizmo SLA" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:150 +#, 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 "" +"Pressione para ativar o retângulo de seleção\n" +"ou para encaixar em 5% i na escala Gizmo\n" +"ou para encaixar por 1mm em Gizmo mover" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:151 +msgid "" +"Press to scale selection to fit print volume\n" +"in Gizmo scale" +msgstr "" +"Pressione para dimensionar a seleção ao volume de impressão\n" +"na escala Gizmo" + +#: 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 "" +"Pressione para ativar o retângulo de deseleção\n" +"ou para dimensionar ou girar objetos selecionados\n" +"em torno de seu próprio centro" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:153 +msgid "Press to activate one direction scaling in Gizmo scale" +msgstr "Pressione para ativar um dimensionamento de direção na escala Gizmo" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:154 +msgid "Change camera type (perspective, orthographic)" +msgstr "Alterar tipo de câmera (perspectiva, ortográfica)" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:155 +msgid "Zoom to Bed" +msgstr "Ampliar para a mesa" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:156 +msgid "Zoom to all objects in scene, if none selected" +msgstr "Ampliar para todos os objetos na cena, se nenhum selecionado" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:157 +msgid "Zoom to selected object" +msgstr "Ampliar para o objeto selecionado" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:158 +msgid "Zoom in" +msgstr "Ampliar" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:159 +msgid "Zoom out" +msgstr "Dimiuir" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:160 +msgid "Unselect gizmo / Clear selection" +msgstr "Desmarcar Gizmo/limpar seleção" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:166 +msgid "Plater Shortcuts" +msgstr "Atalhos do prato" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:181 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:193 +msgid "Arrow Up" +msgstr "Seta para cima" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:181 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:183 +msgid "Upper Layer" +msgstr "Camada superior" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:182 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:194 +msgid "Arrow Down" +msgstr "Seta para baixo" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:182 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:184 +msgid "Lower Layer" +msgstr "Camada inferior" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:185 +msgid "Show/Hide (L)egend" +msgstr "Mostrar/ocultar (L) egenda" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:187 +msgid "Preview Shortcuts" +msgstr "Atalhos de visualização" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:193 +msgid "Move current slider thumb Up" +msgstr "Mover a barra de rolagem para cima" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:194 +msgid "Move current slider thumb Down" +msgstr "Mover a barra de rolagem para baixo" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:195 +msgid "Arrow Left" +msgstr "Seta esquerda" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:195 +msgid "Set upper thumb to current slider thumb" +msgstr "Definir o polegar superior para o polegar deslizante atual" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:196 +msgid "Arrow Right" +msgstr "Seta direita" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:196 +msgid "Set lower thumb to current slider thumb" +msgstr "Definir o polegar inferior para o polegar deslizante atual" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:197 +msgid "Add color change marker for current layer" +msgstr "Adicionar mudança de cor para a camada atual" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:198 +msgid "Delete color change marker for current layer" +msgstr "Excluir mudança de cor para a camada atual" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:200 +msgid "Layers Slider Shortcuts" +msgstr "Atalhos da barra de rolagem de camadas" + +#: src/slic3r/GUI/MainFrame.cpp:64 +msgid "" +" - Remember to check for updates at http://github.com/prusa3d/PrusaSlicer/" +"releases" +msgstr "" +" - Lembre-se de verificar por atualizações em http://github.com/prusa3d/" +"PrusaSlicer/releases" + +#: src/slic3r/GUI/MainFrame.cpp:159 +msgid "based on Slic3r" +msgstr "baseado no Slic3r" + +#: src/slic3r/GUI/MainFrame.cpp:189 +msgid "Plater" +msgstr "Prato" + +#: src/slic3r/GUI/MainFrame.cpp:400 +msgid "&New Project" +msgstr "&Novo projeto" + +#: src/slic3r/GUI/MainFrame.cpp:400 +msgid "Start a new project" +msgstr "Começar um novo projeto" + +#: src/slic3r/GUI/MainFrame.cpp:403 +msgid "&Open Project" +msgstr "&Abrir projeto" + +#: src/slic3r/GUI/MainFrame.cpp:403 +msgid "Open a project file" +msgstr "Abrir novo projeto" + +#: src/slic3r/GUI/MainFrame.cpp:408 +msgid "Recent projects" +msgstr "Projetos recentes" + +#: src/slic3r/GUI/MainFrame.cpp:417 +msgid "The selected project is no more available" +msgstr "O projeto selecionado não está mais disponível" + +#: src/slic3r/GUI/MainFrame.cpp:417 src/slic3r/GUI/MainFrame.cpp:755 +#: src/slic3r/GUI/PrintHostDialogs.cpp:231 +msgid "Error" +msgstr "Erro" + +#: src/slic3r/GUI/MainFrame.cpp:441 +msgid "&Save Project" +msgstr "&Salvar projeto" + +#: src/slic3r/GUI/MainFrame.cpp:441 +msgid "Save current project file" +msgstr "Salvar arquivo" + +#: src/slic3r/GUI/MainFrame.cpp:445 src/slic3r/GUI/MainFrame.cpp:447 +msgid "Save Project &as" +msgstr "Salvar projeto &como" + +#: src/slic3r/GUI/MainFrame.cpp:445 src/slic3r/GUI/MainFrame.cpp:447 +msgid "Save current project file as" +msgstr "Salvar arquivo atual como" + +#: src/slic3r/GUI/MainFrame.cpp:455 +msgid "Import STL/OBJ/AM&F/3MF" +msgstr "Import STL/OBJ/AM&F/3MF" + +#: src/slic3r/GUI/MainFrame.cpp:455 +msgid "Load a model" +msgstr "Carregar um modelo" + +#: src/slic3r/GUI/MainFrame.cpp:459 +msgid "Import &Config" +msgstr "Importar &config." + +#: src/slic3r/GUI/MainFrame.cpp:459 +msgid "Load exported configuration file" +msgstr "Carregar config. de arquivo exportado" + +#: src/slic3r/GUI/MainFrame.cpp:461 +msgid "Import Config from &project" +msgstr "Importar Config do &projeto" + +#: src/slic3r/GUI/MainFrame.cpp:461 +msgid "Load configuration from project file" +msgstr "Carregar config. de arquivo de projeto" + +#: src/slic3r/GUI/MainFrame.cpp:464 +msgid "Import Config &Bundle" +msgstr "Importar coleção &de config." + +#: src/slic3r/GUI/MainFrame.cpp:464 +msgid "Load presets from a bundle" +msgstr "Carregar predefinições de um pacote" + +#: src/slic3r/GUI/MainFrame.cpp:466 +msgid "&Import" +msgstr "&Importar" + +#: src/slic3r/GUI/MainFrame.cpp:469 src/slic3r/GUI/MainFrame.cpp:719 +msgid "Export &G-code" +msgstr "Exportar &G-code" + +#: src/slic3r/GUI/MainFrame.cpp:469 +msgid "Export current plate as G-code" +msgstr "Exporte o prato atual como o G-code" + +#: src/slic3r/GUI/MainFrame.cpp:473 src/slic3r/GUI/MainFrame.cpp:720 +msgid "S&end G-code" +msgstr "E&nviar G-code" + +#: src/slic3r/GUI/MainFrame.cpp:473 +msgid "Send to print current plate as G-code" +msgstr "Enviar para imprimir prato atual como G-code" + +#: src/slic3r/GUI/MainFrame.cpp:478 +msgid "Export plate as &STL" +msgstr "Exportar prato como &STL" + +#: src/slic3r/GUI/MainFrame.cpp:478 +msgid "Export current plate as STL" +msgstr "Exporte o prato atual como STL" + +#: src/slic3r/GUI/MainFrame.cpp:481 +msgid "Export plate as STL &including supports" +msgstr "Exportar prato como STL &incluindo suportes" + +#: src/slic3r/GUI/MainFrame.cpp:481 +msgid "Export current plate as STL including supports" +msgstr "Exporte o prato atual como o STL que inclui suportes" + +#: src/slic3r/GUI/MainFrame.cpp:484 +msgid "Export plate as &AMF" +msgstr "Exportar prato como &AMF" + +#: src/slic3r/GUI/MainFrame.cpp:484 +msgid "Export current plate as AMF" +msgstr "Exporte o prato atual como o AMF" + +#: src/slic3r/GUI/MainFrame.cpp:488 +msgid "Export &toolpaths as OBJ" +msgstr "Exportar &percurso da ferramenta como OBJ" + +#: src/slic3r/GUI/MainFrame.cpp:488 +msgid "Export toolpaths as OBJ" +msgstr "Exportar percursos como OBJ" + +#: src/slic3r/GUI/MainFrame.cpp:492 +msgid "Export &Config" +msgstr "Exportar &config." + +#: src/slic3r/GUI/MainFrame.cpp:492 +msgid "Export current configuration to file" +msgstr "Exporte a config. atual para o arquivo" + +#: src/slic3r/GUI/MainFrame.cpp:494 +msgid "Export Config &Bundle" +msgstr "Exportar coleção &de config." + +#: src/slic3r/GUI/MainFrame.cpp:494 +msgid "Export all presets to file" +msgstr "Exporte todas as predefinições para o arquivo" + +#: src/slic3r/GUI/MainFrame.cpp:496 +msgid "&Export" +msgstr "&Exportar" + +#: src/slic3r/GUI/MainFrame.cpp:502 +msgid "Quick Slice" +msgstr "Fatiamento rápido" + +#: src/slic3r/GUI/MainFrame.cpp:502 +msgid "Slice a file into a G-code" +msgstr "Fatiar um arquivo em um G-code" + +#: src/slic3r/GUI/MainFrame.cpp:508 +msgid "Quick Slice and Save As" +msgstr "Salvamento rápido e salvar como" + +#: src/slic3r/GUI/MainFrame.cpp:508 +msgid "Slice a file into a G-code, save as" +msgstr "Fatiar um arquivo em um G-code, salvar como" + +#: src/slic3r/GUI/MainFrame.cpp:514 +msgid "Repeat Last Quick Slice" +msgstr "Repetir Último Fatiamento Rápido" + +#: src/slic3r/GUI/MainFrame.cpp:514 +msgid "Repeat last quick slice" +msgstr "Repetir último fatiamento rápido" + +#: src/slic3r/GUI/MainFrame.cpp:522 +msgid "(Re)Slice No&w" +msgstr "(Re)Fatiar ago&ra" + +#: src/slic3r/GUI/MainFrame.cpp:522 +msgid "Start new slicing process" +msgstr "Começar novo processo de fatiamento" + +#: src/slic3r/GUI/MainFrame.cpp:526 +msgid "&Repair STL file" +msgstr "&Reparar arquivo STL" + +#: src/slic3r/GUI/MainFrame.cpp:526 +msgid "Automatically repair an STL file" +msgstr "Reparar automaticamente um arquivo STL" + +#: src/slic3r/GUI/MainFrame.cpp:529 +msgid "&Quit" +msgstr "&Sair" + +#: src/slic3r/GUI/MainFrame.cpp:529 +#, c-format +msgid "Quit %s" +msgstr "Sair %s" + +#: src/slic3r/GUI/MainFrame.cpp:554 +msgid "&Select all" +msgstr "&Selecionar todos" + +#: src/slic3r/GUI/MainFrame.cpp:555 +msgid "Selects all objects" +msgstr "Selecionar todos os objetos" + +#: src/slic3r/GUI/MainFrame.cpp:557 +msgid "D&eselect all" +msgstr "D&eselecionar todos" + +#: src/slic3r/GUI/MainFrame.cpp:558 +msgid "Deselects all objects" +msgstr "Deselecionar todos os objetos" + +#: src/slic3r/GUI/MainFrame.cpp:561 +msgid "&Delete selected" +msgstr "&Excluir seleção" + +#: src/slic3r/GUI/MainFrame.cpp:562 +msgid "Deletes the current selection" +msgstr "Excluir a seleção atual" + +#: src/slic3r/GUI/MainFrame.cpp:564 +msgid "Delete &all" +msgstr "Excluir &todos" + +#: src/slic3r/GUI/MainFrame.cpp:565 +msgid "Deletes all objects" +msgstr "Excluir todos os objetos" + +#: src/slic3r/GUI/MainFrame.cpp:569 +msgid "&Undo" +msgstr "&Desfazer" + +#: src/slic3r/GUI/MainFrame.cpp:572 +msgid "&Redo" +msgstr "&Refazer" + +#: src/slic3r/GUI/MainFrame.cpp:577 +msgid "&Copy" +msgstr "&Copiar" + +#: src/slic3r/GUI/MainFrame.cpp:578 +msgid "Copy selection to clipboard" +msgstr "Copiar seleção para a área de transferência" + +#: src/slic3r/GUI/MainFrame.cpp:580 +msgid "&Paste" +msgstr "&Colar" + +#: src/slic3r/GUI/MainFrame.cpp:581 +msgid "Paste clipboard" +msgstr "Colar área de transferência" + +#: src/slic3r/GUI/MainFrame.cpp:590 +msgid "&Plater Tab" +msgstr "&Prato" + +#: src/slic3r/GUI/MainFrame.cpp:590 +msgid "Show the plater" +msgstr "Mostrar o prato" + +#: src/slic3r/GUI/MainFrame.cpp:597 +msgid "P&rint Settings Tab" +msgstr "C&onfig. de impressão" + +#: src/slic3r/GUI/MainFrame.cpp:597 +msgid "Show the print settings" +msgstr "Mostrar as config. de impressão" + +#: src/slic3r/GUI/MainFrame.cpp:599 src/slic3r/GUI/MainFrame.cpp:722 +msgid "&Filament Settings Tab" +msgstr "&config. de filamentos" + +#: src/slic3r/GUI/MainFrame.cpp:599 +msgid "Show the filament settings" +msgstr "Mostrar as config. de filamento" + +#: src/slic3r/GUI/MainFrame.cpp:602 +msgid "Print&er Settings Tab" +msgstr "A&ba de config. da impressora" + +#: src/slic3r/GUI/MainFrame.cpp:602 +msgid "Show the printer settings" +msgstr "Mostrar as config. da impressora" + +#: src/slic3r/GUI/MainFrame.cpp:606 +msgid "3&D" +msgstr "3&D" + +#: src/slic3r/GUI/MainFrame.cpp:606 +msgid "Show the 3D editing view" +msgstr "Mostrar a vista de edição 3D" + +#: src/slic3r/GUI/MainFrame.cpp:609 +msgid "Pre&view" +msgstr "Pre&visualização" + +#: src/slic3r/GUI/MainFrame.cpp:609 +msgid "Show the 3D slices preview" +msgstr "Mostrar a pré-visualização do fatiamento 3D" + +#: src/slic3r/GUI/MainFrame.cpp:628 +msgid "Print &Host Upload Queue" +msgstr "Imprimir &Fila de upload do Host" + +#: src/slic3r/GUI/MainFrame.cpp:628 +msgid "Display the Print Host Upload Queue window" +msgstr "Exibir a janela fila de upload do host de impressão" + +#: src/slic3r/GUI/MainFrame.cpp:637 +msgid "Iso" +msgstr "Isométrico" + +#: src/slic3r/GUI/MainFrame.cpp:637 +msgid "Iso View" +msgstr "Vista isométrica" + +#. 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:641 src/libslic3r/PrintConfig.cpp:2094 +msgid "Top" +msgstr "Topo" + +#: src/slic3r/GUI/MainFrame.cpp:641 +msgid "Top View" +msgstr "Vista do topo" + +#. 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:644 src/libslic3r/PrintConfig.cpp:159 +msgid "Bottom" +msgstr "Base" + +#: src/slic3r/GUI/MainFrame.cpp:644 +msgid "Bottom View" +msgstr "Vista da base" + +#: src/slic3r/GUI/MainFrame.cpp:646 +msgid "Front" +msgstr "Frente" + +#: src/slic3r/GUI/MainFrame.cpp:646 +msgid "Front View" +msgstr "Vista da frente" + +#: src/slic3r/GUI/MainFrame.cpp:648 src/libslic3r/PrintConfig.cpp:1611 +msgid "Rear" +msgstr "Traseira" + +#: src/slic3r/GUI/MainFrame.cpp:648 +msgid "Rear View" +msgstr "Vista traseira" + +#: src/slic3r/GUI/MainFrame.cpp:650 +msgid "Left" +msgstr "Esquerda" + +#: src/slic3r/GUI/MainFrame.cpp:650 +msgid "Left View" +msgstr "Vista esquerda" + +#: src/slic3r/GUI/MainFrame.cpp:652 +msgid "Right" +msgstr "Direita" + +#: src/slic3r/GUI/MainFrame.cpp:652 +msgid "Right View" +msgstr "Vista direita" + +#: src/slic3r/GUI/MainFrame.cpp:659 +msgid "Prusa 3D &Drivers" +msgstr "Drivers 3D &Prusa" + +#: src/slic3r/GUI/MainFrame.cpp:659 +msgid "Open the Prusa3D drivers download page in your browser" +msgstr "Abrir a página para baixar os drivers da Prusa3D no seu navegador" + +#: src/slic3r/GUI/MainFrame.cpp:661 +msgid "Software &Releases" +msgstr "Lançamentos de &software" + +#: src/slic3r/GUI/MainFrame.cpp:661 +msgid "Open the software releases page in your browser" +msgstr "Abrir a página de lançamentos de software no seu navegador" + +#: src/slic3r/GUI/MainFrame.cpp:667 +#, c-format +msgid "%s &Website" +msgstr "%s &Site" + +#: src/slic3r/GUI/MainFrame.cpp:668 +#, c-format +msgid "Open the %s website in your browser" +msgstr "Abra o site do %s no seu navegador" + +#: src/slic3r/GUI/MainFrame.cpp:674 +msgid "System &Info" +msgstr "Informação &do sistema" + +#: src/slic3r/GUI/MainFrame.cpp:674 +msgid "Show system information" +msgstr "Mostrar a informação do sistema" + +#: src/slic3r/GUI/MainFrame.cpp:676 +msgid "Show &Configuration Folder" +msgstr "Mostrar &pasta de config." + +#: src/slic3r/GUI/MainFrame.cpp:676 +msgid "Show user configuration folder (datadir)" +msgstr "Mostrar pasta de config. do usuário (datadir)" + +#: src/slic3r/GUI/MainFrame.cpp:678 +msgid "Report an I&ssue" +msgstr "Reportar um p&roblema" + +#: src/slic3r/GUI/MainFrame.cpp:678 +#, c-format +msgid "Report an issue on %s" +msgstr "Relatar um problema em %s" + +#: src/slic3r/GUI/MainFrame.cpp:680 +#, c-format +msgid "&About %s" +msgstr "&Sobre %s" + +#: src/slic3r/GUI/MainFrame.cpp:680 +msgid "Show about dialog" +msgstr "Mostrar diálogo sobre" + +#: src/slic3r/GUI/MainFrame.cpp:683 +msgid "Show the list of the keyboard shortcuts" +msgstr "Mostrar lista dos atalhos no teclado" + +#: src/slic3r/GUI/MainFrame.cpp:691 +msgid "&File" +msgstr "&Arquivo" + +#: src/slic3r/GUI/MainFrame.cpp:692 +msgid "&Edit" +msgstr "&Editar" + +#: src/slic3r/GUI/MainFrame.cpp:693 +msgid "&Window" +msgstr "&Janela" + +#: src/slic3r/GUI/MainFrame.cpp:694 +msgid "&View" +msgstr "&Vista" + +#: src/slic3r/GUI/MainFrame.cpp:697 +msgid "&Help" +msgstr "&Ajuda" + +#: src/slic3r/GUI/MainFrame.cpp:719 +msgid "E&xport" +msgstr "E&xportar" + +#: src/slic3r/GUI/MainFrame.cpp:720 +msgid "S&end to print" +msgstr "E&nviar para impressora" + +#: src/slic3r/GUI/MainFrame.cpp:722 +msgid "Mate&rial Settings Tab" +msgstr "A&ba de config. de material" + +#: src/slic3r/GUI/MainFrame.cpp:743 +msgid "Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):" +msgstr "Escolha um arquivo para fatiar (STL/OBJ/AMF/3MF/PRUSA):" + +#: src/slic3r/GUI/MainFrame.cpp:754 +msgid "No previously sliced file." +msgstr "Sem arquivo fatiado anteriormente." + +#: src/slic3r/GUI/MainFrame.cpp:760 +msgid "Previously sliced file (" +msgstr "Arquivo fatiado anteriormente (" + +#: src/slic3r/GUI/MainFrame.cpp:760 +msgid ") not found." +msgstr ") não encontrado." + +#: src/slic3r/GUI/MainFrame.cpp:761 +msgid "File Not Found" +msgstr "Arquivo não encontrado" + +#: src/slic3r/GUI/MainFrame.cpp:796 +#, c-format +msgid "Save %s file as:" +msgstr "Salve o arquivo %s como:" + +#: src/slic3r/GUI/MainFrame.cpp:796 +msgid "SVG" +msgstr "SVG" + +#: src/slic3r/GUI/MainFrame.cpp:796 +msgid "G-code" +msgstr "G-code" + +#: src/slic3r/GUI/MainFrame.cpp:808 +msgid "Save zip file as:" +msgstr "Salvar arquivo compactado(zip) como:" + +#: src/slic3r/GUI/MainFrame.cpp:817 src/slic3r/GUI/Plater.cpp:2981 +#: src/slic3r/GUI/Plater.cpp:4533 src/slic3r/GUI/Tab.cpp:1194 +#: src/slic3r/GUI/Tab.cpp:3786 +msgid "Slicing" +msgstr "Fatiamento" + +#. TRN "Processing input_file_basename" +#: src/slic3r/GUI/MainFrame.cpp:819 +#, c-format +msgid "Processing %s" +msgstr "Processando %s" + +#: src/slic3r/GUI/MainFrame.cpp:842 +msgid " was successfully sliced." +msgstr " foi fatiado com sucesso." + +#: src/slic3r/GUI/MainFrame.cpp:844 +msgid "Slicing Done!" +msgstr "Fatiamento completo!" + +#: src/slic3r/GUI/MainFrame.cpp:859 +msgid "Select the STL file to repair:" +msgstr "Selecione o arquivo STL para corrigir:" + +#: src/slic3r/GUI/MainFrame.cpp:869 +msgid "Save OBJ file (less prone to coordinate errors than STL) as:" +msgstr "" +"Salvar arquivo OBJ (menos propenso a erros de coordenada que STL) como:" + +#: src/slic3r/GUI/MainFrame.cpp:881 +msgid "Your file was repaired." +msgstr "Seu arquivo foi corrigido." + +#: src/slic3r/GUI/MainFrame.cpp:881 src/libslic3r/PrintConfig.cpp:3257 +msgid "Repair" +msgstr "Corrigir" + +#: src/slic3r/GUI/MainFrame.cpp:895 +msgid "Save configuration as:" +msgstr "Salvar config. como:" + +#: src/slic3r/GUI/MainFrame.cpp:914 src/slic3r/GUI/MainFrame.cpp:976 +msgid "Select configuration to load:" +msgstr "Selecionar config. para carregar:" + +#: src/slic3r/GUI/MainFrame.cpp:950 +msgid "Save presets bundle as:" +msgstr "Salvar pacote de predefinições como:" + +#: src/slic3r/GUI/MainFrame.cpp:997 +#, c-format +msgid "%d presets successfully imported." +msgstr "%d predefinições importadas com êxito." + +#: src/slic3r/GUI/MsgDialog.cpp:73 +#, c-format +msgid "%s error" +msgstr "%s erro" + +#: src/slic3r/GUI/MsgDialog.cpp:74 +#, c-format +msgid "%s has encountered an error" +msgstr "%s encontrou um erro" + +#: src/slic3r/GUI/OptionsGroup.cpp:249 +msgctxt "Layers" +msgid "Top" +msgstr "Topo" + +#: src/slic3r/GUI/OptionsGroup.cpp:249 +msgctxt "Layers" +msgid "Bottom" +msgstr "Base" + +#: src/slic3r/GUI/Plater.cpp:146 +msgid "Volume" +msgstr "Volume" + +#: src/slic3r/GUI/Plater.cpp:147 +msgid "Facets" +msgstr "Facetas" + +#: src/slic3r/GUI/Plater.cpp:148 +msgid "Materials" +msgstr "Materiais" + +#: src/slic3r/GUI/Plater.cpp:151 +msgid "Manifold" +msgstr "Múltiplo" + +#: src/slic3r/GUI/Plater.cpp:201 +msgid "Sliced Info" +msgstr "Informações fatiadas" + +#: src/slic3r/GUI/Plater.cpp:220 src/slic3r/GUI/Plater.cpp:1150 +msgid "Used Filament (m)" +msgstr "Filamento utilizado (m)" + +#: src/slic3r/GUI/Plater.cpp:221 +msgid "Used Filament (mm³)" +msgstr "Filamento utilizado (mm³)" + +#: src/slic3r/GUI/Plater.cpp:222 +msgid "Used Filament (g)" +msgstr "Filamento utilizado (g)" + +#: src/slic3r/GUI/Plater.cpp:223 +msgid "Used Material (unit)" +msgstr "Material utilizado (unidade)" + +#: src/slic3r/GUI/Plater.cpp:224 src/slic3r/GUI/Plater.cpp:1165 +#: src/libslic3r/PrintConfig.cpp:742 +msgid "Cost" +msgstr "Custo" + +#: src/slic3r/GUI/Plater.cpp:225 src/slic3r/GUI/Plater.cpp:1137 +#: src/slic3r/GUI/Plater.cpp:1179 +msgid "Estimated printing time" +msgstr "Tempo estimado de impressão" + +#: src/slic3r/GUI/Plater.cpp:226 +msgid "Number of tool changes" +msgstr "Número de mudanças de ferramenta" + +#: src/slic3r/GUI/Plater.cpp:316 +msgid "Click to edit preset" +msgstr "Clique para editar a predefinição" + +#: src/slic3r/GUI/Plater.cpp:468 +msgid "Select what kind of support do you need" +msgstr "Selecione o tipo de suporte que você precisa" + +#: src/slic3r/GUI/Plater.cpp:470 src/libslic3r/PrintConfig.cpp:1865 +#: src/libslic3r/PrintConfig.cpp:2561 +msgid "Support on build plate only" +msgstr "Suportes somente na mesa de impressão" + +#: src/slic3r/GUI/Plater.cpp:471 src/slic3r/GUI/Plater.cpp:592 +msgid "For support enforcers only" +msgstr "Para apenas reforçadores de suporte" + +#: src/slic3r/GUI/Plater.cpp:472 +msgid "Everywhere" +msgstr "Em toda parte" + +#: src/slic3r/GUI/Plater.cpp:504 src/slic3r/GUI/Tab.cpp:1091 +msgid "Brim" +msgstr "Aba" + +#: src/slic3r/GUI/Plater.cpp:506 +msgid "" +"This flag enables the brim that will be printed around each object on the " +"first layer." +msgstr "" +"Este sinalizador permite que a aba que será impressa em torno de cada objeto " +"na primeira camada." + +#: src/slic3r/GUI/Plater.cpp:514 +msgid "Purging volumes" +msgstr "Volumes de purga" + +#: src/slic3r/GUI/Plater.cpp:606 +msgid "Select what kind of pad do you need" +msgstr "Selecione o tipo de bloco que você precisa" + +#: src/slic3r/GUI/Plater.cpp:608 +msgid "Below object" +msgstr "Abaixo do objeto" + +#: src/slic3r/GUI/Plater.cpp:609 +msgid "Around object" +msgstr "Em torno do objeto" + +#: src/slic3r/GUI/Plater.cpp:781 +msgid "Print settings" +msgstr "Config. de impressão" + +#: src/slic3r/GUI/Plater.cpp:782 src/slic3r/GUI/Tab.cpp:1640 +#: src/slic3r/GUI/Tab.cpp:1641 +msgid "Filament" +msgstr "Filamento" + +#: src/slic3r/GUI/Plater.cpp:783 +msgid "SLA print settings" +msgstr "Config. de impressão de SLA" + +#: src/slic3r/GUI/Plater.cpp:784 src/slic3r/GUI/Preset.cpp:1314 +msgid "SLA material" +msgstr "Material de SLA" + +#: src/slic3r/GUI/Plater.cpp:785 +msgid "Printer" +msgstr "Impressora" + +#: src/slic3r/GUI/Plater.cpp:835 src/slic3r/GUI/Plater.cpp:4823 +msgid "Send to printer" +msgstr "Enviar para a impressora" + +#: src/slic3r/GUI/Plater.cpp:838 src/slic3r/GUI/Plater.cpp:2981 +#: src/slic3r/GUI/Plater.cpp:4536 +msgid "Slice now" +msgstr "Fatiar agora" + +#: src/slic3r/GUI/Plater.cpp:978 +msgid "Hold Shift to Slice & Export G-code" +msgstr "Hold Shift to Slice & Export G-code" + +#: src/slic3r/GUI/Plater.cpp:1083 +#, c-format +msgid "%d (%d shells)" +msgstr "%d (%d paredes)" + +#: src/slic3r/GUI/Plater.cpp:1088 +#, c-format +msgid "Auto-repaired (%d errors)" +msgstr "Auto reparando (%d erros):" + +#: src/slic3r/GUI/Plater.cpp:1091 +#, c-format +msgid "" +"%d degenerate facets, %d edges fixed, %d facets removed, %d facets added, %d " +"facets reversed, %d backwards edges" +msgstr "" +"%d degenerate facets, %d edges fixed, %d facets removed, %d facets added, %d " +"facets reversed, %d backwards edges" + +#: src/slic3r/GUI/Plater.cpp:1101 +msgid "Yes" +msgstr "Sim" + +#: src/slic3r/GUI/Plater.cpp:1124 +msgid "Used Material (ml)" +msgstr "Material usado (ml)" + +#: src/slic3r/GUI/Plater.cpp:1127 +msgid "object(s)" +msgstr "objeto(s)" + +#: src/slic3r/GUI/Plater.cpp:1127 +msgid "supports and pad" +msgstr "suportes e bloco" + +#: src/slic3r/GUI/Plater.cpp:1152 src/slic3r/GUI/Plater.cpp:1167 +msgid "objects" +msgstr "objetos" + +#: src/slic3r/GUI/Plater.cpp:1152 src/slic3r/GUI/Plater.cpp:1167 +msgid "wipe tower" +msgstr "torre de limpeza" + +#: src/slic3r/GUI/Plater.cpp:1182 +msgid "normal mode" +msgstr "modo normal" + +#: src/slic3r/GUI/Plater.cpp:1186 src/slic3r/GUI/Plater.cpp:1195 +#: src/libslic3r/PrintConfig.cpp:565 +msgid "Color" +msgstr "Cor" + +#: src/slic3r/GUI/Plater.cpp:1191 +msgid "stealth mode" +msgstr "modo silencioso" + +#: src/slic3r/GUI/Plater.cpp:1286 +msgid "Load File" +msgstr "Carregar arquivo" + +#: src/slic3r/GUI/Plater.cpp:1290 +msgid "Load Files" +msgstr "Carregar arquivos" + +#: src/slic3r/GUI/Plater.cpp:1519 +msgid "ERROR: not enough resources to execute a new job." +msgstr "ERRO: não há recursos suficientes para executar um novo trabalho." + +#: src/slic3r/GUI/Plater.cpp:2089 +msgid "New Project" +msgstr "Novo projeto" + +#: src/slic3r/GUI/Plater.cpp:2206 +msgid "Loading" +msgstr "Carregando" + +#: src/slic3r/GUI/Plater.cpp:2216 +#, c-format +msgid "Processing input file %s\n" +msgstr "Processando o arquivo de entrada %s\n" + +#: src/slic3r/GUI/Plater.cpp:2244 +msgid "" +"You can't load SLA project if there is at least one multi-part object on the " +"bed" +msgstr "" +"Não é possível carregar o projeto de SLA se houver pelo menos um objeto de " +"várias partes na mesa" + +#: src/slic3r/GUI/Plater.cpp:2245 src/slic3r/GUI/Tab.cpp:3146 +msgid "Please check your object list before preset changing." +msgstr "Verifique a lista de objetos antes de alterar a predefinição." + +#: src/slic3r/GUI/Plater.cpp:2288 +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" +msgstr "" +"Este arquivo contém vários objetos posicionados em várias alturas. Em vez de " +"considerá-los como múltiplos objetos, devo considerar\n" +"Este arquivo como um único objeto com várias partes?\n" + +#: src/slic3r/GUI/Plater.cpp:2291 src/slic3r/GUI/Plater.cpp:2343 +msgid "Multi-part object detected" +msgstr "Objeto de várias partes detectado" + +#: src/slic3r/GUI/Plater.cpp:2298 +msgid "" +"This file cannot be loaded in a simple mode. Do you want to switch to an " +"advanced mode?\n" +msgstr "" +"Este arquivo não pode ser carregado em um modo simples. Deseja mudar para um " +"modo avançado?\n" + +#: src/slic3r/GUI/Plater.cpp:2299 +msgid "Detected advanced data" +msgstr "Dados avançados detectados" + +#: src/slic3r/GUI/Plater.cpp:2320 +#, 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 "" +"Você não pode adicionar o objeto (s) %s por causa de um ou alguns deles é " +"(são) de várias partes" + +#: src/slic3r/GUI/Plater.cpp:2340 +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" +msgstr "" +"Vários objetos foram carregados para uma impressora de vários materiais.\n" +"Em vez de considerá-los como múltiplos objetos, devo considerar\n" +"esses arquivos para representar um único objeto com várias partes?\n" + +#: src/slic3r/GUI/Plater.cpp:2356 +msgid "Loaded" +msgstr "Carregado" + +#: src/slic3r/GUI/Plater.cpp:2458 +msgid "" +"Your object appears to be too large, so it was automatically scaled down to " +"fit your print bed." +msgstr "" +"Seu objeto parece ser muito grande, por isso foi automaticamente " +"dimensionado para baixo para caber sua mesa de impressão." + +#: src/slic3r/GUI/Plater.cpp:2459 +msgid "Object too large?" +msgstr "Objeto muito grande?" + +#: src/slic3r/GUI/Plater.cpp:2517 +msgid "Export STL file:" +msgstr "Exportar arquivo STL:" + +#: src/slic3r/GUI/Plater.cpp:2524 +msgid "Export AMF file:" +msgstr "Exportar arquivo AMF:" + +#: src/slic3r/GUI/Plater.cpp:2530 +msgid "Save file as:" +msgstr "Salvar arquivo como:" + +#: src/slic3r/GUI/Plater.cpp:2536 +msgid "Export OBJ file:" +msgstr "Exportar arquivo OBJ:" + +#: src/slic3r/GUI/Plater.cpp:2638 +msgid "Delete Object" +msgstr "Excluir objeto" + +#: src/slic3r/GUI/Plater.cpp:2649 +msgid "Reset Project" +msgstr "Redefinir projeto" + +#: src/slic3r/GUI/Plater.cpp:2688 +msgid "Optimize Rotation" +msgstr "Otimize a rotação" + +#: src/slic3r/GUI/Plater.cpp:2734 +msgid "Arranging" +msgstr "Organizar" + +#: src/slic3r/GUI/Plater.cpp:2757 +msgid "Could not arrange model objects! Some geometries may be invalid." +msgstr "" +"Não foi possível organizar objetos de modelo! Algumas geometrias podem ser " +"inválidas." + +#: src/slic3r/GUI/Plater.cpp:2763 +msgid "Arranging canceled." +msgstr "Arranjo cancelado." + +#: src/slic3r/GUI/Plater.cpp:2764 +msgid "Arranging done." +msgstr "Arranjo feito." + +#: src/slic3r/GUI/Plater.cpp:2780 +msgid "Searching for optimal orientation" +msgstr "Procurando orientação ideal" + +#: src/slic3r/GUI/Plater.cpp:2813 +msgid "Orientation search canceled." +msgstr "Pesquisa de orientação cancelada." + +#: src/slic3r/GUI/Plater.cpp:2814 +msgid "Orientation found." +msgstr "Orientação encontrada." + +#: src/slic3r/GUI/Plater.cpp:2830 +msgid "" +"The selected object can't be split because it contains more than one volume/" +"material." +msgstr "" +"O objeto selecionado não pode ser dividido porque contém mais de um volume/" +"material." + +#: src/slic3r/GUI/Plater.cpp:2841 +msgid "Split to Objects" +msgstr "Dividir em objetos" + +#: src/slic3r/GUI/Plater.cpp:2966 +msgid "Invalid data" +msgstr "Dados inválidos" + +#: src/slic3r/GUI/Plater.cpp:2975 +msgid "Ready to slice" +msgstr "Pronto para fatiar" + +#: src/slic3r/GUI/Plater.cpp:3013 src/slic3r/GUI/PrintHostDialogs.cpp:232 +msgid "Cancelling" +msgstr "Cancelar" + +#: src/slic3r/GUI/Plater.cpp:3030 +msgid "Another export job is currently running." +msgstr "Outro trabalho de exportação está em execução no momento." + +#: src/slic3r/GUI/Plater.cpp:3084 src/slic3r/GUI/Plater.cpp:3549 +msgid "Reload from Disk" +msgstr "Recarregar a partir do disco" + +#: src/slic3r/GUI/Plater.cpp:3120 +msgid "Fix Throught NetFabb" +msgstr "Arrumar através do NetFabb" + +#: src/slic3r/GUI/Plater.cpp:3307 +msgid "Export failed" +msgstr "Falha na exportação" + +#: src/slic3r/GUI/Plater.cpp:3312 src/slic3r/GUI/PrintHostDialogs.cpp:233 +msgid "Cancelled" +msgstr "Cancelado" + +#: src/slic3r/GUI/Plater.cpp:3520 src/slic3r/GUI/Plater.cpp:3539 +msgid "Remove the selected object" +msgstr "Remover o objeto selecionado" + +#: src/slic3r/GUI/Plater.cpp:3526 +msgid "Add one more instance of the selected object" +msgstr "Adicionar mais uma instância do objeto selecionado" + +#: src/slic3r/GUI/Plater.cpp:3528 +msgid "Remove one instance of the selected object" +msgstr "Remover uma instância do objeto selecionado" + +#: src/slic3r/GUI/Plater.cpp:3530 +msgid "Set number of instances" +msgstr "Definir o número de instâncias" + +#: src/slic3r/GUI/Plater.cpp:3530 +msgid "Change the number of instances of the selected object" +msgstr "Alterar o número de instâncias do objeto selecionado" + +#: src/slic3r/GUI/Plater.cpp:3549 +msgid "Reload the selected file from Disk" +msgstr "Recarregar o arquivo selecionado a partir do disco" + +#: src/slic3r/GUI/Plater.cpp:3552 +msgid "Export the selected object as STL file" +msgstr "Exportar o objeto selecionado como arquivo STL" + +#: src/slic3r/GUI/Plater.cpp:3577 +msgid "Along X axis" +msgstr "Ao longo do eixo X" + +#: src/slic3r/GUI/Plater.cpp:3577 +msgid "Mirror the selected object along the X axis" +msgstr "Espelhar o objeto selecionado ao longo do eixo X" + +#: src/slic3r/GUI/Plater.cpp:3579 +msgid "Along Y axis" +msgstr "Ao longo do eixo Y" + +#: src/slic3r/GUI/Plater.cpp:3579 +msgid "Mirror the selected object along the Y axis" +msgstr "Espelhar o objeto selecionado ao longo do eixo Y" + +#: src/slic3r/GUI/Plater.cpp:3581 +msgid "Along Z axis" +msgstr "Ao longo do eixo Z" + +#: src/slic3r/GUI/Plater.cpp:3581 +msgid "Mirror the selected object along the Z axis" +msgstr "Espelhar o objeto selecionado ao longo do eixo Z" + +#: src/slic3r/GUI/Plater.cpp:3584 +msgid "Mirror" +msgstr "Espelhar" + +#: src/slic3r/GUI/Plater.cpp:3584 +msgid "Mirror the selected object" +msgstr "Espelhar o objeto selecionado" + +#: src/slic3r/GUI/Plater.cpp:3596 +msgid "To objects" +msgstr "Para objetos" + +#: src/slic3r/GUI/Plater.cpp:3596 src/slic3r/GUI/Plater.cpp:3616 +msgid "Split the selected object into individual objects" +msgstr "Dividir o objeto selecionado em objetos individuais" + +#: src/slic3r/GUI/Plater.cpp:3598 +msgid "To parts" +msgstr "Para peças" + +#: src/slic3r/GUI/Plater.cpp:3598 src/slic3r/GUI/Plater.cpp:3630 +msgid "Split the selected object into individual sub-parts" +msgstr "Dividir o objeto selecionado em subpartes individuais" + +#: src/slic3r/GUI/Plater.cpp:3601 src/slic3r/GUI/Plater.cpp:3616 +#: src/slic3r/GUI/Plater.cpp:3630 src/libslic3r/PrintConfig.cpp:3281 +msgid "Split" +msgstr "Dividir" + +#: src/slic3r/GUI/Plater.cpp:3601 +msgid "Split the selected object" +msgstr "Dividir o objeto selecionado" + +#: src/slic3r/GUI/Plater.cpp:3622 +msgid "Optimize orientation" +msgstr "Otimize a orientação" + +#: src/slic3r/GUI/Plater.cpp:3622 +msgid "Optimize the rotation of the object for better print results." +msgstr "" +"Otimize a rotação do objeto para obter melhores resultados de impressão." + +#: src/slic3r/GUI/Plater.cpp:3662 +msgid "3D editor view" +msgstr "vista do editor 3D" + +#: src/slic3r/GUI/Plater.cpp:3670 src/slic3r/GUI/Tab.cpp:2590 +msgid "Preview" +msgstr "Visualização" + +#: src/slic3r/GUI/Plater.cpp:3907 +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 "" +"a impressora %1% estava ativa no momento em que a captura de desfazer/" +"refazer de destino foi tirado. Mudar para %1% impressora requer recarga de " +"%1% predefinições." + +#: src/slic3r/GUI/Plater.cpp:4081 +msgid "Load Project" +msgstr "Carregar projeto" + +#: src/slic3r/GUI/Plater.cpp:4109 +msgid "Import Object" +msgstr "Importar objeto" + +#: src/slic3r/GUI/Plater.cpp:4113 +msgid "Import Objects" +msgstr "Importar objetos" + +#: src/slic3r/GUI/Plater.cpp:4172 +msgid "All objects will be removed, continue ?" +msgstr "Todos os objetos serão removidos, continuar?" + +#: src/slic3r/GUI/Plater.cpp:4180 +msgid "Delete Selected Objects" +msgstr "Excluir objetos selecionados" + +#: src/slic3r/GUI/Plater.cpp:4188 +msgid "Increase Instances" +msgstr "Aumentar instâncias" + +#: src/slic3r/GUI/Plater.cpp:4224 +msgid "Decrease Instances" +msgstr "Diminuir instâncias" + +#: src/slic3r/GUI/Plater.cpp:4260 +#, c-format +msgid "Set numbers of copies to %d" +msgstr "Definir números de cópias para %d" + +#: src/slic3r/GUI/Plater.cpp:4290 +msgid "Cut by Plane" +msgstr "Cortado por plano" + +#: src/slic3r/GUI/Plater.cpp:4322 +msgid "Save G-code file as:" +msgstr "Salve o arquivo G-code como:" + +#: src/slic3r/GUI/Plater.cpp:4322 +msgid "Save SL1 file as:" +msgstr "Salvar SL1 arquivo como:" + +#: src/slic3r/GUI/Plater.cpp:4434 +#, c-format +msgid "STL file exported to %s" +msgstr "Arquivo STL exportado para %s" + +#: src/slic3r/GUI/Plater.cpp:4450 +#, c-format +msgid "AMF file exported to %s" +msgstr "Arquivo AMF exportado para %s" + +#: src/slic3r/GUI/Plater.cpp:4453 +#, c-format +msgid "Error exporting AMF file %s" +msgstr "Erro ao exportar arquivo AMF %s" + +#: src/slic3r/GUI/Plater.cpp:4479 +#, c-format +msgid "3MF file exported to %s" +msgstr "Arquivo 3MF exportado para %s" + +#: src/slic3r/GUI/Plater.cpp:4484 +#, c-format +msgid "Error exporting 3MF file %s" +msgstr "Erro ao exportar arquivo 3MF %s" + +#: src/slic3r/GUI/Plater.cpp:4822 +msgid "Export" +msgstr "Exportar" + +#: src/slic3r/GUI/Plater.cpp:4823 +msgid "Send G-code" +msgstr "Enviar G-code" + +#: src/slic3r/GUI/Plater.cpp:4907 +msgid "Paste From Clipboard" +msgstr "Colar da área de transferência" + +#: src/slic3r/GUI/Preferences.cpp:22 src/slic3r/GUI/Tab.cpp:2001 +#: src/slic3r/GUI/Tab.cpp:2242 +msgid "General" +msgstr "Geral" + +#: src/slic3r/GUI/Preferences.cpp:44 +msgid "Remember output directory" +msgstr "Lembrar diretório de saída" + +#: 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 "" +"Se isso estiver habilitado, Slic3r solicitará o último diretório de saída em " +"vez de um contendo os arquivos de entrada." + +#: src/slic3r/GUI/Preferences.cpp:52 +msgid "Auto-center parts" +msgstr "Centrar automaticamente as partes" + +#: src/slic3r/GUI/Preferences.cpp:54 +msgid "" +"If this is enabled, Slic3r will auto-center objects around the print bed " +"center." +msgstr "" +"Se isso estiver habilitado, o Slic3r irá centralizar objetos automaticamente " +"ao redor do centro de mesa de impressão." + +#: src/slic3r/GUI/Preferences.cpp:60 +msgid "Background processing" +msgstr "Processamento em segundo plano" + +#: 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 "" +"Se isso estiver ativado, o Slic3r irá pré-processar objetos assim que eles " +"forem carregados para economizar tempo ao exportar o 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 "" +"Se habilitado, PrusaSlicer irá verificar as novas versões de si mesmo on-" +"line. Quando uma nova versão se torna disponível, uma notificação é exibida " +"na próxima inicialização do aplicativo (nunca durante o uso do programa). " +"Este é apenas um mecanismos de notificação, nenhuma instalação automática é " +"feita." + +#: 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 "" +"Se ativada, o Slic3r baixa atualizações de predefinições de sistema " +"incorporadas em segundo plano. Essas atualizações são baixadas em um local " +"temporário separado. Quando uma nova versão predefinida se torna disponível, " +"ela é oferecida na inicialização do aplicativo." + +#: src/slic3r/GUI/Preferences.cpp:84 +msgid "Suppress \" - default - \" presets" +msgstr "Suprimir predefinições \"-padrão-\"" + +#: 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 "" +"Suprimir predefinições \"-padrão-\" em impressão/filamento/impressora, uma " +"vez que existam outras predefinições válidas disponíveis." + +#: src/slic3r/GUI/Preferences.cpp:92 +msgid "Show incompatible print and filament presets" +msgstr "Mostrar predefinições de impressão e filamento incompatíveis" + +#: 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 "" +"Quando marcada, as predefinições de impressão e filamento são mostradas no " +"editor de predefinições, mesmo que estejam marcadas como incompatíveis com a " +"impressora ativa" + +#: src/slic3r/GUI/Preferences.cpp:101 +msgid "Use Retina resolution for the 3D scene" +msgstr "Usar a resolução retina para a cena 3D" + +#: 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 "" +"Se ativada, a cena 3D será renderizada na resolução retina. Se você estiver " +"enfrentando problemas de desempenho 3D, desabilitar essa opção pode ajudar." + +#: src/slic3r/GUI/Preferences.cpp:110 +msgid "Use perspective camera" +msgstr "Usar a câmera em perspectiva" + +#: src/slic3r/GUI/Preferences.cpp:112 +msgid "" +"If enabled, use perspective camera. If not enabled, use orthographic camera." +msgstr "" +"Se ativada, use a câmera em perspectiva. Se não estiver ativada, use a " +"câmera ortográfica." + +#: src/slic3r/GUI/Preferences.cpp:117 +msgid "Use custom size for toolbar icons" +msgstr "Usar tamanho personalizado para ícones da barra de ferramentas" + +#: src/slic3r/GUI/Preferences.cpp:119 +msgid "If enabled, you can change size of toolbar icons manually." +msgstr "" +"Se ativado, você pode alterar o tamanho dos ícones da barra de ferramentas " +"manualmente." + +#: src/slic3r/GUI/Preferences.cpp:144 +#, c-format +msgid "You need to restart %s to make the changes effective." +msgstr "Você precisa reiniciar %s para tornar as alterações efetivas." + +#: src/slic3r/GUI/Preferences.cpp:192 +msgid "Icon size in a respect to the default size" +msgstr "Tamanho do ícone em relação ao tamanho padrão" + +#: src/slic3r/GUI/Preferences.cpp:207 +msgid "Select toolbar icon size in respect to the default one." +msgstr "" +"Selecione o tamanho do ícone da barra de ferramentas em relação ao padrão." + +#: src/slic3r/GUI/Preset.cpp:212 +msgid "modified" +msgstr "modificado" + +#: src/slic3r/GUI/Preset.cpp:967 src/slic3r/GUI/Preset.cpp:1007 +#: src/slic3r/GUI/Preset.cpp:1072 src/slic3r/GUI/Preset.cpp:1104 +#: src/slic3r/GUI/PresetBundle.cpp:1484 src/slic3r/GUI/PresetBundle.cpp:1559 +msgid "System presets" +msgstr "Predefinições do sistema" + +#: src/slic3r/GUI/Preset.cpp:1011 src/slic3r/GUI/Preset.cpp:1108 +#: src/slic3r/GUI/PresetBundle.cpp:1564 +msgid "User presets" +msgstr "Predefinições do usuário" + +#: src/slic3r/GUI/Preset.cpp:1040 src/slic3r/GUI/Tab.cpp:243 +msgid "Add a new printer" +msgstr "Adicionar uma nova impressora" + +#: src/slic3r/GUI/Preset.cpp:1312 +msgid "filament" +msgstr "filamento" + +#: src/slic3r/GUI/Preset.cpp:1313 +msgid "SLA print" +msgstr "Impressão de SLA" + +#: 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 "" +"Se o tempo estimado da camada estiver abaixo de ~%1%s, o ventoinha será " +"executado em %2%%% e a velocidade de impressão será reduzida para que não " +"menos de %3%s sejam gastos nessa camada (no entanto, a velocidade nunca será " +"reduzida abaixo de %4% mm/s)." + +#: src/slic3r/GUI/PresetHints.cpp:35 +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" +"Se o tempo estimado da camada for maior, mas ainda abaixo de ~%1%s, o " +"ventoinha será executado em uma velocidade proporcionalmente decrescente " +"entre %2%%% e %3%%%." + +#: src/slic3r/GUI/PresetHints.cpp:39 +msgid "" +"\n" +"During the other layers, fan" +msgstr "" +"\n" +"Durante as outras camadas, o ventoinha" + +#: src/slic3r/GUI/PresetHints.cpp:41 +msgid "Fan" +msgstr "Ventoinha" + +#: src/slic3r/GUI/PresetHints.cpp:47 +msgid "will always run at %1%%%" +msgstr "será sempre executado em %1%%%" + +#: src/slic3r/GUI/PresetHints.cpp:50 +msgid "except for the first %1% layers." +msgstr "exceto para as primeiras camadas %1%." + +#: src/slic3r/GUI/PresetHints.cpp:52 +msgid "except for the first layer." +msgstr "exceto para a primeira camada." + +#: src/slic3r/GUI/PresetHints.cpp:54 +msgid "will be turned off." +msgstr "será desligado." + +#: src/slic3r/GUI/PresetHints.cpp:155 +msgid "external perimeters" +msgstr "perímetros externos" + +#: src/slic3r/GUI/PresetHints.cpp:164 +msgid "perimeters" +msgstr "perímetros" + +#: src/slic3r/GUI/PresetHints.cpp:173 +msgid "infill" +msgstr "preenchimento" + +#: src/slic3r/GUI/PresetHints.cpp:183 +msgid "solid infill" +msgstr "preenchimento sólido" + +#: src/slic3r/GUI/PresetHints.cpp:191 +msgid "top solid infill" +msgstr "preenchimento sólido do topo" + +#: src/slic3r/GUI/PresetHints.cpp:202 +msgid "support" +msgstr "suporte" + +#: src/slic3r/GUI/PresetHints.cpp:212 +msgid "support interface" +msgstr "interface de suporte" + +#: src/slic3r/GUI/PresetHints.cpp:218 +msgid "First layer volumetric" +msgstr "Primeira camada volumétrica" + +#: src/slic3r/GUI/PresetHints.cpp:218 +msgid "Bridging volumetric" +msgstr "Ponteamento volumétrico" + +#: src/slic3r/GUI/PresetHints.cpp:218 +msgid "Volumetric" +msgstr "Volumétrica" + +#: src/slic3r/GUI/PresetHints.cpp:219 +msgid "flow rate is maximized" +msgstr "a taxa de fluxo é maximizada" + +#: src/slic3r/GUI/PresetHints.cpp:222 +msgid "by the print profile maximum" +msgstr "pelo perfil de impressão máximo" + +#: src/slic3r/GUI/PresetHints.cpp:223 +msgid "when printing" +msgstr "ao imprimir" + +#: src/slic3r/GUI/PresetHints.cpp:224 +msgid "with a volumetric rate" +msgstr "com uma taxa volumétrica" + +#: 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 na velocidade do filamento %3.2f mm/s." + +#: src/slic3r/GUI/PresetHints.cpp:246 +msgid "" +"Recommended object thin wall thickness: Not available due to invalid layer " +"height." +msgstr "" +"Espessura de parede fina do objeto recomendado: não disponível devido à " +"altura da camada inválida." + +#: src/slic3r/GUI/PresetHints.cpp:262 +#, c-format +msgid "Recommended object thin wall thickness for layer height %.2f and" +msgstr "" +"Espessura de parede fina do objeto recomendado para a altura da camada %.2f e" + +#: src/slic3r/GUI/PresetHints.cpp:268 +#, c-format +msgid "%d lines: %.2f mm" +msgstr "%d linhas: %.2f mm" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:33 +msgid "Send G-Code to printer host" +msgstr "Enviar G-code para o host da impressora" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:33 +msgid "Upload to Printer Host with the following filename:" +msgstr "Carregue para o host da impressora com o seguinte nome de arquivo:" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:35 +msgid "Start printing after upload" +msgstr "Iniciar a impressão após o envio" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:42 +msgid "Use forward slashes ( / ) as a directory separator if needed." +msgstr "Use barras (/) como um separador de diretório, se necessário." + +#: src/slic3r/GUI/PrintHostDialogs.cpp:149 +msgid "ID" +msgstr "ID" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:150 +msgid "Progress" +msgstr "Progresso" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:151 +msgid "Status" +msgstr "Status" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:152 +msgid "Host" +msgstr "Servidor" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:153 +msgid "Filename" +msgstr "Nome do arquivo" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:154 +msgid "Error Message" +msgstr "Mensagem de Erro" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:157 +msgid "Cancel selected" +msgstr "Cancelar selecionado" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:159 +msgid "Show error message" +msgstr "Exibir mensagem de erro" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:198 +#: src/slic3r/GUI/PrintHostDialogs.cpp:229 +msgid "Enqueued" +msgstr "Enfileirado" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:230 +msgid "Uploading" +msgstr "Enviando" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:234 +msgid "Completed" +msgstr "Concluído" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:272 +msgid "Error uploading to print host:" +msgstr "Erro ao carregar para o host de impressão:" + +#: src/slic3r/GUI/RammingChart.cpp:23 +msgid "NO RAMMING AT ALL" +msgstr "Não usar Ramming" + +#: src/slic3r/GUI/RammingChart.cpp:76 +msgid "Time" +msgstr "Tempo" + +#: 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:2426 src/libslic3r/PrintConfig.cpp:2434 +#: src/libslic3r/PrintConfig.cpp:2441 src/libslic3r/PrintConfig.cpp:2449 +#: src/libslic3r/PrintConfig.cpp:2457 +msgid "s" +msgstr "s" + +#: src/slic3r/GUI/RammingChart.cpp:81 +msgid "Volumetric speed" +msgstr "Velocidade volumétrica" + +#: src/slic3r/GUI/RammingChart.cpp:81 src/libslic3r/PrintConfig.cpp:584 +#: src/libslic3r/PrintConfig.cpp:1234 +msgid "mm³/s" +msgstr "mm ³/s" + +#: src/slic3r/GUI/Selection.cpp:146 +msgid "Selection-Add" +msgstr "Seleção-Adicionar" + +#: src/slic3r/GUI/Selection.cpp:187 +msgid "Selection-Remove" +msgstr "Seleção-remover" + +#: src/slic3r/GUI/Selection.cpp:219 +msgid "Selection-Add Object" +msgstr "Seleção-Adicionar objeto" + +#: src/slic3r/GUI/Selection.cpp:238 +msgid "Selection-Remove Object" +msgstr "Seleção-remover objeto" + +#: src/slic3r/GUI/Selection.cpp:256 +msgid "Selection-Add Instance" +msgstr "Instância de seleção-Adicionar" + +#: src/slic3r/GUI/Selection.cpp:275 +msgid "Selection-Remove Instance" +msgstr "Seleção-remover instância" + +#: src/slic3r/GUI/Selection.cpp:376 +msgid "Selection-Add All" +msgstr "Seleção-adicionar todos" + +#: src/slic3r/GUI/Selection.cpp:402 +msgid "Selection-Remove All" +msgstr "Seleção-remover todos" + +#: src/slic3r/GUI/Selection.cpp:939 +msgid "Scale To Fit" +msgstr "Dimensionar para caber" + +#: src/slic3r/GUI/Selection.cpp:1474 +msgid "Set Printable Instance" +msgstr "Definir instância imprimível" + +#: src/slic3r/GUI/Selection.cpp:1474 +msgid "Set Unprintable Instance" +msgstr "Definir instância não imprimível" + +#: src/slic3r/GUI/SysInfoDialog.cpp:78 +msgid "System Information" +msgstr "Informações do sistema" + +#: src/slic3r/GUI/SysInfoDialog.cpp:154 +msgid "Copy to Clipboard" +msgstr "Copiar para a Área de Transferência" + +#: src/slic3r/GUI/Tab.cpp:52 src/libslic3r/PrintConfig.cpp:239 +msgid "Compatible printers" +msgstr "Impressoras compatíveis" + +#: src/slic3r/GUI/Tab.cpp:53 +msgid "Select the printers this profile is compatible with." +msgstr "Selecione as impressoras com as quais este perfil é compatível." + +#: src/slic3r/GUI/Tab.cpp:58 src/libslic3r/PrintConfig.cpp:254 +msgid "Compatible print profiles" +msgstr "Perfis de impressão compatíveis" + +#: src/slic3r/GUI/Tab.cpp:59 +msgid "Select the print profiles this profile is compatible with." +msgstr "" +"Selecione os perfis de impressão com os quais este perfil é compatível." + +#. TRN "Save current Settings" +#: src/slic3r/GUI/Tab.cpp:135 +#, c-format +msgid "Save current %s" +msgstr "Salvar %s atual" + +#: src/slic3r/GUI/Tab.cpp:136 +msgid "Delete this preset" +msgstr "Exclua esta predefinição" + +#: src/slic3r/GUI/Tab.cpp:141 +msgid "" +"Hover the cursor over buttons to find more information \n" +"or click this button." +msgstr "" +"Passe o cursor sobre os botões para encontrar mais informações \n" +"ou clique neste botão." + +#: src/slic3r/GUI/Tab.cpp:943 +msgid "This is a default preset." +msgstr "Esta é uma predefinição padrão." + +#: src/slic3r/GUI/Tab.cpp:945 +msgid "This is a system preset." +msgstr "Esta é uma predefinição do sistema." + +#: src/slic3r/GUI/Tab.cpp:947 +msgid "Current preset is inherited from the default preset." +msgstr "Predefinição atual é herdada da predefinição padrão." + +#: src/slic3r/GUI/Tab.cpp:950 +#, c-format +msgid "" +"Current preset is inherited from:\n" +"\t%s" +msgstr "" +"Predefinição atual é herdada de:\n" +"\t%s" + +#: src/slic3r/GUI/Tab.cpp:954 +msgid "It can't be deleted or modified." +msgstr "Ele não pode ser excluído ou modificado." + +#: src/slic3r/GUI/Tab.cpp:955 +msgid "" +"Any modifications should be saved as a new preset inherited from this one." +msgstr "" +"Todas as modificações devem ser salvas como uma nova predefinição herdada de " +"uma presente." + +#: src/slic3r/GUI/Tab.cpp:956 +msgid "To do that please specify a new name for the preset." +msgstr "Para fazer isso, especifique um novo nome para a predefinição." + +#: src/slic3r/GUI/Tab.cpp:960 +msgid "Additional information:" +msgstr "Informações adicionais:" + +#: src/slic3r/GUI/Tab.cpp:966 +msgid "printer model" +msgstr "modelo de impressora" + +#: src/slic3r/GUI/Tab.cpp:974 +msgid "default print profile" +msgstr "perfil de impressão padrão" + +#: src/slic3r/GUI/Tab.cpp:977 +msgid "default filament profile" +msgstr "perfil de filamento padrão" + +#: src/slic3r/GUI/Tab.cpp:991 +msgid "default SLA material profile" +msgstr "perfil de material SLA padrão" + +#: src/slic3r/GUI/Tab.cpp:995 +msgid "default SLA print profile" +msgstr "perfil de impressão padrão do SLA" + +#: src/slic3r/GUI/Tab.cpp:1032 src/slic3r/GUI/Tab.cpp:3731 +msgid "Layers and perimeters" +msgstr "Camadas e perímetros" + +#: src/slic3r/GUI/Tab.cpp:1037 +msgid "Vertical shells" +msgstr "Paredes verticais" + +#: src/slic3r/GUI/Tab.cpp:1048 +msgid "Horizontal shells" +msgstr "Paredes horizontais" + +#: src/slic3r/GUI/Tab.cpp:1049 src/libslic3r/PrintConfig.cpp:1759 +msgid "Solid layers" +msgstr "Camadas sólidas" + +#: src/slic3r/GUI/Tab.cpp:1054 +msgid "Quality (slower slicing)" +msgstr "Qualidade (fatiamento mais lento)" + +#: src/slic3r/GUI/Tab.cpp:1072 +msgid "Reducing printing time" +msgstr "Reduzindo o tempo de impressão" + +#: src/slic3r/GUI/Tab.cpp:1084 +msgid "Skirt and brim" +msgstr "Saia e aba" + +#: src/slic3r/GUI/Tab.cpp:1101 +msgid "Raft" +msgstr "Estrado" + +#: src/slic3r/GUI/Tab.cpp:1105 +msgid "Options for support material and raft" +msgstr "Opções para material de suporte e estrado" + +#: src/slic3r/GUI/Tab.cpp:1120 +msgid "Speed for print moves" +msgstr "Velocidade para movimentos de impressão" + +#: src/slic3r/GUI/Tab.cpp:1132 +msgid "Speed for non-print moves" +msgstr "Velocidade para movimentos não impressos" + +#: src/slic3r/GUI/Tab.cpp:1135 +msgid "Modifiers" +msgstr "Modificadores" + +#: src/slic3r/GUI/Tab.cpp:1138 +msgid "Acceleration control (advanced)" +msgstr "Controle de aceleração (avançado)" + +#: src/slic3r/GUI/Tab.cpp:1145 +msgid "Autospeed (advanced)" +msgstr "Velocidade automática (avançado)" + +#: src/slic3r/GUI/Tab.cpp:1153 +msgid "Multiple Extruders" +msgstr "Extrusoras múltiplas" + +#: src/slic3r/GUI/Tab.cpp:1161 +msgid "Ooze prevention" +msgstr "Prevenção de vazão" + +#: src/slic3r/GUI/Tab.cpp:1178 +msgid "Extrusion width" +msgstr "Espessura da extrusão" + +#: src/slic3r/GUI/Tab.cpp:1188 +msgid "Overlap" +msgstr "Cobrir" + +#: src/slic3r/GUI/Tab.cpp:1191 +msgid "Flow" +msgstr "Fluxo" + +#: src/slic3r/GUI/Tab.cpp:1200 +msgid "Other" +msgstr "Outro" + +#: src/slic3r/GUI/Tab.cpp:1203 src/slic3r/GUI/Tab.cpp:3789 +msgid "Output options" +msgstr "Opções de saída" + +#: src/slic3r/GUI/Tab.cpp:1204 +msgid "Sequential printing" +msgstr "Impressão sequencial" + +#: src/slic3r/GUI/Tab.cpp:1206 +msgid "Extruder clearance (mm)" +msgstr "Folga da extrusora (milímetro)" + +#: src/slic3r/GUI/Tab.cpp:1215 src/slic3r/GUI/Tab.cpp:3790 +msgid "Output file" +msgstr "Arquivo de saída" + +#: src/slic3r/GUI/Tab.cpp:1222 src/libslic3r/PrintConfig.cpp:1432 +msgid "Post-processing scripts" +msgstr "Scripts de pós-processamento" + +#: src/slic3r/GUI/Tab.cpp:1228 src/slic3r/GUI/Tab.cpp:1229 +#: src/slic3r/GUI/Tab.cpp:1752 src/slic3r/GUI/Tab.cpp:1753 +#: src/slic3r/GUI/Tab.cpp:2214 src/slic3r/GUI/Tab.cpp:2215 +#: src/slic3r/GUI/Tab.cpp:2328 src/slic3r/GUI/Tab.cpp:2329 +#: src/slic3r/GUI/Tab.cpp:3668 src/slic3r/GUI/Tab.cpp:3669 +msgid "Notes" +msgstr "Notas" + +#: src/slic3r/GUI/Tab.cpp:1235 src/slic3r/GUI/Tab.cpp:1760 +#: src/slic3r/GUI/Tab.cpp:2221 src/slic3r/GUI/Tab.cpp:2335 +#: src/slic3r/GUI/Tab.cpp:3676 src/slic3r/GUI/Tab.cpp:3795 +msgid "Dependencies" +msgstr "Dependências" + +#: src/slic3r/GUI/Tab.cpp:1236 src/slic3r/GUI/Tab.cpp:1761 +#: src/slic3r/GUI/Tab.cpp:2222 src/slic3r/GUI/Tab.cpp:2336 +#: src/slic3r/GUI/Tab.cpp:3677 src/slic3r/GUI/Tab.cpp:3796 +msgid "Profile dependencies" +msgstr "Dependências de perfil" + +#: src/slic3r/GUI/Tab.cpp:1538 src/slic3r/GUI/Tab.cpp:1593 +msgid "Filament Overrides" +msgstr "Sobrescrever config." + +#: src/slic3r/GUI/Tab.cpp:1539 src/slic3r/GUI/Tab.cpp:1598 +#: src/slic3r/GUI/Tab.cpp:2570 +msgid "Retraction" +msgstr "Retração" + +#: src/slic3r/GUI/Tab.cpp:1648 src/libslic3r/PrintConfig.cpp:2030 +msgid "Temperature" +msgstr "Temperatura" + +#: src/slic3r/GUI/Tab.cpp:1654 +msgid "Bed" +msgstr "Mesa" + +#: src/slic3r/GUI/Tab.cpp:1659 +msgid "Cooling" +msgstr "Resfriamento" + +#: src/slic3r/GUI/Tab.cpp:1660 src/libslic3r/PrintConfig.cpp:1335 +#: src/libslic3r/PrintConfig.cpp:2150 +msgid "Enable" +msgstr "Habilitar" + +#: src/slic3r/GUI/Tab.cpp:1671 +msgid "Fan settings" +msgstr "Config. da ventoinha" + +#: src/slic3r/GUI/Tab.cpp:1672 +msgid "Fan speed" +msgstr "Velocidade do ventoinha" + +#: src/slic3r/GUI/Tab.cpp:1680 +msgid "Cooling thresholds" +msgstr "Limiares de resfriamento" + +#: src/slic3r/GUI/Tab.cpp:1686 +msgid "Filament properties" +msgstr "Propriedades de filamento" + +#: src/slic3r/GUI/Tab.cpp:1690 +msgid "Print speed override" +msgstr "Substituição da velocidade de impressão" + +#: src/slic3r/GUI/Tab.cpp:1700 +msgid "Wipe tower parameters" +msgstr "Parâmetros da torre de limpeza" + +#: src/slic3r/GUI/Tab.cpp:1703 +msgid "Toolchange parameters with single extruder MM printers" +msgstr "" +"Parâmetros de mudança de ferramenta com impressoras de multi material com " +"apenas uma extrusora" + +#: src/slic3r/GUI/Tab.cpp:1717 +msgid "Ramming settings" +msgstr "config. de Ramming" + +#: src/slic3r/GUI/Tab.cpp:1739 src/slic3r/GUI/Tab.cpp:2177 +msgid "Custom G-code" +msgstr "G-code customizado" + +#: src/slic3r/GUI/Tab.cpp:1740 src/slic3r/GUI/Tab.cpp:2178 +#: src/libslic3r/PrintConfig.cpp:1785 src/libslic3r/PrintConfig.cpp:1800 +msgid "Start G-code" +msgstr "G-code de início" + +#: src/slic3r/GUI/Tab.cpp:1746 src/slic3r/GUI/Tab.cpp:2184 +#: src/libslic3r/PrintConfig.cpp:369 src/libslic3r/PrintConfig.cpp:379 +msgid "End G-code" +msgstr "G-code de finalização" + +#: src/slic3r/GUI/Tab.cpp:1803 +msgid "Volumetric flow hints not available" +msgstr "Dicas de fluxo volumétrico não disponíveis" + +#: src/slic3r/GUI/Tab.cpp:1889 src/slic3r/GUI/Tab.cpp:2117 +msgid "Test" +msgstr "Teste" + +#: src/slic3r/GUI/Tab.cpp:1899 +msgid "Could not get a valid Printer Host reference" +msgstr "Não foi possível obter uma referência de host de impressora válida" + +#: src/slic3r/GUI/Tab.cpp:1905 src/slic3r/GUI/Tab.cpp:2130 +msgid "Success!" +msgstr "Sucesso!" + +#: src/slic3r/GUI/Tab.cpp:1920 +msgid "" +"HTTPS CA file is optional. It is only needed if you use HTTPS with a self-" +"signed certificate." +msgstr "" +"O arquivo HTTPS CA é opcional. Só é necessário se você usar HTTPS com um " +"certificado auto-assinado." + +#: src/slic3r/GUI/Tab.cpp:1933 +msgid "Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*" +msgstr "" +"Arquivos de certificado (*. CRT, *. pem) | *. CRT; *. pem | Todos os " +"arquivos | *. *" + +#: src/slic3r/GUI/Tab.cpp:1934 +msgid "Open CA certificate file" +msgstr "Abra o arquivo de certificado da CA" + +#: src/slic3r/GUI/Tab.cpp:1962 +#, 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 "" +"Arquivo HTTPS CA:\n" +" \tNeste sistema, %s usa certificados HTTPS do sistema Certificate Store " +"ou keychain.\n" +" \tPara usar um arquivo de CA personalizado, importe seu arquivo de CA " +"para o repositório de certificados/chaveiro." + +#: src/slic3r/GUI/Tab.cpp:2002 src/slic3r/GUI/Tab.cpp:2243 +msgid "Size and coordinates" +msgstr "Tamanho e coordenadas" + +#: src/slic3r/GUI/Tab.cpp:2007 src/slic3r/GUI/Tab.cpp:2248 +#: src/slic3r/GUI/Tab.cpp:3338 +msgid "Set" +msgstr "Definir" + +#: src/slic3r/GUI/Tab.cpp:2039 +msgid "Capabilities" +msgstr "Capacidades" + +#: src/slic3r/GUI/Tab.cpp:2044 +msgid "Number of extruders of the printer." +msgstr "Número de extrusoras da impressora." + +#: src/slic3r/GUI/Tab.cpp:2069 +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 "" +"A extrusora multi material é selecionada, \n" +"e todas as extrusoras devem ter o mesmo diâmetro.\n" +"Você quer mudar o diâmetro para todas as extrusoras ao primeiro valor do " +"diâmetro da ponteira da extrusora?" + +#: src/slic3r/GUI/Tab.cpp:2072 src/slic3r/GUI/Tab.cpp:2540 +#: src/libslic3r/PrintConfig.cpp:1310 +msgid "Nozzle diameter" +msgstr "Diâmetro do bico" + +#: src/slic3r/GUI/Tab.cpp:2102 +msgid "USB/Serial connection" +msgstr "Conexão USB/serial" + +#: src/slic3r/GUI/Tab.cpp:2103 src/libslic3r/PrintConfig.cpp:1640 +msgid "Serial port" +msgstr "Porte Serial" + +#: src/slic3r/GUI/Tab.cpp:2108 +msgid "Rescan serial ports" +msgstr "Portas seriais de Rescan" + +#: src/slic3r/GUI/Tab.cpp:2130 +msgid "Connection to printer works correctly." +msgstr "A ligação à impressora funciona corretamente." + +#: src/slic3r/GUI/Tab.cpp:2133 +msgid "Connection failed." +msgstr "A conexão falhou." + +#: src/slic3r/GUI/Tab.cpp:2146 src/slic3r/GUI/Tab.cpp:2323 +msgid "Print Host upload" +msgstr "Upload do host de impressão" + +#: src/slic3r/GUI/Tab.cpp:2190 src/libslic3r/PrintConfig.cpp:138 +msgid "Before layer change G-code" +msgstr "Antes da mudança de camada G-code" + +#: src/slic3r/GUI/Tab.cpp:2196 src/libslic3r/PrintConfig.cpp:1056 +msgid "After layer change G-code" +msgstr "Após a mudança da camada do G-code" + +#: src/slic3r/GUI/Tab.cpp:2202 src/libslic3r/PrintConfig.cpp:2056 +msgid "Tool change G-code" +msgstr "G-code de troca de ferramenta" + +#: src/slic3r/GUI/Tab.cpp:2208 +msgid "Between objects G-code (for sequential printing)" +msgstr "G-code entre objetos (para impressão sequencial)" + +#: src/slic3r/GUI/Tab.cpp:2280 +msgid "Display" +msgstr "Exibição" + +#: src/slic3r/GUI/Tab.cpp:2295 +msgid "Tilt" +msgstr "Inclinar" + +#: src/slic3r/GUI/Tab.cpp:2296 +msgid "Tilt time" +msgstr "Tempo de inclinação" + +#: src/slic3r/GUI/Tab.cpp:2302 src/slic3r/GUI/Tab.cpp:3650 +msgid "Corrections" +msgstr "Correções" + +#: src/slic3r/GUI/Tab.cpp:2317 src/slic3r/GUI/Tab.cpp:3646 +msgid "Exposure" +msgstr "Exposição" + +#: src/slic3r/GUI/Tab.cpp:2388 src/slic3r/GUI/Tab.cpp:2473 +#: 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 +msgid "Machine limits" +msgstr "Limites da máquina" + +#: src/slic3r/GUI/Tab.cpp:2402 +msgid "Values in this column are for Normal mode" +msgstr "Valores nesta coluna são para o modo normal" + +#: src/slic3r/GUI/Tab.cpp:2403 +msgid "Normal" +msgstr "Normal" + +#: src/slic3r/GUI/Tab.cpp:2408 +msgid "Values in this column are for Stealth mode" +msgstr "Valores nesta coluna são para o modo furtivo" + +#: src/slic3r/GUI/Tab.cpp:2409 +msgid "Stealth" +msgstr "Furtivo" + +#: src/slic3r/GUI/Tab.cpp:2417 +msgid "Maximum feedrates" +msgstr "Velocidade máxima de alimentação" + +#: src/slic3r/GUI/Tab.cpp:2422 +msgid "Maximum accelerations" +msgstr "Acelerações máximas" + +#: src/slic3r/GUI/Tab.cpp:2429 +msgid "Jerk limits" +msgstr "Limites de empurrão" + +#: src/slic3r/GUI/Tab.cpp:2434 +msgid "Minimum feedrates" +msgstr "Velocidades alimentação mínimos" + +#: src/slic3r/GUI/Tab.cpp:2498 src/slic3r/GUI/Tab.cpp:2506 +msgid "Single extruder MM setup" +msgstr "config. de extrusora multi material" + +#: src/slic3r/GUI/Tab.cpp:2507 +msgid "Single extruder multimaterial parameters" +msgstr "Parâmetros para extrusora única multimaterial" + +#: src/slic3r/GUI/Tab.cpp:2520 src/libslic3r/GCode/PreviewData.cpp:461 +#, c-format +msgid "Extruder %d" +msgstr "Extrusora %d" + +#: src/slic3r/GUI/Tab.cpp:2538 +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 "" +"Esta é uma única impressora multimaterial extrusora, diâmetros de todas as " +"extrusoras será definido para o novo valor. Você quer prosseguir?" + +#: src/slic3r/GUI/Tab.cpp:2562 +msgid "Layer height limits" +msgstr "Limites de altura da camada" + +#: src/slic3r/GUI/Tab.cpp:2567 +msgid "Position (for multi-extruder printers)" +msgstr "Posição (para impressoras multiextrusoras)" + +#: src/slic3r/GUI/Tab.cpp:2573 +msgid "Only lift Z" +msgstr "Apenas elevar Z" + +#: src/slic3r/GUI/Tab.cpp:2586 +msgid "" +"Retraction when tool is disabled (advanced settings for multi-extruder " +"setups)" +msgstr "" +"Retração quando a ferramenta está desativada (config. avançadas para " +"instalações multiextrusoras)" + +#: src/slic3r/GUI/Tab.cpp:2594 +msgid "Reset to Filament Color" +msgstr "Restabelecer cor do filamento" + +#: src/slic3r/GUI/Tab.cpp:2775 +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 "" +"A opção limpar não está disponível ao usar o modo de retração de firmware.\n" +"\n" +"Devo desativá-lo, a fim de permitir a retração de firmware?" + +#: src/slic3r/GUI/Tab.cpp:2777 +msgid "Firmware Retraction" +msgstr "Retração do firmware" + +#: src/slic3r/GUI/Tab.cpp:3106 +#, c-format +msgid "Default preset (%s)" +msgstr "Predefinição padrão ( %s)" + +#: src/slic3r/GUI/Tab.cpp:3107 +#, c-format +msgid "Preset (%s)" +msgstr "Predefinição ( %s)" + +#: src/slic3r/GUI/Tab.cpp:3124 +msgid "has the following unsaved changes:" +msgstr "tem as seguintes alterações não salvas:" + +#: src/slic3r/GUI/Tab.cpp:3127 +msgid "is not compatible with printer" +msgstr "não é compatível com a impressora" + +#: src/slic3r/GUI/Tab.cpp:3128 +msgid "is not compatible with print profile" +msgstr "não é compatível com o perfil de impressão" + +#: src/slic3r/GUI/Tab.cpp:3130 +msgid "and it has the following unsaved changes:" +msgstr "e tem as seguintes alterações não salvas:" + +#: src/slic3r/GUI/Tab.cpp:3134 +msgid "Unsaved Changes" +msgstr "Alterações não salvas" + +#: src/slic3r/GUI/Tab.cpp:3225 +msgid "%1% - Copy" +msgstr "%1% - cópia" + +#: src/slic3r/GUI/Tab.cpp:3248 +msgid "The supplied name is empty. It can't be saved." +msgstr "O nome fornecido está vazio. Não pode ser salvo." + +#: src/slic3r/GUI/Tab.cpp:3253 +msgid "Cannot overwrite a system profile." +msgstr "Não é possível substituir um perfil de sistema." + +#: src/slic3r/GUI/Tab.cpp:3257 +msgid "Cannot overwrite an external profile." +msgstr "Não é possível substituir um perfil externo." + +#: src/slic3r/GUI/Tab.cpp:3283 +msgid "remove" +msgstr "remover" + +#: src/slic3r/GUI/Tab.cpp:3283 +msgid "delete" +msgstr "excluir" + +#. TRN remove/delete +#: src/slic3r/GUI/Tab.cpp:3285 +msgid "Are you sure you want to %1% the selected preset?" +msgstr "Tem certeza de que deseja %1% da predefinição selecionada?" + +#. TRN Remove/Delete +#: src/slic3r/GUI/Tab.cpp:3288 +msgid "%1% Preset" +msgstr "%1% Predefinição" + +#: src/slic3r/GUI/Tab.cpp:3414 +msgid "LOCKED LOCK" +msgstr "CADEADO FECHADO" + +#. TRN Description for "LOCKED LOCK" +#: src/slic3r/GUI/Tab.cpp:3416 +msgid "" +"indicates that the settings are the same as the system (or default) values " +"for the current option group" +msgstr "" +"indica que as config. são as mesmas que os valores do sistema (ou padrão) " +"para o grupo de opções atual" + +#: src/slic3r/GUI/Tab.cpp:3418 +msgid "UNLOCKED LOCK" +msgstr "CADEADO ABERTO" + +#. TRN Description for "UNLOCKED LOCK" +#: src/slic3r/GUI/Tab.cpp:3420 +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 "" +"indica que algumas config. foram alteradas e não são iguais aos valores do " +"sistema (ou padrão) para o grupo de opções atual.\n" +"Clique no ícone DESBLOQUEAR para redefinir todas as config. do grupo de " +"opções atual para os valores do sistema (ou padrão)." + +#: src/slic3r/GUI/Tab.cpp:3425 +msgid "WHITE BULLET" +msgstr "PONTO BRANCO" + +#. TRN Description for "WHITE BULLET" +#: src/slic3r/GUI/Tab.cpp:3427 +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 "" +"para o botão esquerdo: \t indica uma predefinição que não é do sistema (ou " +"não-padrão),\n" +"para o botão direito: \t indica que as config. não foram modificadas." + +#: src/slic3r/GUI/Tab.cpp:3430 +msgid "BACK ARROW" +msgstr "REDEFINIR" + +#. TRN Description for "BACK ARROW" +#: src/slic3r/GUI/Tab.cpp:3432 +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 "" +"indica que as config. foram alteradas e não são iguais à última predefinição " +"salva para o grupo de opções atual.\n" +"Clique no ícone REDEFINIR para redefinir todas as config. do grupo de opções " +"atual para a última predefinição salva." + +#: src/slic3r/GUI/Tab.cpp:3442 +msgid "" +"LOCKED LOCK icon indicates that the settings are the same as the system (or " +"default) values for the current option group" +msgstr "" +"O ícone CADEADO FECHADO indica que as config. são as mesmas que os valores " +"do sistema (ou padrão) para o grupo de opções atual" + +#: src/slic3r/GUI/Tab.cpp:3444 +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 "" +"O ícone de CADEADO ABERTO indica que algumas config. foram alteradas e não " +"são iguais aos valores do sistema (ou padrão) para o grupo de opções atual.\n" +"Clique para redefinir todas as config. para o grupo de opções atual para os " +"valores do sistema (ou padrão)." + +#: src/slic3r/GUI/Tab.cpp:3447 +msgid "WHITE BULLET icon indicates a non system (or non default) preset." +msgstr "" +"O ícone PONTO BRANCO indica uma predefinição que não é do sistema (ou não " +"predefinida)." + +#: src/slic3r/GUI/Tab.cpp:3450 +msgid "" +"WHITE BULLET icon indicates that the settings are the same as in the last " +"saved preset for the current option group." +msgstr "" +"O ícone PONTO BRANCO indica que as config. são as mesmas da última " +"predefinição salva para o grupo de opções atual." + +#: src/slic3r/GUI/Tab.cpp:3452 +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 "" +"O ícone de REDEFINIR indica que as config. foram alteradas e não são iguais " +"à última predefinição salva para o grupo de opções atual.\n" +"Clique para redefinir todas as config. do grupo de opções atual para a " +"última predefinição salva." + +#: src/slic3r/GUI/Tab.cpp:3458 +msgid "" +"LOCKED LOCK icon indicates that the value is the same as the system (or " +"default) value." +msgstr "" +"O ícone CADEADO FECHADO indica que o valor é o mesmo que o valor do sistema " +"(ou padrão)." + +#: src/slic3r/GUI/Tab.cpp:3459 +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 "" +"O ícone de CADEADO ABERTO indica que o valor foi alterado e não é igual ao " +"valor do sistema (ou padrão).\n" +"Clique para redefinir o valor atual para o valor do sistema (ou padrão)." + +#: src/slic3r/GUI/Tab.cpp:3465 +msgid "" +"WHITE BULLET icon indicates that the value is the same as in the last saved " +"preset." +msgstr "" +"O ícone PONTO BRANCO indica que o valor é o mesmo da última predefinição " +"guardada." + +#: src/slic3r/GUI/Tab.cpp:3466 +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 "" +"O ícone de REDEFINIR indica que o valor foi alterado e não é igual à última " +"predefinição salva.\n" +"Clique para redefinir o valor atual para a última predefinição salva." + +#. TRN Preset +#: src/slic3r/GUI/Tab.cpp:3579 +#, c-format +msgid "Save %s as:" +msgstr "Salvar %s como:" + +#: src/slic3r/GUI/Tab.cpp:3623 +msgid "the following suffix is not allowed:" +msgstr "o sufixo seguinte não é permitido:" + +#: src/slic3r/GUI/Tab.cpp:3627 +msgid "The supplied name is not available." +msgstr "O nome fornecido não está disponível." + +#: src/slic3r/GUI/Tab.cpp:3640 +msgid "Material" +msgstr "Material" + +#: src/slic3r/GUI/Tab.cpp:3642 src/slic3r/GUI/Tab.cpp:3733 +#: src/slic3r/GUI/wxExtensions.cpp:482 +msgid "Layers" +msgstr "Camadas" + +#: src/slic3r/GUI/Tab.cpp:3741 +msgid "Support head" +msgstr "Cabeça de suporte" + +#: src/slic3r/GUI/Tab.cpp:3746 +msgid "Support pillar" +msgstr "Pilar de suporte" + +#: src/slic3r/GUI/Tab.cpp:3760 +msgid "Connection of the support sticks and junctions" +msgstr "Conexão das varas de suporte e junções" + +#: src/slic3r/GUI/Tab.cpp:3765 +msgid "Automatic generation" +msgstr "Geração Automática" + +#: src/slic3r/GUI/Tab.hpp:328 src/slic3r/GUI/Tab.hpp:428 +msgid "Print Settings" +msgstr "Config. de impressão" + +#: src/slic3r/GUI/Tab.hpp:353 +msgid "Filament Settings" +msgstr "Config. de filamento" + +#: src/slic3r/GUI/Tab.hpp:389 +msgid "Printer Settings" +msgstr "Config. da impressora" + +#: src/slic3r/GUI/Tab.hpp:413 +msgid "Material Settings" +msgstr "Config. de material" + +#: src/slic3r/GUI/Tab.hpp:440 +msgid "Save preset" +msgstr "Salvar predefinição" + +#: src/slic3r/GUI/UpdateDialogs.cpp:38 +msgid "Update available" +msgstr "Atualização disponível" + +#: src/slic3r/GUI/UpdateDialogs.cpp:38 +#, c-format +msgid "New version of %s is available" +msgstr "Nova versão do %s está disponível" + +#: src/slic3r/GUI/UpdateDialogs.cpp:45 +msgid "Current version:" +msgstr "Versão atual:" + +#: src/slic3r/GUI/UpdateDialogs.cpp:47 +msgid "New version:" +msgstr "Nova versão:" + +#: src/slic3r/GUI/UpdateDialogs.cpp:55 +msgid "Changelog && Download" +msgstr "Changelog && Download" + +#: src/slic3r/GUI/UpdateDialogs.cpp:62 src/slic3r/GUI/UpdateDialogs.cpp:127 +msgid "Open changelog page" +msgstr "Abra a página do changelog" + +#: src/slic3r/GUI/UpdateDialogs.cpp:67 +msgid "Open download page" +msgstr "Abrir página de download" + +#: src/slic3r/GUI/UpdateDialogs.cpp:73 +msgid "Don't notify about new releases any more" +msgstr "Não notifique mais sobre novas versões" + +#: src/slic3r/GUI/UpdateDialogs.cpp:91 src/slic3r/GUI/UpdateDialogs.cpp:207 +msgid "Configuration update" +msgstr "Atualização de config." + +#: src/slic3r/GUI/UpdateDialogs.cpp:91 +msgid "Configuration update is available" +msgstr "A atualização de config. está disponível" + +#: src/slic3r/GUI/UpdateDialogs.cpp:94 +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 "" +"Gostaria de instalá-lo?\n" +"\n" +"Observe que uma captura da config. completa será criado primeiro. Ele pode " +"então ser restaurado a qualquer momento se houver um problema com a nova " +"versão.\n" +"\n" +"Pacotes de config. atualizados:" + +#: src/slic3r/GUI/UpdateDialogs.cpp:115 +msgid "Comment:" +msgstr "Comentário:" + +#: src/slic3r/GUI/UpdateDialogs.cpp:151 +#, c-format +msgid "%s incompatibility" +msgstr "%s incompatibilidade" + +#: src/slic3r/GUI/UpdateDialogs.cpp:152 +#, c-format +msgid "%s configuration is incompatible" +msgstr "%s config. é incompatível" + +#: src/slic3r/GUI/UpdateDialogs.cpp:157 +#, 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.\n" +msgstr "" +"Esta versão do %s não é compatível com pacotes de config. atualmente " +"instalados.\n" +"Isso provavelmente aconteceu como resultado da execução de um %s mais antigo " +"depois de usar um mais recente.\n" +"\n" +"Você pode sair %s e tente novamente com uma versão mais recente, ou você " +"pode executar novamente a config. inicial. Isso criará um instantâneo de " +"backup da config. existente antes de instalar os arquivos compatíveis com " +"este %s.\n" + +#: src/slic3r/GUI/UpdateDialogs.cpp:166 +#, c-format +msgid "This %s version: %s" +msgstr "Esta versão %s : %s" + +#: src/slic3r/GUI/UpdateDialogs.cpp:171 +msgid "Incompatible bundles:" +msgstr "Pacotes incompatíveis:" + +#: src/slic3r/GUI/UpdateDialogs.cpp:187 +#, c-format +msgid "Exit %s" +msgstr "Saída %s" + +#: src/slic3r/GUI/UpdateDialogs.cpp:190 +msgid "Re-configure" +msgstr "Re-config.urar" + +#: src/slic3r/GUI/UpdateDialogs.cpp:211 +#, 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 agora usa uma estrutura de config. atualizada.\n" +"\n" +"Assim chamado ' Predefinições do sistema ' foram introduzidas, que mantêm as " +"config. padrão internas para várias impressoras. Essas predefinições do " +"sistema não podem ser modificadas, em vez disso, os usuários agora podem " +"criar suas próprias predefinições herdando as config. de uma das " +"predefinições do sistema.\n" +"Uma predefinição herdada pode herdar um valor específico de seu pai ou " +"substituí-lo por um valor personalizado.\n" +"\n" +"Por favor, prossiga com o %s que se segue para config.urar as novas " +"predefinições e para escolher se deseja ativar as atualizações automáticas " +"predefinidas." + +#: src/slic3r/GUI/UpdateDialogs.cpp:227 +msgid "For more information please visit our wiki page:" +msgstr "Para mais informações, visite a nossa página wiki:" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:14 +msgid "Ramming customization" +msgstr "Personalização de Ramming" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:40 +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 "" +"O Ramming denota a extrusão rápida apenas antes que uma mudança da " +"ferramenta em uma única-extrusora a impressora de multifilamentos Sua " +"finalidade é moldar corretamente a extremidade do filamento descarregado " +"assim que não impede a inserção do filamento novo e pode próprio ser " +"reintroduzido mais tarde. Esta fase é importante e os materiais diferentes " +"podem exigir velocidades diferentes da extrusão para começ a boa forma. Por " +"esta razão, as taxas de extrusão durante a batendo são ajustáveis.\n" +"\n" +"Esta é uma config. de nível especialista, ajuste incorreto provavelmente " +"levará a compotas, roda extrusora moagem em filamento etc." + +#: src/slic3r/GUI/WipeTowerDialog.cpp:82 +msgid "Total ramming time" +msgstr "Tempo total de Ramming" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:84 +msgid "Total rammed volume" +msgstr "Volume total de Ramming" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:88 +msgid "Ramming line width" +msgstr "Largura da linha de Ramming" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:90 +msgid "Ramming line spacing" +msgstr "Espaçamento de linha de Ramming" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:141 +msgid "Wipe tower - Purging volume adjustment" +msgstr "Torre de limpeza - Ajuste de volume de purga" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:225 +msgid "" +"Here you can adjust required purging volume (mm³) for any given pair of " +"tools." +msgstr "" +"Aqui você pode ajustar o volume de purga necessário (mm ³) para qualquer par " +"dado de ferramentas." + +#: src/slic3r/GUI/WipeTowerDialog.cpp:226 +msgid "Extruder changed to" +msgstr "Extrusora alterada para" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:234 +msgid "unloaded" +msgstr "descarregado" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:235 +msgid "loaded" +msgstr "carregado" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:240 +msgid "Tool #" +msgstr "Ferramenta #" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:247 +msgid "" +"Total purging volume is calculated by summing two values below, depending on " +"which tools are loaded/unloaded." +msgstr "" +"O volume de purga total é calculado somando-se dois valores abaixo, " +"dependendo de quais ferramentas são carregadas/descarregadas." + +#: src/slic3r/GUI/WipeTowerDialog.cpp:248 +msgid "Volume to purge (mm³) when the filament is being" +msgstr "Volume a purgar (mm ³) quando o filamento está a ser" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:262 +msgid "From" +msgstr "De" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:327 +msgid "" +"Switching to simple settings will discard changes done in the advanced " +"mode!\n" +"\n" +"Do you want to proceed?" +msgstr "" +"Mudar para config. simples irá descartar as alterações feitas no modo " +"avançado!\n" +"\n" +"Você quer prosseguir?" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:339 +msgid "Show simplified settings" +msgstr "Mostrar config. simplificadas" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:339 +msgid "Show advanced settings" +msgstr "Mostrar opções avançadas" + +#: src/slic3r/GUI/wxExtensions.cpp:471 +msgid "Instances" +msgstr "Instâncias" + +#: src/slic3r/GUI/wxExtensions.cpp:475 src/slic3r/GUI/wxExtensions.cpp:619 +#, c-format +msgid "Instance %d" +msgstr "Instância %d" + +#: src/slic3r/GUI/wxExtensions.cpp:509 +msgid "Range" +msgstr "Intervalo" + +#: src/slic3r/GUI/wxExtensions.cpp:2731 +msgid "One layer mode" +msgstr "Modo de uma camada" + +#: src/slic3r/GUI/wxExtensions.cpp:2732 +msgid "Add/Del color change" +msgstr "Add/Excluir mudança de cor" + +#: src/slic3r/GUI/wxExtensions.cpp:2733 +msgid "Discard all color changes" +msgstr "Descartar todas as alterações de cor" + +#: src/slic3r/GUI/wxExtensions.cpp:2993 +#, c-format +msgid "Switch to the %s mode" +msgstr "Mude para o modo %s" + +#: src/slic3r/GUI/wxExtensions.cpp:2994 +#, c-format +msgid "Current mode is %s" +msgstr "O modo atual é %s" + +#: src/slic3r/Utils/Duet.cpp:51 +msgid "Connection to Duet works correctly." +msgstr "A conexão com o Duet funciona corretamente." + +#: src/slic3r/Utils/Duet.cpp:56 +msgid "Could not connect to Duet" +msgstr "Não foi possível conectar-se ao Duet" + +#: src/slic3r/Utils/Duet.cpp:84 src/slic3r/Utils/Duet.cpp:154 +msgid "Unknown error occured" +msgstr "Ocorreu um erro desconhecido" + +#: src/slic3r/Utils/Duet.cpp:148 +msgid "Wrong password" +msgstr "Senha incorreta" + +#: src/slic3r/Utils/Duet.cpp:151 +msgid "Could not get resources to create a new connection" +msgstr "Não foi possível obter recursos para criar uma nova conexão" + +#: src/slic3r/Utils/OctoPrint.cpp:70 +#, c-format +msgid "Mismatched type of print host: %s" +msgstr "Tipo incompatível de host de impressão: %s" + +#: src/slic3r/Utils/OctoPrint.cpp:85 +msgid "Connection to OctoPrint works correctly." +msgstr "A ligação ao OctoPrint funciona correctamente." + +#: src/slic3r/Utils/OctoPrint.cpp:91 +msgid "Could not connect to OctoPrint" +msgstr "Não foi possível conectar-se ao OctoPrint" + +#: src/slic3r/Utils/OctoPrint.cpp:91 +msgid "Note: OctoPrint version at least 1.1.0 is required." +msgstr "Nota: OctoPrint versão pelo menos 1.1.0 é necessária." + +#: src/slic3r/Utils/OctoPrint.cpp:196 +msgid "Connection to Prusa SL1 works correctly." +msgstr "A conexão com o Prusa SL1 funciona corretamente." + +#: src/slic3r/Utils/OctoPrint.cpp:201 +msgid "Could not connect to Prusa SLA" +msgstr "Não foi possível conectar-se a Prusa SLA" + +#: src/slic3r/Utils/PresetUpdater.cpp:614 +#, c-format +msgid "requires min. %s and max. %s" +msgstr "requer min . %s e máx. %s" + +#: src/slic3r/Utils/PresetUpdater.cpp:619 +#, c-format +msgid "requires min. %s" +msgstr "requer min . %s" + +#: src/slic3r/Utils/PresetUpdater.cpp:621 +#, c-format +msgid "requires max. %s" +msgstr "requer Max. %s" + +#: src/slic3r/Utils/FixModelByWin10.cpp:219 +#: src/slic3r/Utils/FixModelByWin10.cpp:359 +msgid "Exporting source model" +msgstr "Exportando o modelo de origem" + +#: src/slic3r/Utils/FixModelByWin10.cpp:235 +msgid "Failed loading the input model." +msgstr "Falha ao carregar o modelo de entrada." + +#: src/slic3r/Utils/FixModelByWin10.cpp:242 +msgid "Repairing model by the Netfabb service" +msgstr "Modelo de reparação pelo serviço Netfabb" + +#: src/slic3r/Utils/FixModelByWin10.cpp:248 +msgid "Mesh repair failed." +msgstr "Falha na reparação de malha." + +#: src/slic3r/Utils/FixModelByWin10.cpp:251 +#: src/slic3r/Utils/FixModelByWin10.cpp:378 +msgid "Loading repaired model" +msgstr "Carregando o modelo reparado" + +#: 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 "Falha ao salvar a malha 3MF no contêiner." + +#: src/slic3r/Utils/FixModelByWin10.cpp:340 +msgid "Model fixing" +msgstr "Fixação do modelo" + +#: src/slic3r/Utils/FixModelByWin10.cpp:341 +msgid "Exporting model..." +msgstr "Exportando o modelo..." + +#: src/slic3r/Utils/FixModelByWin10.cpp:368 +msgid "Export of a temporary 3mf file failed" +msgstr "Falha na exportação de um arquivo 3mf temporário" + +#: src/slic3r/Utils/FixModelByWin10.cpp:383 +msgid "Import of the repaired 3mf file failed" +msgstr "Falha na importação do arquivo 3mf reparado" + +#: src/slic3r/Utils/FixModelByWin10.cpp:385 +msgid "Repaired 3MF file does not contain any object" +msgstr "O arquivo 3MF reparado não contém nenhum objeto" + +#: src/slic3r/Utils/FixModelByWin10.cpp:387 +msgid "Repaired 3MF file contains more than one object" +msgstr "O arquivo 3MF reparado contém mais de um objeto" + +#: src/slic3r/Utils/FixModelByWin10.cpp:389 +msgid "Repaired 3MF file does not contain any volume" +msgstr "O arquivo 3MF reparado não contém nenhum volume" + +#: src/slic3r/Utils/FixModelByWin10.cpp:391 +msgid "Repaired 3MF file contains more than one volume" +msgstr "O arquivo 3MF reparado contém mais de um volume" + +#: src/slic3r/Utils/FixModelByWin10.cpp:400 +msgid "Model repair finished" +msgstr "Reparo do modelo terminado" + +#: src/slic3r/Utils/FixModelByWin10.cpp:406 +msgid "Model repair canceled" +msgstr "Reparo do modelo cancelado" + +#: src/slic3r/Utils/FixModelByWin10.cpp:423 +msgid "Model repaired successfully" +msgstr "Modelo reparado com sucesso" + +#: src/slic3r/Utils/FixModelByWin10.cpp:423 +#: src/slic3r/Utils/FixModelByWin10.cpp:426 +msgid "Model Repair by the Netfabb service" +msgstr "Reparação de modelos pelo serviço Netfabb" + +#: src/slic3r/Utils/FixModelByWin10.cpp:426 +msgid "Model repair failed: \n" +msgstr "Falha no reparo do modelo:\n" + +#: src/libslic3r/Zipper.cpp:32 +msgid "undefined error" +msgstr "erro indefinido" + +#: src/libslic3r/Zipper.cpp:34 +msgid "too many files" +msgstr "muitos arquivos" + +#: src/libslic3r/Zipper.cpp:36 +msgid "file too large" +msgstr "arquivo muito grande" + +#: src/libslic3r/Zipper.cpp:38 +msgid "unsupported method" +msgstr "método não suportado" + +#: src/libslic3r/Zipper.cpp:40 +msgid "unsupported encryption" +msgstr "criptografia sem suporte" + +#: src/libslic3r/Zipper.cpp:42 +msgid "unsupported feature" +msgstr "recurso não suportado" + +#: src/libslic3r/Zipper.cpp:44 +msgid "failed finding central directory" +msgstr "falha ao encontrar o diretório central" + +#: src/libslic3r/Zipper.cpp:46 +msgid "not a ZIP archive" +msgstr "não um arquivo ZIP" + +#: src/libslic3r/Zipper.cpp:48 +msgid "invalid header or archive is corrupted" +msgstr "cabeçalho ou arquivo inválido está corrompido" + +#: src/libslic3r/Zipper.cpp:50 +msgid "unsupported multidisk archive" +msgstr "arquivo Multidisk sem suporte" + +#: src/libslic3r/Zipper.cpp:52 +msgid "decompression failed or archive is corrupted" +msgstr "descompressão falhou ou arquivo está corrompido" + +#: src/libslic3r/Zipper.cpp:54 +msgid "compression failed" +msgstr "falha na compactação" + +#: src/libslic3r/Zipper.cpp:56 +msgid "unexpected decompressed size" +msgstr "tamanho descomprimido inesperado" + +#: src/libslic3r/Zipper.cpp:58 +msgid "CRC-32 check failed" +msgstr "Verificação CRC-32 falhou" + +#: src/libslic3r/Zipper.cpp:60 +msgid "unsupported central directory size" +msgstr "tamanho do diretório central não suportado" + +#: src/libslic3r/Zipper.cpp:62 +msgid "allocation failed" +msgstr "alocação falhou" + +#: src/libslic3r/Zipper.cpp:64 +msgid "file open failed" +msgstr "falha na abertura do arquivo" + +#: src/libslic3r/Zipper.cpp:66 +msgid "file create failed" +msgstr "falha na criação do arquivo" + +#: src/libslic3r/Zipper.cpp:68 +msgid "file write failed" +msgstr "falha na gravação do arquivo" + +#: src/libslic3r/Zipper.cpp:70 +msgid "file read failed" +msgstr "falha na leitura do arquivo" + +#: src/libslic3r/Zipper.cpp:72 +msgid "file close failed" +msgstr "falha ao fechar o arquivo" + +#: src/libslic3r/Zipper.cpp:74 +msgid "file seek failed" +msgstr "falha na busca de arquivo" + +#: src/libslic3r/Zipper.cpp:76 +msgid "file stat failed" +msgstr "falha no status do arquivo" + +#: src/libslic3r/Zipper.cpp:78 +msgid "invalid parameter" +msgstr "parâmetro inválido" + +#: src/libslic3r/Zipper.cpp:80 +msgid "invalid filename" +msgstr "nome de arquivo inválido" + +#: src/libslic3r/Zipper.cpp:82 +msgid "buffer too small" +msgstr "buffer muito pequeno" + +#: src/libslic3r/Zipper.cpp:84 +msgid "internal error" +msgstr "erro interno" + +#: src/libslic3r/Zipper.cpp:86 +msgid "file not found" +msgstr "arquivo não encontrado" + +#: src/libslic3r/Zipper.cpp:88 +msgid "archive is too large" +msgstr "arquivo é muito grande" + +#: src/libslic3r/Zipper.cpp:90 +msgid "validation failed" +msgstr "falha na validação" + +#: src/libslic3r/Zipper.cpp:92 +msgid "write calledback failed" +msgstr "write calledback falhou" + +#: src/libslic3r/Zipper.cpp:102 +msgid "Error with zip archive" +msgstr "Erro com arquivo zip" + +#: src/libslic3r/Print.cpp:1112 +msgid "All objects are outside of the print volume." +msgstr "Todos os objetos estão fora do volume de impressão." + +#: src/libslic3r/Print.cpp:1139 +msgid "Some objects are too close; your extruder will collide with them." +msgstr "Alguns objetos são muito próximos; sua extrusora irá colidir com eles." + +#: src/libslic3r/Print.cpp:1154 +msgid "" +"Some objects are too tall and cannot be printed without extruder collisions." +msgstr "" +"Alguns objetos são muito altos e não podem ser impressos sem colisões de " +"extrusoras." + +#: src/libslic3r/Print.cpp:1164 +msgid "The Spiral Vase option can only be used when printing a single object." +msgstr "A opção vaso espiral só pode ser usada ao imprimir um único objeto." + +#: src/libslic3r/Print.cpp:1171 +msgid "" +"The Spiral Vase option can only be used when printing single material " +"objects." +msgstr "" +"A opção vaso espiral só pode ser usada ao imprimir objetos de material único." + +#: src/libslic3r/Print.cpp:1184 +msgid "" +"The wipe tower is only supported if all extruders have the same nozzle " +"diameter and use filaments of the same diameter." +msgstr "" +"A torre de limpeza só é suportada se todas as extrusoras tiverem o mesmo " +"diâmetro da ponteira e usarem filamentos do mesmo diâmetro." + +#: src/libslic3r/Print.cpp:1189 +msgid "" +"The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter " +"and Repetier G-code flavors." +msgstr "" +"A Wipe Tower é atualmente suportada apenas para os firmwares Marlin, RepRap/" +"Sprinter e Repetier G-code." + +#: src/libslic3r/Print.cpp:1191 +msgid "" +"The Wipe Tower is currently only supported with the relative extruder " +"addressing (use_relative_e_distances=1)." +msgstr "" +"A torre da limpeza é suportada atualmente somente com o endereçamento " +"relativo da extrusora (use_relative_e_distances = 1)." + +#: src/libslic3r/Print.cpp:1193 +msgid "Ooze prevention is currently not supported with the wipe tower enabled." +msgstr "" +"A prevenção de escorrimento não é suportada atualmente com a torre da " +"limpeza permitida." + +#: src/libslic3r/Print.cpp:1214 +msgid "" +"The Wipe Tower is only supported for multiple objects if they have equal " +"layer heights" +msgstr "" +"A torre de limpeza só é suportada para vários objetos se eles tiverem " +"alturas de camada iguais" + +#: src/libslic3r/Print.cpp:1216 +msgid "" +"The Wipe Tower is only supported for multiple objects if they are printed " +"over an equal number of raft layers" +msgstr "" +"A torre de limpeza só é suportada para vários objetos se elas forem " +"impressas em um número igual de camadas de estrado" + +#: src/libslic3r/Print.cpp:1218 +msgid "" +"The Wipe Tower is only supported for multiple objects if they are printed " +"with the same support_material_contact_distance" +msgstr "" +"A torre de limpeza só é suportado para vários objetos se eles são impressos " +"com a mesma distância de contato do suporte" + +#: src/libslic3r/Print.cpp:1220 +msgid "" +"The Wipe Tower is only supported for multiple objects if they are sliced " +"equally." +msgstr "" +"A torre de limpeza só é suportada para vários objetos se eles são fatiados " +"igualmente." + +#: src/libslic3r/Print.cpp:1248 +msgid "" +"The Wipe tower is only supported if all objects have the same layer height " +"profile" +msgstr "" +"A torre de limpeza só é suportada se todos os objetos tiverem o mesmo perfil " +"de altura da camada" + +#: src/libslic3r/Print.cpp:1258 +msgid "The supplied settings will cause an empty print." +msgstr "As config. fornecidas causarão uma impressão vazia." + +#: src/libslic3r/Print.cpp:1275 +msgid "" +"One or more object were assigned an extruder that the printer does not have." +msgstr "" +"Um ou mais objetos foram atribuídos a uma extrusora que a impressora não tem." + +#: src/libslic3r/Print.cpp:1284 +msgid "%1%=%2% mm is too low to be printable at a layer height %3% mm" +msgstr "" +"%1% = %2% mm é muito baixo para ser impresso a uma altura de camada %3% mm" + +#: src/libslic3r/Print.cpp:1287 +msgid "Excessive %1%=%2% mm to be printable with a nozzle diameter %3% mm" +msgstr "" +"Excesso %1%=%2% milímetro a ser imprimível com um diâmetro da ponteira %3% " +"milímetro" + +#: src/libslic3r/Print.cpp:1298 +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 "" +"Impressão com múltiplas extrusoras de diferentes diâmetros de bicos. Se a " +"sustentação deve ser imprimida com a extrusora atual " +"(support_material_extruder = = 0 ou support_material_interface_extruder = = " +"0), todos as ponteiras têm que ser do mesmo diâmetro." + +#: src/libslic3r/Print.cpp:1306 +msgid "" +"For the Wipe Tower to work with the soluble supports, the support layers " +"need to be synchronized with the object layers." +msgstr "" +"Para que a torre de limpeza funcione com os suportes solúveis, as camadas de " +"suporte precisam ser sincronizadas com as camadas de objeto." + +#: src/libslic3r/Print.cpp:1310 +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 "" +"A torre de limpeza suporta atualmente os suportes não-solúveis somente se " +"são imprimidos com o extrusor atual sem provocar uma mudança da ferramenta. " +"(ambos support_material_extruder e support_material_interface_extruder " +"precisam ser definidos como 0)." + +#: src/libslic3r/Print.cpp:1332 +msgid "First layer height can't be greater than nozzle diameter" +msgstr "" +"A primeira altura da camada não pode ser maior do que o diâmetro da ponteira" + +#: src/libslic3r/Print.cpp:1337 +msgid "Layer height can't be greater than nozzle diameter" +msgstr "A altura da camada não pode ser maior do que o diâmetro da ponteira" + +#: src/libslic3r/Print.cpp:1492 +msgid "Infilling layers" +msgstr "Camadas de preenchimento" + +#: src/libslic3r/Print.cpp:1500 +msgid "Generating skirt" +msgstr "Gerando saia" + +#: src/libslic3r/Print.cpp:1508 +msgid "Generating brim" +msgstr "Gerando a aba" + +#: src/libslic3r/Print.cpp:1536 +msgid "Exporting G-code" +msgstr "Exportando o G-code" + +#: src/libslic3r/Print.cpp:1540 +msgid "Generating G-code" +msgstr "Gerando G-code" + +#: src/libslic3r/SLAPrint.cpp:64 +msgid "Slicing model" +msgstr "Modelo de fatiamento" + +#: src/libslic3r/SLAPrint.cpp:65 src/libslic3r/SLAPrint.cpp:899 +msgid "Generating support points" +msgstr "Gerando pontos de suporte" + +#: src/libslic3r/SLAPrint.cpp:66 +msgid "Generating support tree" +msgstr "Gerando suporte em árvore" + +#: src/libslic3r/SLAPrint.cpp:67 +msgid "Generating pad" +msgstr "Gerando pad" + +#: src/libslic3r/SLAPrint.cpp:68 +msgid "Slicing supports" +msgstr "Fatiando suportes" + +#: src/libslic3r/SLAPrint.cpp:85 +msgid "Merging slices and calculating statistics" +msgstr "Mesclando camadas e calculando estatísticas" + +#: src/libslic3r/SLAPrint.cpp:86 +msgid "Rasterizing layers" +msgstr "Rasterizando camadas" + +#: src/libslic3r/SLAPrint.cpp:661 +msgid "" +"Cannot proceed without support points! Add support points or disable support " +"generation." +msgstr "" +"Não pode prosseguir sem pontos de suporte! Adicione pontos de suporte ou " +"desative a geração de suporte." + +#: src/libslic3r/SLAPrint.cpp:678 +msgid "" +"Elevation is too low for object. Use the \"Pad around object\" feature to " +"print the object without elevation." +msgstr "" +"A elevação é muito baixa para o objeto. Use o recurso \"pad ao redor do " +"objeto\" para imprimir o objeto sem elevação." + +#: src/libslic3r/SLAPrint.cpp:684 +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 "" +"As terminações dos pilares de suporte serão implantadas na lacuna entre o " +"objeto e o pad. ' Distância de segurança de base de suporte ' tem de ser " +"maior do que o parâmetro ' pad objecto Gap ' para evitar este." + +#: src/libslic3r/SLAPrint.cpp:696 +msgid "Exposition time is out of printer profile bounds." +msgstr "O tempo de exposição está fora dos limites do perfil da impressora." + +#: src/libslic3r/SLAPrint.cpp:703 +msgid "Initial exposition time is out of printer profile bounds." +msgstr "" +"O tempo de exposição inicial está fora dos limites do perfil da impressora." + +#: src/libslic3r/SLAPrint.cpp:787 +msgid "" +"Slicing had to be stopped due to an internal error: Inconsistent slice index." +msgstr "" +"O fatiamento teve que ser parado devido a um erro interno: índice de " +"fatiamento inconsistente." + +#: src/libslic3r/SLAPrint.cpp:982 src/libslic3r/SLAPrint.cpp:992 +#: src/libslic3r/SLAPrint.cpp:1033 +msgid "Visualizing supports" +msgstr "Visualizando suportes" + +#: src/libslic3r/SLAPrint.cpp:1566 +msgid "Slicing done" +msgstr "Fatiamento pronto" + +#: src/libslic3r/PrintBase.cpp:71 +msgid "Failed processing of the output_filename_format template." +msgstr "Falha no processamento do modelo output_filename_format." + +#: src/libslic3r/PrintConfig.cpp:43 src/libslic3r/PrintConfig.cpp:44 +msgid "Printer technology" +msgstr "Tecnologia da impressora" + +#: src/libslic3r/PrintConfig.cpp:51 +msgid "Bed shape" +msgstr "Formato da mesa" + +#: src/libslic3r/PrintConfig.cpp:56 +msgid "Bed custom texture" +msgstr "Textura customizada da mesa" + +#: src/libslic3r/PrintConfig.cpp:61 +msgid "Bed custom model" +msgstr "Modelo customizado da mesa" + +#: src/libslic3r/PrintConfig.cpp:68 +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 "" +"Essa config. controla a altura (e, portanto, o número total) das fatias/" +"camadas. Camadas mais finas dão melhor precisão, mas levam mais tempo para " +"imprimir." + +#: src/libslic3r/PrintConfig.cpp:75 +msgid "Max print height" +msgstr "Altura máxima de impressão" + +#: src/libslic3r/PrintConfig.cpp:76 +msgid "" +"Set this to the maximum height that can be reached by your extruder while " +"printing." +msgstr "" +"Defina isto para a altura máxima que pode ser alcançada pela sua extrusora " +"durante a impressão." + +#: src/libslic3r/PrintConfig.cpp:82 +msgid "Slice gap closing radius" +msgstr "Raio de fechamento da abertura da fatia" + +#: src/libslic3r/PrintConfig.cpp:84 +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 "" +"As rachaduras menores do que duas vezes o raio de fechamento estão sendo " +"preenchidas durante o fatiamento da malha triangular. A operação de " +"fechamento de vão pode reduzir a resolução final de impressão, portanto, é " +"aconselhável manter o valor razoavelmente baixo." + +#: src/libslic3r/PrintConfig.cpp:92 +msgid "Hostname, IP or URL" +msgstr "Hostname, IP ou URL" + +#: src/libslic3r/PrintConfig.cpp:93 +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 "" +"Slic3r pode carregar arquivos de G-code para um host de impressora. Este " +"campo deve conter o nome de host, o endereço IP ou a URL da instância de " +"host da impressora." + +#: src/libslic3r/PrintConfig.cpp:99 +msgid "API Key / Password" +msgstr "Chave de API/senha" + +#: src/libslic3r/PrintConfig.cpp:100 +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 "" +"Slic3r pode carregar arquivos de G-code para um host de impressora. Este " +"campo deve conter a chave de API ou a senha exigida para a autenticação." + +#: src/libslic3r/PrintConfig.cpp:106 +msgid "HTTPS CA File" +msgstr "Arquivo de CA HTTPS" + +#: src/libslic3r/PrintConfig.cpp:107 +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 "" +"O arquivo de certificado de CA personalizado pode ser especificado para " +"conexões HTTPS OctoPrint, no formato CRT/PEM. Se deixado em branco, o " +"repositório de certificados do OS CA padrão é usado." + +#: src/libslic3r/PrintConfig.cpp:121 +msgid "Avoid crossing perimeters" +msgstr "Evitar cruzamento de perímetros" + +#: src/libslic3r/PrintConfig.cpp:122 +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 "" +"Otimize os movimentos de viagem para minimizar o cruzamento de perímetros. " +"Isto é principalmente útil com extrusoras Bowden que sofrem de escorrimento. " +"Este recurso retarda a impressão e a geração de G-code." + +#: src/libslic3r/PrintConfig.cpp:129 src/libslic3r/PrintConfig.cpp:2027 +msgid "Other layers" +msgstr "Outras camadas" + +#: src/libslic3r/PrintConfig.cpp:130 +msgid "" +"Bed temperature for layers after the first one. Set this to zero to disable " +"bed temperature control commands in the output." +msgstr "" +"Temperatura da mesa para camadas após o primeiro. Defina isso como zero para " +"desabilitar os comandos de controle de temperatura da mesa na saída." + +#: src/libslic3r/PrintConfig.cpp:132 +msgid "Bed temperature" +msgstr "Temperatura da mesa" + +#: src/libslic3r/PrintConfig.cpp:139 +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 "" +"Esse código personalizado é inserido em cada alteração de camada, logo antes " +"da movimentação Z. Observe que você pode usar variáveis de espaço reservado " +"para todas as config. Slic3r, bem como [layer_num] e [layer_z]." + +#: src/libslic3r/PrintConfig.cpp:149 +msgid "Between objects G-code" +msgstr "G-code entre objetos" + +#: src/libslic3r/PrintConfig.cpp:150 +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 "" +"Esse código é inserido entre objetos ao usar a impressão sequencial. Por " +"padrão, a extrusora e a temperatura da mesa são redefinidas usando o comando " +"não esperar; no entanto, se M104, M109, M140 ou M190 são detectados neste " +"código personalizado, Slic3r não adicionará comandos de temperatura. Observe " +"que você pode usar variáveis de espaço reservado para todas as config. de " +"Slic3r, para que você possa colocar um comando \"M109 S " +"[temperatura_primeira_camada]\" onde quiser." + +#: src/libslic3r/PrintConfig.cpp:161 +msgid "Number of solid layers to generate on bottom surfaces." +msgstr "Número de camadas sólidas para gerar em superfícies inferiores." + +#: src/libslic3r/PrintConfig.cpp:162 +msgid "Bottom solid layers" +msgstr "Camadas sólidas inferiores" + +#: src/libslic3r/PrintConfig.cpp:167 +msgid "Bridge" +msgstr "Ponte" + +#: src/libslic3r/PrintConfig.cpp:168 +msgid "" +"This is the acceleration your printer will use for bridges. Set zero to " +"disable acceleration control for bridges." +msgstr "" +"Esta é a aceleração que sua impressora usará para pontes. Defina zero para " +"desabilitar o controle de aceleração para pontes." + +#: 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 +msgid "mm/s²" +msgstr "mm/s²" + +#: src/libslic3r/PrintConfig.cpp:176 +msgid "Bridging angle" +msgstr "Ângulo de ponte" + +#: src/libslic3r/PrintConfig.cpp:178 +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 "" +"Sobreposição de ângulo de ponte. Se deixado em zero, o ângulo de ponte será " +"calculado automaticamente. Caso contrário, o ângulo fornecido será usado " +"para todas as pontes. Use 180 ° para o ângulo zero." + +#: 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:2614 +#: src/libslic3r/PrintConfig.cpp:2724 +msgid "°" +msgstr "°" + +#: src/libslic3r/PrintConfig.cpp:187 +msgid "Bridges fan speed" +msgstr "Velocidade da ventoinha nas pontes" + +#: src/libslic3r/PrintConfig.cpp:188 +msgid "This fan speed is enforced during all bridges and overhangs." +msgstr "" +"Esta velocidade da ventoinha é imposta durante todas as pontes e angulações." + +#: 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:2654 +msgid "%" +msgstr "%" + +#: src/libslic3r/PrintConfig.cpp:196 +msgid "Bridge flow ratio" +msgstr "Relação de fluxo da ponte" + +#: src/libslic3r/PrintConfig.cpp:198 +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 "" +"Esse fator afeta a quantidade de plástico para a ponte. Você pode diminuí-lo " +"um pouco para puxar as extrusões e evitar a flacidez, embora as config. " +"padrão são geralmente boas e você deve experimentar com refrigeração (use " +"uma ventoinha) antes de ajustes isso." + +#: src/libslic3r/PrintConfig.cpp:208 +msgid "Bridges" +msgstr "Pontes" + +#: src/libslic3r/PrintConfig.cpp:210 +msgid "Speed for printing bridges." +msgstr "Velocidade para a impressão de pontes." + +#: 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 +msgid "mm/s" +msgstr "mm/s" + +#: src/libslic3r/PrintConfig.cpp:218 +msgid "Brim width" +msgstr "Largura da aba" + +#: src/libslic3r/PrintConfig.cpp:219 +msgid "" +"Horizontal width of the brim that will be printed around each object on the " +"first layer." +msgstr "" +"Largura horizontal da aba que será impressa em torno de cada objeto na " +"primeira camada." + +#: src/libslic3r/PrintConfig.cpp:226 +msgid "Clip multi-part objects" +msgstr "Clip objetos de várias partes" + +#: src/libslic3r/PrintConfig.cpp:227 +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 "" +"Ao imprimir objetos de vários materiais, essas config. farão com que o " +"Slic3r recorte as partes do objeto sobrepostas uma pela outra (2ª parte será " +"cortada pela 1ª, 3ª parte será cortada pela 1ª e 2ª, etc.)." + +#: src/libslic3r/PrintConfig.cpp:234 +msgid "Colorprint height" +msgstr "Altura da impressão colorida" + +#: src/libslic3r/PrintConfig.cpp:235 +msgid "Heights at which a filament change is to occur." +msgstr "Alturas em que uma mudança do filamento ocorre." + +#: src/libslic3r/PrintConfig.cpp:245 +msgid "Compatible printers condition" +msgstr "Condição de impressoras compatíveis" + +#: src/libslic3r/PrintConfig.cpp:246 +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 "" +"Uma expressão booleana usando os valores de config. de um perfil de " +"impressora ativo. Se essa expressão for avaliada como verdadeira, esse " +"perfil será considerado compatível com o perfil de impressora ativo." + +#: src/libslic3r/PrintConfig.cpp:260 +msgid "Compatible print profiles condition" +msgstr "Condição de perfis de impressão compatíveis" + +#: src/libslic3r/PrintConfig.cpp:261 +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 "" +"Uma expressão booleana usando os valores de config. de um perfil de " +"impressão ativo. Se essa expressão for avaliada como verdadeira, esse perfil " +"será considerado compatível com o perfil de impressão ativo." + +#: src/libslic3r/PrintConfig.cpp:278 +msgid "Complete individual objects" +msgstr "Complete objetos individuais" + +#: src/libslic3r/PrintConfig.cpp:279 +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 "" +"Ao imprimir vários objetos ou cópias, esse recurso concluirá cada objeto " +"antes de passar para o próximo (e iniciando-o de sua camada inferior). Este " +"recurso é útil para evitar o risco de impressões arruinadas. Slic3r deve " +"avisar e impedi-lo de colisões de extrusoras, mas cuidado." + +#: src/libslic3r/PrintConfig.cpp:287 +msgid "Enable auto cooling" +msgstr "Ativar o resfriamento automático" + +#: src/libslic3r/PrintConfig.cpp:288 +msgid "" +"This flag enables the automatic cooling logic that adjusts print speed and " +"fan speed according to layer printing time." +msgstr "" +"Esse sinalizador permite a lógica de resfriamento automática que ajusta a " +"velocidade de impressão e a velocidade do ventoinha de acordo com o tempo de " +"impressão da camada." + +#: src/libslic3r/PrintConfig.cpp:293 +msgid "Cooling tube position" +msgstr "Posição do tubo de resfriamento" + +#: src/libslic3r/PrintConfig.cpp:294 +msgid "Distance of the center-point of the cooling tube from the extruder tip." +msgstr "" +"Distância do ponto central do tubo de resfriamento da ponta da extrusora." + +#: src/libslic3r/PrintConfig.cpp:301 +msgid "Cooling tube length" +msgstr "Comprimento do tubo de resfriamento" + +#: src/libslic3r/PrintConfig.cpp:302 +msgid "Length of the cooling tube to limit space for cooling moves inside it." +msgstr "" +"Comprimento do tubo de resfriamento para limitar o espaço para movimentos de " +"resfriamento dentro dele." + +#: src/libslic3r/PrintConfig.cpp:310 +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 "" +"Esta é a aceleração que sua impressora será redefinida para depois que os " +"valores de aceleração específicos da função forem usados (perímetro/" +"preenchimento). Defina zero para evitar redefinir a aceleração em tudo." + +#: src/libslic3r/PrintConfig.cpp:319 +msgid "Default filament profile" +msgstr "Perfil de filamento padrão" + +#: src/libslic3r/PrintConfig.cpp:320 +msgid "" +"Default filament profile associated with the current printer profile. On " +"selection of the current printer profile, this filament profile will be " +"activated." +msgstr "" +"Perfil de filamento padrão associado ao perfil de impressora atual. Na " +"seleção do perfil da impressora atual, este perfil de filamento será ativado." + +#: src/libslic3r/PrintConfig.cpp:326 +msgid "Default print profile" +msgstr "Perfil de impressão padrão" + +#: src/libslic3r/PrintConfig.cpp:327 src/libslic3r/PrintConfig.cpp:2479 +#: src/libslic3r/PrintConfig.cpp:2490 +msgid "" +"Default print profile associated with the current printer profile. On " +"selection of the current printer profile, this print profile will be " +"activated." +msgstr "" +"Perfil de impressão padrão associado ao perfil de impressora atual. Na " +"seleção do perfil de impressora atual, este perfil de impressão será ativado." + +#: src/libslic3r/PrintConfig.cpp:333 +msgid "Disable fan for the first" +msgstr "Desabilite o ventoinha para a(s) primeira(s)" + +#: src/libslic3r/PrintConfig.cpp:334 +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 "" +"Você pode ajustar isto a um valor positivo para desabilitar a ventoinha " +"durante as primeiras camadas, de modo que melhore a adesão." + +#: 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 "camadas" + +#: src/libslic3r/PrintConfig.cpp:343 +msgid "Don't support bridges" +msgstr "Não suporte pontes" + +#: src/libslic3r/PrintConfig.cpp:345 +msgid "" +"Experimental option for preventing support material from being generated " +"under bridged areas." +msgstr "" +"Opção experimental para impedir que o material de suporte seja gerado em " +"áreas com ponte." + +#: src/libslic3r/PrintConfig.cpp:351 +msgid "Distance between copies" +msgstr "Distância entre cópias" + +#: src/libslic3r/PrintConfig.cpp:352 +msgid "Distance used for the auto-arrange feature of the plater." +msgstr "Distância usada para o recurso de organizar automaticamente o prato." + +#: src/libslic3r/PrintConfig.cpp:359 +msgid "Elephant foot compensation" +msgstr "Compensação do pé do elefante" + +#: src/libslic3r/PrintConfig.cpp:361 +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 "" +"A primeira camada será encolhido no plano XY pelo valor config.urado para " +"compensar a 1ª camada esmagada, também conhecida como pé de elefante." + +#: src/libslic3r/PrintConfig.cpp:370 +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 "" +"Este procedimento final é inserido no final do arquivo de saída. Observe que " +"você pode usar variáveis de espaço reservado para todas as config. de " +"PrusaSlicer." + +#: src/libslic3r/PrintConfig.cpp:380 +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 "" +"Este procedimento final é inserido no final do arquivo de saída, antes da " +"extremidade da impressora Gcode (e antes de qualquer troca de ferramenta " +"deste filamento em caso de impressoras multimaterial). Observe que você pode " +"usar variáveis de espaço reservado para todas as config. de PrusaSlicer. Se " +"você tiver várias extrusoras, o Gcode é processado em ordem de extrusora." + +#: src/libslic3r/PrintConfig.cpp:391 +msgid "Ensure vertical shell thickness" +msgstr "Assegure a espessura vertical da parede" + +#: src/libslic3r/PrintConfig.cpp:393 +msgid "" +"Add solid infill near sloping surfaces to guarantee the vertical shell " +"thickness (top+bottom solid layers)." +msgstr "" +"Adicionar preenchimento sólido perto de superfícies inclinadas para garantir " +"a espessura do escudo vertical (camadas sólidas no topo + base )." + +#: src/libslic3r/PrintConfig.cpp:399 +msgid "Top fill pattern" +msgstr "Padrão de preenchimento do topo" + +#: src/libslic3r/PrintConfig.cpp:401 +msgid "" +"Fill pattern for top infill. This only affects the top visible layer, and " +"not its adjacent solid shells." +msgstr "" +"Padrão de preenchimento para preenchimento do topo. Isto afeta somente a " +"camada visível superior, e não suas paredes adjacentes." + +#: src/libslic3r/PrintConfig.cpp:409 src/libslic3r/PrintConfig.cpp:821 +#: src/libslic3r/PrintConfig.cpp:1972 +msgid "Rectilinear" +msgstr "Rectilíneo" + +#: src/libslic3r/PrintConfig.cpp:410 src/libslic3r/PrintConfig.cpp:827 +msgid "Concentric" +msgstr "Concêntrico" + +#: src/libslic3r/PrintConfig.cpp:411 src/libslic3r/PrintConfig.cpp:831 +msgid "Hilbert Curve" +msgstr "Curva de Hilbert" + +#: src/libslic3r/PrintConfig.cpp:412 src/libslic3r/PrintConfig.cpp:832 +msgid "Archimedean Chords" +msgstr "Cordas Archimedean" + +#: src/libslic3r/PrintConfig.cpp:413 src/libslic3r/PrintConfig.cpp:833 +msgid "Octagram Spiral" +msgstr "Espiral estrelado" + +#: src/libslic3r/PrintConfig.cpp:419 +msgid "Bottom fill pattern" +msgstr "Padrão de preenchimento da base" + +#: src/libslic3r/PrintConfig.cpp:421 +msgid "" +"Fill pattern for bottom infill. This only affects the bottom external " +"visible layer, and not its adjacent solid shells." +msgstr "" +"Padrão de preenchimento para preenchimento da base. Isto afeta somente a " +"camada visível externa inferior, e não suas paredes adjacentes." + +#: src/libslic3r/PrintConfig.cpp:430 src/libslic3r/PrintConfig.cpp:440 +msgid "External perimeters" +msgstr "Perímetros externos" + +#: src/libslic3r/PrintConfig.cpp:432 +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 "" +"Defina isso como um valor diferente de zero para definir uma largura de " +"extrusão manual para perímetros externos. Se for deixado zero, a largura de " +"extrusão padrão será usada se definido, caso contrário, 1,125 x diâmetro da " +"ponteira será usado. Se expresso em porcentagem(por exemplo 200%), será " +"calculado sobre a altura da camada." + +#: 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 +msgid "mm or %" +msgstr "mm ou %" + +#: src/libslic3r/PrintConfig.cpp:442 +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 "" +"Esta config. separada afetará a velocidade dos perímetros externos (os " +"visíveis). Se expresso em porcentagem(por exemplo: 80%) Ele será calculado " +"sobre a velocidade de perímetros config. acima. Defina como zero para auto." + +#: 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 +msgid "mm/s or %" +msgstr "mm/s ou %" + +#: src/libslic3r/PrintConfig.cpp:452 +msgid "External perimeters first" +msgstr "Perímetros externos primeiro" + +#: src/libslic3r/PrintConfig.cpp:454 +msgid "" +"Print contour perimeters from the outermost one to the innermost one instead " +"of the default inverse order." +msgstr "" +"Imprima perímetros de contorno do mais externo para o mais interno em vez da " +"ordem inversa padrão." + +#: src/libslic3r/PrintConfig.cpp:460 +msgid "Extra perimeters if needed" +msgstr "Perímetros extras se necessário" + +#: src/libslic3r/PrintConfig.cpp:462 +#, 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 "" +"Adicione mais perímetros quando necessário para evitar lacunas em paredes " +"inclinados. Slic3r continua adicionando perímetros, até que mais de 70% o do " +"loop imediatamente acima é suportado." + +#: src/libslic3r/PrintConfig.cpp:472 +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 "" +"A extrusora a ser usada (a menos que config. de extrusoras mais específicas " +"sejam especificadas). Esse valor substitui as extrusoras de perímetro e " +"preenchimento, mas não as extrusoras de suporte." + +#: src/libslic3r/PrintConfig.cpp:484 +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 "" +"Defina isto para a distância vertical entre a ponta do bico e (normalmente) " +"as hastes do X. Em outras palavras, esta é a altura do cilindro de folga em " +"torno de sua extrusora, e representa a profundidade máxima que a extrusora " +"pode espreitar antes de colidir com outros objetos impressos." + +#: src/libslic3r/PrintConfig.cpp:494 +msgid "Radius" +msgstr "Raio" + +#: src/libslic3r/PrintConfig.cpp:495 +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 "" +"Defina isso para o raio de folga em torno de sua extrusora. Se a extrusora " +"não estiver centralizada, escolha o maior valor para a segurança. Essa " +"config. é usada para verificar colisões e exibir a visualização gráfica no " +"prato." + +#: src/libslic3r/PrintConfig.cpp:505 +msgid "Extruder Color" +msgstr "Cor da extrusora" + +#: src/libslic3r/PrintConfig.cpp:506 src/libslic3r/PrintConfig.cpp:566 +msgid "This is only used in the Slic3r interface as a visual help." +msgstr "Isso é usado apenas na interface Slic3r como uma ajuda visual." + +#: src/libslic3r/PrintConfig.cpp:512 +msgid "Extruder offset" +msgstr "Compensamento da extrusora" + +#: src/libslic3r/PrintConfig.cpp:513 +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 "" +"Se o seu firmware não manipula o deslocamento da extrusora, você precisa do " +"G-code para levá-lo em conta. Esta opção permite especificar o deslocamento " +"de cada extrusora em relação à primeira. Ele espera coordenadas positivas " +"(eles serão subtraída da coordenada XY)." + +#: src/libslic3r/PrintConfig.cpp:522 +msgid "Extrusion axis" +msgstr "Eixo de extrusão" + +#: src/libslic3r/PrintConfig.cpp:523 +msgid "" +"Use this option to set the axis letter associated to your printer's extruder " +"(usually E but some printers use A)." +msgstr "" +"Use esta opção para definir a letra do eixo associada à extrusora da sua " +"impressora (geralmente E, mas algumas impressoras usam A)." + +#: src/libslic3r/PrintConfig.cpp:528 +msgid "Extrusion multiplier" +msgstr "Multiplicador de extrusão" + +#: src/libslic3r/PrintConfig.cpp:529 +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 "" +"Esse fator altera a quantidade de fluxo proporcionalmente. Você pode " +"precisar de ajustar esta config. para obter acabamento de superfície " +"agradável e corrigir larguras de parede única. Os valores usuais são entre " +"0,9 e 1,1. Se você acha que precisa mudar isso mais, verifique o diâmetro do " +"filamento e os passos configurados no firmware da extrusora." + +#: src/libslic3r/PrintConfig.cpp:537 +msgid "Default extrusion width" +msgstr "Largura de extrusão padrão" + +#: src/libslic3r/PrintConfig.cpp:539 +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 "" +"Defina isso como um valor diferente de zero para permitir uma largura de " +"extrusão manual. Se deixado a zero, Slic3r deriva larguras da extrusão do " +"diâmetro da ponteira (veja as dicas ferramentas para a largura da extrusão " +"do perímetro, a largura de extrusão do preenchimento etc.). Se expresso como " +"porcentagem (por exemplo: 230%), ele será calculado sobre a altura da camada." + +#: src/libslic3r/PrintConfig.cpp:548 +msgid "Keep fan always on" +msgstr "Mantenha a ventoinha sempre ligada" + +#: src/libslic3r/PrintConfig.cpp:549 +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 "" +"Se isso estiver ativado, a ventoinha nunca será desativada e será mantida " +"funcionando pelo menos em sua velocidade mínima. Útil para o PLA, " +"prejudicial para o ABS." + +#: src/libslic3r/PrintConfig.cpp:554 +msgid "Enable fan if layer print time is below" +msgstr "Ative o ventoinha se o tempo de impressão da camada estiver abaixo" + +#: src/libslic3r/PrintConfig.cpp:555 +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 "" +"Se o tempo de impressão da camada for estimado abaixo desse número de " +"segundos, a ventoinha será ativada e sua velocidade será calculada " +"interpolando as velocidades mínima e máxima." + +#: src/libslic3r/PrintConfig.cpp:557 src/libslic3r/PrintConfig.cpp:1687 +msgid "approximate seconds" +msgstr "segundos aproximados" + +#: src/libslic3r/PrintConfig.cpp:571 +msgid "Filament notes" +msgstr "Notas de filamento" + +#: src/libslic3r/PrintConfig.cpp:572 +msgid "You can put your notes regarding the filament here." +msgstr "Você pode colocar suas anotações sobre o filamento aqui." + +#: src/libslic3r/PrintConfig.cpp:580 src/libslic3r/PrintConfig.cpp:1231 +msgid "Max volumetric speed" +msgstr "Máxima velocidade volumétrica" + +#: src/libslic3r/PrintConfig.cpp:581 +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 "" +"Velocidade máxima volumétrica permitida para este filamento. Limita a " +"velocidade volumétrica máxima de uma impressão ao mínimo de velocidade " +"volumétrica de impressão e de filamento. Defina como zero para nenhum limite." + +#: src/libslic3r/PrintConfig.cpp:590 +msgid "Loading speed" +msgstr "Velocidade de carregamento" + +#: src/libslic3r/PrintConfig.cpp:591 +msgid "Speed used for loading the filament on the wipe tower." +msgstr "Velocidade utilizada para carregar o filamento na torre de limpeza." + +#: src/libslic3r/PrintConfig.cpp:598 +msgid "Loading speed at the start" +msgstr "Velocidade de carregamento no início" + +#: src/libslic3r/PrintConfig.cpp:599 +msgid "Speed used at the very beginning of loading phase." +msgstr "Velocidade utilizada no início da fase de carregamento." + +#: src/libslic3r/PrintConfig.cpp:606 +msgid "Unloading speed" +msgstr "Velocidade de descarregamento" + +#: src/libslic3r/PrintConfig.cpp:607 +msgid "" +"Speed used for unloading the filament on the wipe tower (does not affect " +"initial part of unloading just after ramming)." +msgstr "" +"Velocidade utilizada para descarregar o filamento na torre de limpeza (não " +"afeta a parte inicial do descarregamento logo após o Ramming)." + +#: src/libslic3r/PrintConfig.cpp:615 +msgid "Unloading speed at the start" +msgstr "Velocidade de descarregamento no início" + +#: src/libslic3r/PrintConfig.cpp:616 +msgid "" +"Speed used for unloading the tip of the filament immediately after ramming." +msgstr "" +"Velocidade usada para descarregar a ponta do filamento imediatamente após o " +"Ramming." + +#: src/libslic3r/PrintConfig.cpp:623 +msgid "Delay after unloading" +msgstr "Atraso após o descarregamento" + +#: src/libslic3r/PrintConfig.cpp:624 +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 "" +"Tempo de espera após o filamento ser descarregado. Pode ajudar a obter " +"trocas de ferramenta confiáveis com materiais flexíveis que podem precisar " +"de mais tempo para reduzir as dimensões originais." + +#: src/libslic3r/PrintConfig.cpp:633 +msgid "Number of cooling moves" +msgstr "Número de movimentos de resfriamento" + +#: src/libslic3r/PrintConfig.cpp:634 +msgid "" +"Filament is cooled by being moved back and forth in the cooling tubes. " +"Specify desired number of these moves." +msgstr "" +"O filamento é resfriado por ser movido para frente e para trás nos tubos de " +"resfriamento. Especifique o número desejado desses movimentos." + +#: src/libslic3r/PrintConfig.cpp:642 +msgid "Speed of the first cooling move" +msgstr "Velocidade do primeiro movimento de resfriamento" + +#: src/libslic3r/PrintConfig.cpp:643 +msgid "Cooling moves are gradually accelerating beginning at this speed." +msgstr "" +"Movimentos de resfriamento estão gradualmente acelerando a partir desta " +"velocidade." + +#: src/libslic3r/PrintConfig.cpp:650 +msgid "Minimal purge on wipe tower" +msgstr "Remoção mínima na torre da limpeza" + +#: src/libslic3r/PrintConfig.cpp:651 +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 "" +"Após uma mudança da ferramenta, a posição exata do filamento recentemente " +"carregado dentro da ponteira pode não ser conhecida, e a pressão do " +"filamento provavelmente ainda não esteja estável. Antes de purgar a cabeça " +"de impressão em um preenchimento ou um objeto sacrificial, Slic3r sempre " +"Prime esta quantidade de material para a torre de limpeza para produzir " +"sucessivas preenchimento ou sacrificial objeto extrusões de forma confiável." + +#: src/libslic3r/PrintConfig.cpp:655 +msgid "mm³" +msgstr "mm³" + +#: src/libslic3r/PrintConfig.cpp:661 +msgid "Speed of the last cooling move" +msgstr "Velocidade do último movimento de resfriamento" + +#: src/libslic3r/PrintConfig.cpp:662 +msgid "Cooling moves are gradually accelerating towards this speed." +msgstr "" +"Movimentos de resfriamento estão gradualmente acelerando para esta " +"velocidade." + +#: src/libslic3r/PrintConfig.cpp:669 +msgid "Filament load time" +msgstr "Tempo de carga do filamento" + +#: src/libslic3r/PrintConfig.cpp:670 +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 "" +"Tempo para o firmware da impressora (ou a Multi Material Unit 2.0 para " +"carregar um novo filamento durante uma mudança de ferramenta (ao executar o " +"código T). Esse tempo é adicionado ao tempo total de impressão pelo " +"estimador de tempo do G-code." + +#: src/libslic3r/PrintConfig.cpp:677 +msgid "Ramming parameters" +msgstr "Parâmetros de Ramming" + +#: src/libslic3r/PrintConfig.cpp:678 +msgid "" +"This string is edited by RammingDialog and contains ramming specific " +"parameters." +msgstr "" +"Essa cadeia de caracteres é editada por rammingdialog e contém parâmetros " +"específicos de Ramming." + +#: src/libslic3r/PrintConfig.cpp:684 +msgid "Filament unload time" +msgstr "Tempo de descarregamento do filamento" + +#: src/libslic3r/PrintConfig.cpp:685 +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 "" +"Tempo para o firmware da impressora (ou a unidade de material multi 2,0) " +"para descarregar um filamento durante uma mudança de ferramenta (ao executar " +"o código T). Esse tempo é adicionado ao tempo total de impressão pelo " +"estimador de tempo do G-code." + +#: src/libslic3r/PrintConfig.cpp:693 +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 "" +"Insira o diâmetro do filamento aqui. Boa precisão é necessária, então use um " +"paquímetro e fazer várias medições ao longo do filamento, em seguida, " +"calcular a média." + +#: src/libslic3r/PrintConfig.cpp:700 +msgid "Density" +msgstr "Densidade" + +#: src/libslic3r/PrintConfig.cpp:701 +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 "" +"Insira sua densidade de filamento aqui. Isto é apenas para informação " +"estatística. Uma maneira decente é pesar um comprimento conhecido do " +"filamento e computar a relação do comprimento ao volume. Melhor é calcular o " +"volume diretamente através do deslocamento." + +#: src/libslic3r/PrintConfig.cpp:704 +msgid "g/cm³" +msgstr "g/cm³" + +#: src/libslic3r/PrintConfig.cpp:709 +msgid "Filament type" +msgstr "Tipo de filamento" + +#: src/libslic3r/PrintConfig.cpp:710 +msgid "The filament material type for use in custom G-codes." +msgstr "O tipo de material de filamento para uso em G-code customizados." + +#: src/libslic3r/PrintConfig.cpp:736 +msgid "Soluble material" +msgstr "Material solúvel" + +#: src/libslic3r/PrintConfig.cpp:737 +msgid "Soluble material is most likely used for a soluble support." +msgstr "O material solúvel é mais provável usado para um suporte solúvel." + +#: src/libslic3r/PrintConfig.cpp:743 +msgid "" +"Enter your filament cost per kg here. This is only for statistical " +"information." +msgstr "" +"Insira o seu custo de filamento por kg aqui. Isto é apenas para informação " +"estatística." + +#: src/libslic3r/PrintConfig.cpp:744 +msgid "money/kg" +msgstr "dinheiro/kg" + +#: src/libslic3r/PrintConfig.cpp:753 +msgid "Fill angle" +msgstr "Ângulo de preenchimento" + +#: src/libslic3r/PrintConfig.cpp:755 +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 "" +"Ângulo padrão para a orientação de preenchimento. A hachura cruzada será " +"aplicada a isso. Pontes serão preenchidas usando a melhor direção Slic3r " +"pode detectar, portanto, essa config. não vai afeta-los." + +#: src/libslic3r/PrintConfig.cpp:767 +msgid "Fill density" +msgstr "Densidade de preenchimento" + +#: src/libslic3r/PrintConfig.cpp:769 +msgid "Density of internal infill, expressed in the range 0% - 100%." +msgstr "Densidade de preenchimento interno, expresso na faixa de 0%-100%." + +#: src/libslic3r/PrintConfig.cpp:804 +msgid "Fill pattern" +msgstr "Padrão de preenchimento" + +#: src/libslic3r/PrintConfig.cpp:806 +msgid "Fill pattern for general low-density infill." +msgstr "Padrão de preenchimento para preenchimento de baixa densidade." + +#: src/libslic3r/PrintConfig.cpp:822 +msgid "Grid" +msgstr "Grade" + +#: src/libslic3r/PrintConfig.cpp:823 +msgid "Triangles" +msgstr "Triângulos" + +#: src/libslic3r/PrintConfig.cpp:824 +msgid "Stars" +msgstr "Estrelas" + +#: src/libslic3r/PrintConfig.cpp:825 +msgid "Cubic" +msgstr "Cúbico" + +#: src/libslic3r/PrintConfig.cpp:826 +msgid "Line" +msgstr "Linha" + +#: src/libslic3r/PrintConfig.cpp:828 src/libslic3r/PrintConfig.cpp:1974 +msgid "Honeycomb" +msgstr "Hexágono" + +#: src/libslic3r/PrintConfig.cpp:829 +msgid "3D Honeycomb" +msgstr "Hexágono 3D" + +#: src/libslic3r/PrintConfig.cpp:830 +msgid "Gyroid" +msgstr "Giróide" + +#: src/libslic3r/PrintConfig.cpp:837 src/libslic3r/PrintConfig.cpp:846 +#: src/libslic3r/PrintConfig.cpp:854 src/libslic3r/PrintConfig.cpp:887 +msgid "First layer" +msgstr "Primeira camada" + +#: src/libslic3r/PrintConfig.cpp:838 +msgid "" +"This is the acceleration your printer will use for first layer. Set zero to " +"disable acceleration control for first layer." +msgstr "" +"Esta é a aceleração que sua impressora usará para a primeira camada. Defina " +"zero para desabilitar o controle de aceleração para a primeira camada." + +#: src/libslic3r/PrintConfig.cpp:847 +msgid "" +"Heated build plate temperature for the first layer. Set this to zero to " +"disable bed temperature control commands in the output." +msgstr "" +"Temperatura da mesa aquecida para a primeira camada. Defina isso como zero " +"para desabilitar os comandos de controle de temperatura da mesa na saída." + +#: src/libslic3r/PrintConfig.cpp:856 +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 "" +"Defina isso como um valor diferente de zero para definir uma largura de " +"extrusão manual para a primeira camada. Você pode usar este para forçar " +"extrusões maiores para a melhor adesão. Se expresso em porcentagem(por " +"exemplo, 120%) será computado sobre a primeira altura da camada. Se definido " +"como zero, ele usará a largura de extrusão padrão." + +#: src/libslic3r/PrintConfig.cpp:866 +msgid "First layer height" +msgstr "Altura da primeira camada" + +#: src/libslic3r/PrintConfig.cpp:868 +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 "" +"Ao imprimir com alturas muito baixas da camada, você pode ainda querer " +"imprimir uma camada inferior mais grossa para melhorar a adesão e a " +"tolerância para mesas não perfeitas. Isso pode ser expresso como um valor " +"absoluto ou como uma porcentagem (por exemplo: 150%) sobre a altura da " +"camada padrão." + +#: src/libslic3r/PrintConfig.cpp:877 +msgid "First layer speed" +msgstr "Velocidade da primeira camada" + +#: src/libslic3r/PrintConfig.cpp:878 +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 "" +"Se expresso como valor absoluto em mm/s, esta velocidade será aplicada a " +"todos os movimentos de impressão da primeira camada, independentemente do " +"seu tipo. Se expresso em porcentagem(por exemplo: 40%) Ele dimensionará as " +"velocidades padrão." + +#: src/libslic3r/PrintConfig.cpp:888 +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 "" +"Temperatura da extrusora para a primeira camada. Se você quiser controlar a " +"temperatura manualmente durante a impressão, defina isso como zero para " +"desabilitar os comandos de controle de temperatura no arquivo de saída." + +#: src/libslic3r/PrintConfig.cpp:897 +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 "" +"Velocidade para encher pequenas lacunas usando movimentos de ziguezague " +"curtos. Mantenha este razoavelmente baixo para evitar demasiada agitação e " +"problemas de ressonância. Defina zero para desabilitar o preenchimento de " +"lacunas." + +#: src/libslic3r/PrintConfig.cpp:905 +msgid "Verbose G-code" +msgstr "Gcode detalhado" + +#: src/libslic3r/PrintConfig.cpp:906 +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 "" +"Habilite isso para obter um arquivo de G-code comentado, com cada linha " +"explicada por um texto descritivo. Se você imprimir a partir do cartão SD, o " +"peso adicional do arquivo pode fazer o seu firmware ficar mais lento." + +#: src/libslic3r/PrintConfig.cpp:913 +msgid "G-code flavor" +msgstr "Tipo de G-code" + +#: src/libslic3r/PrintConfig.cpp:914 +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 "" +"Alguns comandos G/M-code, incluindo controle de temperatura e outros, não " +"são universais. Defina esta opção para o firmware da impressora para obter " +"uma saída compatível. O \"sem extrusão\" tipo impede PrusaSlicer de exportar " +"qualquer valor de extrusão em tudo." + +#: src/libslic3r/PrintConfig.cpp:937 +msgid "No extrusion" +msgstr "Sem extrusão" + +#: src/libslic3r/PrintConfig.cpp:942 +msgid "Label objects" +msgstr "Rotular objetos" + +#: src/libslic3r/PrintConfig.cpp:943 +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 "" +"Habilite isso para adicionar comentários aos movimentos de impressão de " +"rotulagem do G-code com o objeto ao qual eles pertencem, o que é útil para o " +"plugin Octoprint CancelObject. Essas config. não são compatíveis com a " +"config. de multi material de extrusora única e limpe em objeto/limpar em " +"preenchimento." + +#: src/libslic3r/PrintConfig.cpp:950 +msgid "High extruder current on filament swap" +msgstr "Corrente elevada da extrusora na troca do filamento" + +#: src/libslic3r/PrintConfig.cpp:951 +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 "" +"Pode ser benéfico aumentar a corrente do motor da extrusora durante a " +"seqüência da troca do filamento para permitir taxas de alimentação de " +"Ramming rápidas e para superar a resistência ao carregar um filamento com " +"uma ponta feia." + +#: src/libslic3r/PrintConfig.cpp:959 +msgid "" +"This is the acceleration your printer will use for infill. Set zero to " +"disable acceleration control for infill." +msgstr "" +"Esta é a aceleração que sua impressora usará para preenchimento. Defina zero " +"para desabilitar o controle de aceleração para preenchimento." + +#: src/libslic3r/PrintConfig.cpp:967 +msgid "Combine infill every" +msgstr "Combine preenchimento a cada" + +#: src/libslic3r/PrintConfig.cpp:969 +msgid "" +"This feature allows to combine infill and speed up your print by extruding " +"thicker infill layers while preserving thin perimeters, thus accuracy." +msgstr "" +"Este recurso permite combinar preenchimento e acelerar a sua impressão por " +"extrusão camadas de preenchimento mais espessa, preservando perímetros " +"finos, assim, a precisão." + +#: src/libslic3r/PrintConfig.cpp:972 +msgid "Combine infill every n layers" +msgstr "Combine preenchimento cada n camadas" + +#: src/libslic3r/PrintConfig.cpp:978 +msgid "Infill extruder" +msgstr "Extrusora de preenchimento" + +#: src/libslic3r/PrintConfig.cpp:980 +msgid "The extruder to use when printing infill." +msgstr "" +"A extrusora a ser utilizada quando estiver imprimindo preenchimento sólido." + +#: src/libslic3r/PrintConfig.cpp:988 +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 "" +"Defina isso como um valor diferente de zero para definir uma largura de " +"extrusão manual para preenchimento. Se for deixado zero, a largura de " +"extrusão padrão será usada se definido, caso contrário, 1,125 x diâmetro da " +"ponteira será usado. Você pode querer usar extrusora mais larga para " +"acelerar o preenchimento e tornar suas peças mais fortes. Se expresso em " +"porcentagem(por exemplo, 90%) Ele será calculado sobre a altura da camada." + +#: src/libslic3r/PrintConfig.cpp:997 +msgid "Infill before perimeters" +msgstr "Preenchimento antes dos perímetros" + +#: src/libslic3r/PrintConfig.cpp:998 +msgid "" +"This option will switch the print order of perimeters and infill, making the " +"latter first." +msgstr "" +"Esta opção irá mudar a ordem de impressão de perímetros e preenchimento, " +"tornando o último primeiro." + +#: src/libslic3r/PrintConfig.cpp:1003 +msgid "Only infill where needed" +msgstr "Somente preenchimento onde necessário" + +#: src/libslic3r/PrintConfig.cpp:1005 +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 "" +"Esta opção limitará a preenchimento às áreas realmente necessárias para " +"suportar tetos (atuará como o material de sustentação interno). Se " +"habilitada, retarda a geração de G-code devido às várias verificações " +"envolvidas." + +#: src/libslic3r/PrintConfig.cpp:1012 +msgid "Infill/perimeters overlap" +msgstr "Sobreposição de preenchimento/perímetros" + +#: src/libslic3r/PrintConfig.cpp:1014 +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 "" +"Esta config. aplica uma sobreposição adicional entre preenchimento e " +"perímetros para melhor colagem. Teoricamente isso não deveria ser " +"necessário, mas a folga pode causar lacunas. Se expresso em " +"porcentagem(exemplo: 15%) é calculado sobre a largura da extrusão do " +"perímetro." + +#: src/libslic3r/PrintConfig.cpp:1025 +msgid "Speed for printing the internal fill. Set to zero for auto." +msgstr "" +"Velocidade para imprimir o preenchimento interno. Defina como zero para auto." + +#: src/libslic3r/PrintConfig.cpp:1033 +msgid "Inherits profile" +msgstr "Herda o perfil" + +#: src/libslic3r/PrintConfig.cpp:1034 +msgid "Name of the profile, from which this profile inherits." +msgstr "Nome do perfil, a partir do qual este perfil herda." + +#: src/libslic3r/PrintConfig.cpp:1047 +msgid "Interface shells" +msgstr "Interface dos perímetros externos." + +#: src/libslic3r/PrintConfig.cpp:1048 +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 "" +"Force a geração de perímetros externos sólidas entre materiais/volumes " +"adjacentes. Útil para cópias da multi-extrusora com materiais translúcidos " +"ou material de sustentação solúvel manual." + +#: src/libslic3r/PrintConfig.cpp:1057 +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 "" +"Este código personalizado é inserido em cada mudança de camada, logo após o " +"movimento Z e antes que a extrusora se mova para o primeiro ponto de camada. " +"Observe que você pode usar variáveis de espaço reservado para todas as " +"config. Slic3r, bem como [layer_num] e [layer_z]." + +#: src/libslic3r/PrintConfig.cpp:1068 +msgid "Supports remaining times" +msgstr "Tempo de impressão restante" + +#: src/libslic3r/PrintConfig.cpp:1069 +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 "" +"Emita M73 P [porcentagem impressa] R [tempo restante em minutos] em " +"intervalos de 1 minuto no G-code para permitir que o firmware mostre o tempo " +"restante exato. A partir de agora apenas o firmware Prusa i3 MK3 reconhece " +"M73. Além disso, o firmware i3 MK3 suporta M73 QXX Sxx para o modo " +"silencioso." + +#: src/libslic3r/PrintConfig.cpp:1077 +msgid "Supports stealth mode" +msgstr "Suporta o modo silencioso" + +#: src/libslic3r/PrintConfig.cpp:1078 +msgid "The firmware supports stealth mode" +msgstr "O firmware suporta o modo silencioso" + +#: src/libslic3r/PrintConfig.cpp:1102 +msgid "Maximum feedrate X" +msgstr "Máxima taxa de alimentação do X" + +#: src/libslic3r/PrintConfig.cpp:1103 +msgid "Maximum feedrate Y" +msgstr "Máxima taxa de alimentação do Y" + +#: src/libslic3r/PrintConfig.cpp:1104 +msgid "Maximum feedrate Z" +msgstr "Máxima taxa de alimentação do Z" + +#: src/libslic3r/PrintConfig.cpp:1105 +msgid "Maximum feedrate E" +msgstr "Máxima taxa de alimentação do E" + +#: src/libslic3r/PrintConfig.cpp:1108 +msgid "Maximum feedrate of the X axis" +msgstr "Máxima taxa de alimentação do eixo X" + +#: src/libslic3r/PrintConfig.cpp:1109 +msgid "Maximum feedrate of the Y axis" +msgstr "Máxima taxa de alimentação do eixo Y" + +#: src/libslic3r/PrintConfig.cpp:1110 +msgid "Maximum feedrate of the Z axis" +msgstr "Máxima taxa de alimentação do eixo Z" + +#: src/libslic3r/PrintConfig.cpp:1111 +msgid "Maximum feedrate of the E axis" +msgstr "Máxima taxa de alimentação do eixo E" + +#: src/libslic3r/PrintConfig.cpp:1120 +msgid "Maximum acceleration X" +msgstr "Aceleração máxima do X" + +#: src/libslic3r/PrintConfig.cpp:1121 +msgid "Maximum acceleration Y" +msgstr "Aceleração máxima do Y" + +#: src/libslic3r/PrintConfig.cpp:1122 +msgid "Maximum acceleration Z" +msgstr "Aceleração máxima do Z" + +#: src/libslic3r/PrintConfig.cpp:1123 +msgid "Maximum acceleration E" +msgstr "Aceleração máxima do E" + +#: src/libslic3r/PrintConfig.cpp:1126 +msgid "Maximum acceleration of the X axis" +msgstr "Aceleração máxima do eixo X" + +#: src/libslic3r/PrintConfig.cpp:1127 +msgid "Maximum acceleration of the Y axis" +msgstr "Aceleração máxima do eixo Y" + +#: src/libslic3r/PrintConfig.cpp:1128 +msgid "Maximum acceleration of the Z axis" +msgstr "Aceleração máxima do eixo Z" + +#: src/libslic3r/PrintConfig.cpp:1129 +msgid "Maximum acceleration of the E axis" +msgstr "Aceleração máxima do eixo E" + +#: src/libslic3r/PrintConfig.cpp:1138 +msgid "Maximum jerk X" +msgstr "Máximo empurrão X" + +#: src/libslic3r/PrintConfig.cpp:1139 +msgid "Maximum jerk Y" +msgstr "Máximo empurrão Y" + +#: src/libslic3r/PrintConfig.cpp:1140 +msgid "Maximum jerk Z" +msgstr "Máximo empurrão Z" + +#: src/libslic3r/PrintConfig.cpp:1141 +msgid "Maximum jerk E" +msgstr "Máximo empurrão E" + +#: src/libslic3r/PrintConfig.cpp:1144 +msgid "Maximum jerk of the X axis" +msgstr "Máximo empurrão do eixo X" + +#: src/libslic3r/PrintConfig.cpp:1145 +msgid "Maximum jerk of the Y axis" +msgstr "Máximo empurrão do eixo Y" + +#: src/libslic3r/PrintConfig.cpp:1146 +msgid "Maximum jerk of the Z axis" +msgstr "Máximo empurrão do eixo Z" + +#: src/libslic3r/PrintConfig.cpp:1147 +msgid "Maximum jerk of the E axis" +msgstr "Máximo empurrão do eixo E" + +#: src/libslic3r/PrintConfig.cpp:1158 +msgid "Minimum feedrate when extruding" +msgstr "Taxa de alimentação mínima ao extrudar" + +#: src/libslic3r/PrintConfig.cpp:1160 +msgid "Minimum feedrate when extruding (M205 S)" +msgstr "Taxa de alimentação mínima ao extrudar (M205 S)" + +#: src/libslic3r/PrintConfig.cpp:1169 +msgid "Minimum travel feedrate" +msgstr "Taxa de alimentação mínima ao viajar" + +#: src/libslic3r/PrintConfig.cpp:1171 +msgid "Minimum travel feedrate (M205 T)" +msgstr "Taxa de alimentação mínima ao viajar (M205 T)" + +#: src/libslic3r/PrintConfig.cpp:1180 +msgid "Maximum acceleration when extruding" +msgstr "Aceleração máxima quando expurgando" + +#: src/libslic3r/PrintConfig.cpp:1182 +msgid "Maximum acceleration when extruding (M204 S)" +msgstr "Aceleração máxima quando extrudando (M204 S)" + +#: src/libslic3r/PrintConfig.cpp:1191 +msgid "Maximum acceleration when retracting" +msgstr "Aceleração máxima durante a retração" + +#: src/libslic3r/PrintConfig.cpp:1193 +msgid "Maximum acceleration when retracting (M204 T)" +msgstr "Aceleração máxima quando retração (M204 T)" + +#: src/libslic3r/PrintConfig.cpp:1201 src/libslic3r/PrintConfig.cpp:1210 +msgid "Max" +msgstr "Máx" + +#: src/libslic3r/PrintConfig.cpp:1202 +msgid "This setting represents the maximum speed of your fan." +msgstr "Esta config. representa a velocidade máxima da sua ventoinha." + +#: src/libslic3r/PrintConfig.cpp:1211 +#, 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 "" +"Esta é a altura mais alta imprimível para esta extrusora, usada para tampar " +"a altura variável da camada e suportar a altura da camada. A altura " +"recomendada máxima da camada é 75% o da largura da extrusão para conseguir a " +"adesão razoável entre camadas. Se definido como 0, a altura da camada é " +"limitada a 75% o do diâmetro da ponteira." + +#: src/libslic3r/PrintConfig.cpp:1221 +msgid "Max print speed" +msgstr "Velocidade máxima de impressão" + +#: src/libslic3r/PrintConfig.cpp:1222 +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 "" +"Ao definir outras config. de velocidade para 0, o Slic3r irá calcular " +"automaticamente a velocidade ideal, a fim de manter a pressão constante da " +"extrusora. Esta config. experimental é usada para definir a velocidade de " +"impressão mais alta que você deseja permitir." + +#: src/libslic3r/PrintConfig.cpp:1232 +msgid "" +"This experimental setting is used to set the maximum volumetric speed your " +"extruder supports." +msgstr "" +"Esta config. experimental é usada para definir a velocidade máxima " +"volumétrica que sua extrusora suporta." + +#: src/libslic3r/PrintConfig.cpp:1241 +msgid "Max volumetric slope positive" +msgstr "Inclinação volumétrica máx positiva" + +#: src/libslic3r/PrintConfig.cpp:1242 src/libslic3r/PrintConfig.cpp:1253 +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 "" +"Esta config. experimental é usada para limitar a velocidade de mudança na " +"taxa de extrusão. Um valor de 1,8 mm ³/s ² assegura que uma alteração da " +"taxa de extrusão de 1,8 mm ³/s (largura de extrusão de 0,45 mm, altura de " +"extrusão de 0,2 mm, avanço de 20 mm/s) para 5,4 mm ³/s (avanço 60 mm/s) " +"levará pelo menos 2 segundos." + +#: src/libslic3r/PrintConfig.cpp:1246 src/libslic3r/PrintConfig.cpp:1257 +msgid "mm³/s²" +msgstr "mm ³/s ²" + +#: src/libslic3r/PrintConfig.cpp:1252 +msgid "Max volumetric slope negative" +msgstr "Inclinação volumétrica máx negativa" + +#: src/libslic3r/PrintConfig.cpp:1264 src/libslic3r/PrintConfig.cpp:1273 +msgid "Min" +msgstr "Min" + +#: src/libslic3r/PrintConfig.cpp:1265 +msgid "This setting represents the minimum PWM your fan needs to work." +msgstr "" +"Esta config. representa o PWM mínimo que seu ventoinha precisa para " +"trabalhar." + +#: src/libslic3r/PrintConfig.cpp:1274 +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 "" +"Esta é a altura mais baixa imprimível para esta extrusora e limita a " +"definição para a altura variável da camada. Os valores típicos são entre 0, " +"5 mm e 0,1 mm." + +#: src/libslic3r/PrintConfig.cpp:1282 +msgid "Min print speed" +msgstr "Velocidade mínima de impressão" + +#: src/libslic3r/PrintConfig.cpp:1283 +msgid "Slic3r will not scale speed down below this speed." +msgstr "Slic3r não vai escalar a velocidade abaixo desta velocidade." + +#: src/libslic3r/PrintConfig.cpp:1290 +msgid "Minimal filament extrusion length" +msgstr "Comprimento mínimo da extrusão do filamento" + +#: src/libslic3r/PrintConfig.cpp:1291 +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 "" +"Gerar não menos do que o número de voltas de saia necessários para consumir " +"a quantidade especificada de filamento na camada inferior. Para máquinas " +"multiextrusoras, este mínimo aplica-se a cada extrusora." + +#: src/libslic3r/PrintConfig.cpp:1300 +msgid "Configuration notes" +msgstr "Notas de config." + +#: src/libslic3r/PrintConfig.cpp:1301 +msgid "" +"You can put here your personal notes. This text will be added to the G-code " +"header comments." +msgstr "" +"Você pode colocar aqui suas anotações pessoais. Este texto será adicionado " +"aos comentários do cabeçalho do G-code." + +#: src/libslic3r/PrintConfig.cpp:1311 +msgid "" +"This is the diameter of your extruder nozzle (for example: 0.5, 0.35 etc.)" +msgstr "" +"Este é o diâmetro da ponteira da extrusora (por exemplo: 0.5, 0.35 etc.)" + +#: src/libslic3r/PrintConfig.cpp:1316 +msgid "Host Type" +msgstr "Tipo de host" + +#: src/libslic3r/PrintConfig.cpp:1317 +msgid "" +"Slic3r can upload G-code files to a printer host. This field must contain " +"the kind of the host." +msgstr "" +"Slic3r pode carregar arquivos de G-code para um host de impressora. Este " +"campo deve conter o tipo do host." + +#: src/libslic3r/PrintConfig.cpp:1328 +msgid "Only retract when crossing perimeters" +msgstr "Apenas retrair quando cruzar perímetros" + +#: src/libslic3r/PrintConfig.cpp:1329 +msgid "" +"Disables retraction when the travel path does not exceed the upper layer's " +"perimeters (and thus any ooze will be probably invisible)." +msgstr "" +"Desativa a retração quando o caminho de viagem não excede os perímetros da " +"camada superior (e, portanto, qualquer escorrimento será provavelmente " +"invisível)." + +#: src/libslic3r/PrintConfig.cpp:1336 +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 "" +"Esta opção irá descartar a temperatura das extrusoras inativas para evitar a " +"escorrimento. Ele vai permitir uma saia alta automaticamente e mover " +"extrusoras fora de tal saia quando a mudança de temperatura." + +#: src/libslic3r/PrintConfig.cpp:1343 +msgid "Output filename format" +msgstr "Formato de nome de arquivo de saída" + +#: src/libslic3r/PrintConfig.cpp:1344 +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 "" +"Você pode usar todas as opções de config. como variáveis dentro deste " +"modelo. Por exemplo: [camada_altura], [densidade_preenchimento] etc. Você " +"também pode usar [tempo], [ano], [mês], [dia], [hora], [minuto], [segundo], " +"[versão], [nome_entrada], [nome_entrada_base]." + +#: src/libslic3r/PrintConfig.cpp:1353 +msgid "Detect bridging perimeters" +msgstr "Detectar perímetros de ponte" + +#: src/libslic3r/PrintConfig.cpp:1355 +msgid "" +"Experimental option to adjust flow for overhangs (bridge flow will be used), " +"to apply bridge speed to them and enable fan." +msgstr "" +"Opção experimental para ajustar o fluxo para angulações (o fluxo da ponte " +"será usado), para aplicar a velocidade da ponte a eles e para habilitar a " +"ventoinha." + +#: src/libslic3r/PrintConfig.cpp:1361 +msgid "Filament parking position" +msgstr "Posição de estacionamento do filamento" + +#: src/libslic3r/PrintConfig.cpp:1362 +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 "" +"Distância da ponta da extrusora da posição onde o filamento está estacionado " +"quando descarregado. Isso deve corresponder ao valor no firmware da " +"impressora." + +#: src/libslic3r/PrintConfig.cpp:1370 +msgid "Extra loading distance" +msgstr "Distância de carregamento extra" + +#: src/libslic3r/PrintConfig.cpp:1371 +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 "" +"Quando ajustado a zero, a distância que o filamento é movida da posição do " +"estacionamento durante a carga é exatamente a mesma que foi movida para trás " +"durante o descarregamento. Quando positivo, ele é carregado ainda mais, se " +"negativo, o movimento de carga é menor do que o descarregamento." + +#: src/libslic3r/PrintConfig.cpp:1379 src/libslic3r/PrintConfig.cpp:1397 +#: src/libslic3r/PrintConfig.cpp:1409 src/libslic3r/PrintConfig.cpp:1419 +msgid "Perimeters" +msgstr "Perímetros" + +#: src/libslic3r/PrintConfig.cpp:1380 +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 "" +"Esta é a aceleração que sua impressora usará para perímetros. Um alto valor " +"como 9000 geralmente dá bons resultados se o seu hardware suporta. Defina " +"zero para desabilitar o controle de aceleração para perímetros." + +#: src/libslic3r/PrintConfig.cpp:1388 +msgid "Perimeter extruder" +msgstr "Extrusora de perímetro" + +#: src/libslic3r/PrintConfig.cpp:1390 +msgid "" +"The extruder to use when printing perimeters and brim. First extruder is 1." +msgstr "" +"A extrusora para usar ao imprimir perímetros e aba. A primeira extrusora é 1." + +#: src/libslic3r/PrintConfig.cpp:1399 +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 "" +"Defina isso como um valor diferente de zero para definir uma largura de " +"extrusão manual para perímetros. Você pode querer usar extrusões mais finos " +"para obter superfícies mais precisas. Se for deixado zero, a largura de " +"extrusão padrão será usada se definido, caso contrário, 1,125 x diâmetro da " +"ponteira será usado. Se expresso em porcentagem(por exemplo, 200%) Ele será " +"calculado sobre a altura da camada." + +#: src/libslic3r/PrintConfig.cpp:1411 +msgid "" +"Speed for perimeters (contours, aka vertical shells). Set to zero for auto." +msgstr "" +"Velocidade para perímetros (contornos, também chamadas de perímetros " +"externos verticais). Defina como zero para auto." + +#: src/libslic3r/PrintConfig.cpp:1421 +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 "" +"Esta opção define o número de perímetros a gerar para cada camada. Observe " +"que o Slic3r pode aumentar esse número automaticamente quando detecta " +"superfícies inclinadas que se beneficiam de um número maior de perímetros se " +"a opção extra perímetros estiver habilitada." + +#: src/libslic3r/PrintConfig.cpp:1425 +msgid "(minimum)" +msgstr "(mínimo)" + +#: src/libslic3r/PrintConfig.cpp:1433 +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 "" +"Se você quiser processar o G-code de saída por meio de scripts " +"personalizados, basta listar seus caminhos absolutos aqui. Separe vários " +"scripts com um ponto-e-vírgula. Os scripts serão passados o caminho absoluto " +"para o arquivo de G-code como o primeiro argumento, e eles poderão acessar " +"as config. de config. do Slic3r lendo variáveis de ambiente." + +#: src/libslic3r/PrintConfig.cpp:1445 +msgid "Printer type" +msgstr "Tipo de impressora" + +#: src/libslic3r/PrintConfig.cpp:1446 +msgid "Type of the printer." +msgstr "Tipo da impressora." + +#: src/libslic3r/PrintConfig.cpp:1451 +msgid "Printer notes" +msgstr "Notas da impressora" + +#: src/libslic3r/PrintConfig.cpp:1452 +msgid "You can put your notes regarding the printer here." +msgstr "Você pode colocar suas anotações sobre a impressora aqui." + +#: src/libslic3r/PrintConfig.cpp:1460 +msgid "Printer vendor" +msgstr "Fornecedor da impressora" + +#: src/libslic3r/PrintConfig.cpp:1461 +msgid "Name of the printer vendor." +msgstr "Nome do fornecedor da impressora." + +#: src/libslic3r/PrintConfig.cpp:1466 +msgid "Printer variant" +msgstr "Variante da impressora" + +#: src/libslic3r/PrintConfig.cpp:1467 +msgid "" +"Name of the printer variant. For example, the printer variants may be " +"differentiated by a nozzle diameter." +msgstr "" +"Nome da variante da impressora. Por exemplo, as variantes da impressora " +"podem ser diferenciadas por um diâmetro da ponteira." + +#: src/libslic3r/PrintConfig.cpp:1480 +msgid "Raft layers" +msgstr "Camadas da estrado" + +#: src/libslic3r/PrintConfig.cpp:1482 +msgid "" +"The object will be raised by this number of layers, and support material " +"will be generated under it." +msgstr "" +"O objeto será elevado por este número de camadas, e o material de suporte " +"será gerado em baixo dele." + +#: src/libslic3r/PrintConfig.cpp:1490 +msgid "Resolution" +msgstr "Resolução" + +#: src/libslic3r/PrintConfig.cpp:1491 +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 "" +"Resolução de detalhes mínimos, usada para simplificar o arquivo de entrada " +"para acelerar o trabalho de fatiamento e reduzir o uso de memória. Modelos " +"de alta resolução geralmente carregam mais detalhes do que as impressoras " +"podem renderizar. Defina como zero para desabilitar qualquer simplificação e " +"usar a resolução completa da entrada." + +#: src/libslic3r/PrintConfig.cpp:1501 +msgid "Minimum travel after retraction" +msgstr "Retração em viagens acima de" + +#: src/libslic3r/PrintConfig.cpp:1502 +msgid "" +"Retraction is not triggered when travel moves are shorter than this length." +msgstr "" +"A retração não é acionada quando os movimentos de viagem são mais curtos que " +"esse comprimento." + +#: src/libslic3r/PrintConfig.cpp:1508 +msgid "Retract amount before wipe" +msgstr "Quantidade de retração antes da limpeza" + +#: src/libslic3r/PrintConfig.cpp:1509 +msgid "" +"With bowden extruders, it may be wise to do some amount of quick retract " +"before doing the wipe movement." +msgstr "" +"Com extrusoras Bowden, pode ser sábio fazer alguma quantidade de retração " +"rápida antes de fazer o movimento da limpeza." + +#: src/libslic3r/PrintConfig.cpp:1516 +msgid "Retract on layer change" +msgstr "Retrair na mudança de camada" + +#: src/libslic3r/PrintConfig.cpp:1517 +msgid "This flag enforces a retraction whenever a Z move is done." +msgstr "Este sinalizador impõe uma retração sempre que um movimento Z é feito." + +#: src/libslic3r/PrintConfig.cpp:1522 src/libslic3r/PrintConfig.cpp:1530 +msgid "Length" +msgstr "Comprimento" + +#: src/libslic3r/PrintConfig.cpp:1523 +msgid "Retraction Length" +msgstr "Comprimento de retração" + +#: src/libslic3r/PrintConfig.cpp:1524 +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 "" +"Quando a retração é acionada, o filamento é puxado para trás pela quantidade " +"especificada (o comprimento é medido em filamento cru, antes de entrar na " +"extrusora)." + +#: src/libslic3r/PrintConfig.cpp:1526 src/libslic3r/PrintConfig.cpp:1535 +msgid "mm (zero to disable)" +msgstr "mm (zero para desativar)" + +#: src/libslic3r/PrintConfig.cpp:1531 +msgid "Retraction Length (Toolchange)" +msgstr "Comprimento de retração (mudança de ferramenta)" + +#: src/libslic3r/PrintConfig.cpp:1532 +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 "" +"Quando a retração é acionada antes de mudar de ferramenta, o filamento é " +"puxado para trás pela quantidade especificada (o comprimento é medido em " +"filamento cru, antes de entrar na extrusora)." + +#: src/libslic3r/PrintConfig.cpp:1540 +msgid "Lift Z" +msgstr "Elevar Z" + +#: src/libslic3r/PrintConfig.cpp:1541 +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 "" +"Se você definir isso como um valor positivo, Z é rapidamente elevado sempre " +"que uma retração é acionada. Ao usar várias extrusoras, somente a config. " +"para a primeira extrusora será considerada." + +#: src/libslic3r/PrintConfig.cpp:1548 +msgid "Above Z" +msgstr "Acima de Z" + +#: src/libslic3r/PrintConfig.cpp:1549 +msgid "Only lift Z above" +msgstr "Apenas elevar Z acima" + +#: src/libslic3r/PrintConfig.cpp:1550 +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 "" +"Se você definir isso como um valor positivo, o levante do Z só ocorrerá " +"acima do Z absoluto especificado. Você pode ajustar essa config. para pular " +"o elevador nas primeiras camadas." + +#: src/libslic3r/PrintConfig.cpp:1557 +msgid "Below Z" +msgstr "Abaixo de Z" + +#: src/libslic3r/PrintConfig.cpp:1558 +msgid "Only lift Z below" +msgstr "Apenas elevar Z abaixo" + +#: src/libslic3r/PrintConfig.cpp:1559 +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 "" +"Se você definir isso como um valor positivo, o levante do Z só ocorrerá " +"abaixo do Z absoluto especificado. Você pode ajustar essa config. para " +"limitar a elevação às primeiras camadas." + +#: src/libslic3r/PrintConfig.cpp:1567 src/libslic3r/PrintConfig.cpp:1575 +msgid "Extra length on restart" +msgstr "Comprimento extra no reinício" + +#: src/libslic3r/PrintConfig.cpp:1568 +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 "" +"Quando a retração é compensada após o movimento de viagem, a extrusora vai " +"empurrar esta quantidade adicional de filamento. Essa config. raramente é " +"necessária." + +#: src/libslic3r/PrintConfig.cpp:1576 +msgid "" +"When the retraction is compensated after changing tool, the extruder will " +"push this additional amount of filament." +msgstr "" +"Quando a retração é compensada após a ferramenta de mudança, a extrusora " +"empurrará esta quantidade adicional de filamento." + +#: src/libslic3r/PrintConfig.cpp:1583 src/libslic3r/PrintConfig.cpp:1584 +msgid "Retraction Speed" +msgstr "Velocidade da retração" + +#: src/libslic3r/PrintConfig.cpp:1585 +msgid "The speed for retractions (it only applies to the extruder motor)." +msgstr "A velocidade para retrações (aplica-se somente ao motor da extrusora)." + +#: src/libslic3r/PrintConfig.cpp:1591 src/libslic3r/PrintConfig.cpp:1592 +msgid "Deretraction Speed" +msgstr "Velocidade de retorno de retração" + +#: src/libslic3r/PrintConfig.cpp:1593 +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 "" +"A velocidade para o carregamento de um filamento na extrusora após a " +"retração (aplica-se somente ao motor da extrusora). Se deixada como zero, a " +"velocidade de retração é usada." + +#: src/libslic3r/PrintConfig.cpp:1600 +msgid "Seam position" +msgstr "Posição da costura" + +#: src/libslic3r/PrintConfig.cpp:1602 +msgid "Position of perimeters starting points." +msgstr "Posição inicial dos pontos do perímetro." + +#: src/libslic3r/PrintConfig.cpp:1608 +msgid "Random" +msgstr "Aleatório" + +#: src/libslic3r/PrintConfig.cpp:1609 +msgid "Nearest" +msgstr "Próximo" + +#: src/libslic3r/PrintConfig.cpp:1610 +msgid "Aligned" +msgstr "Alinhado(a)" + +#: src/libslic3r/PrintConfig.cpp:1618 +msgid "Direction" +msgstr "Direção" + +#: src/libslic3r/PrintConfig.cpp:1620 +msgid "Preferred direction of the seam" +msgstr "Direção preferida da costura" + +#: src/libslic3r/PrintConfig.cpp:1621 +msgid "Seam preferred direction" +msgstr "Direção de preferência da costura" + +#: src/libslic3r/PrintConfig.cpp:1628 +msgid "Jitter" +msgstr "Jitter" + +#: src/libslic3r/PrintConfig.cpp:1630 +msgid "Seam preferred direction jitter" +msgstr "Direção da costura preferencial para Jitter" + +#: src/libslic3r/PrintConfig.cpp:1631 +msgid "Preferred direction of the seam - jitter" +msgstr "Direção preferida da costura-jitter" + +#: src/libslic3r/PrintConfig.cpp:1641 +msgid "USB/serial port for printer connection." +msgstr "USB/porta serial para conexão da impressora." + +#: src/libslic3r/PrintConfig.cpp:1648 +msgid "Serial port speed" +msgstr "Velocidade da porta serial" + +#: src/libslic3r/PrintConfig.cpp:1649 +msgid "Speed (baud) of USB/serial port for printer connection." +msgstr "Velocidade (baud) do USB/porta serial para conexão da impressora." + +#: src/libslic3r/PrintConfig.cpp:1658 +msgid "Distance from object" +msgstr "Distância do objeto" + +#: src/libslic3r/PrintConfig.cpp:1659 +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 "" +"Distância entre a saia e o objeto (s). Defina isso como zero para anexar a " +"saia para o objeto (s) e obter uma aba para uma melhor aderência." + +#: src/libslic3r/PrintConfig.cpp:1666 +msgid "Skirt height" +msgstr "Altura da saia" + +#: src/libslic3r/PrintConfig.cpp:1667 +msgid "" +"Height of skirt expressed in layers. Set this to a tall value to use skirt " +"as a shield against drafts." +msgstr "" +"Altura da saia expressa em camadas. Defina isso como um valor alto para usar " +"a saia como um escudo contra rascunhos." + +#: src/libslic3r/PrintConfig.cpp:1674 +msgid "Loops (minimum)" +msgstr "Voltas (mínimo)" + +#: src/libslic3r/PrintConfig.cpp:1675 +msgid "Skirt Loops" +msgstr "Voltas de saia" + +#: src/libslic3r/PrintConfig.cpp:1676 +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 "" +"Número de voltas para a saia. Se a opção comprimento mínimo de extrusão " +"estiver definida, o número de voltas pode ser maior do que aquele " +"configurado aqui. Defina isso como zero para desabilitar a saia " +"completamente." + +#: src/libslic3r/PrintConfig.cpp:1684 +msgid "Slow down if layer print time is below" +msgstr "Diminuir a velocidade quando o tempo de impressão for menor que" + +#: src/libslic3r/PrintConfig.cpp:1685 +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 "" +"Se o tempo de impressão da camada for estimado abaixo desse número de " +"segundos, a velocidade de impressão será reduzida para estender a duração a " +"esse valor." + +#: src/libslic3r/PrintConfig.cpp:1695 +msgid "Small perimeters" +msgstr "Perímetro pequeno" + +#: src/libslic3r/PrintConfig.cpp:1697 +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 "" +"Este ajuste separado afetará a velocidade dos perímetros que têm o raio < = " +"6.5 mm (geralmente furos). Se expresso em porcentagem(por exemplo: 80%) Ele " +"será calculado sobre a velocidade de perímetros configurados acima. Defina " +"como zero para auto." + +#: src/libslic3r/PrintConfig.cpp:1707 +msgid "Solid infill threshold area" +msgstr "Área de limiar de preenchimento sólido" + +#: src/libslic3r/PrintConfig.cpp:1709 +msgid "" +"Force solid infill for regions having a smaller area than the specified " +"threshold." +msgstr "" +"Forçar preenchimento sólido para regiões com uma área menor do que o limite " +"especificado." + +#: src/libslic3r/PrintConfig.cpp:1710 +msgid "mm²" +msgstr "mm²" + +#: src/libslic3r/PrintConfig.cpp:1716 +msgid "Solid infill extruder" +msgstr "Extrusora de preenchimento sólido" + +#: src/libslic3r/PrintConfig.cpp:1718 +msgid "The extruder to use when printing solid infill." +msgstr "" +"A extrusora a ser utilizada quando estiver imprimindo preenchimento sólido." + +#: src/libslic3r/PrintConfig.cpp:1724 +msgid "Solid infill every" +msgstr "Preenchimento sólido a cada" + +#: src/libslic3r/PrintConfig.cpp:1726 +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 "" +"Este recurso permite forçar uma camada sólida a cada número determinado de " +"camadas. Zero para desabilitar. Você pode definir isso para qualquer valor " +"(por exemplo 9999); Slic3r escolherá automaticamente o número máximo " +"possível de camadas para combinar de acordo com o diâmetro da ponteira e a " +"altura da camada." + +#: src/libslic3r/PrintConfig.cpp:1738 +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 "" +"Defina isso como um valor diferente de zero para definir uma largura de " +"extrusão manual para preenchimento de superfícies sólidas. Se for deixado " +"zero, a largura de extrusão padrão será usada se definido, caso contrário, " +"1,125 x diâmetro da ponteira será usado. Se expresso em porcentagem(por " +"exemplo, 90%) Ele será calculado sobre a altura da camada." + +#: src/libslic3r/PrintConfig.cpp:1748 +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 "" +"Velocidade para imprimir regiões sólidas (topo/fundo/perímetros externos " +"horizontais internas). Isto pode ser expresso em porcentagem(por exemplo: " +"80%) sobre a velocidade de preenchimento padrão acima. Defina como zero para " +"auto." + +#: src/libslic3r/PrintConfig.cpp:1760 +msgid "Number of solid layers to generate on top and bottom surfaces." +msgstr "" +"Número de camadas sólidas a serem geradas nas interfaces do topo e base." + +#: src/libslic3r/PrintConfig.cpp:1766 +msgid "Spiral vase" +msgstr "Vaso espiral" + +#: src/libslic3r/PrintConfig.cpp:1767 +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 "" +"Este recurso irá elevar Z gradualmente durante a impressão de um objeto de " +"parede única, a fim de remover qualquer costura visível. Esta opção exige um " +"único perímetro, nenhum preenchimento, nenhumas camadas contínuas superiores " +"e nenhum material de sustentação. Você ainda pode definir qualquer número de " +"camadas sólidas de fundo, bem como saia/aba voltas. Ele não funcionará ao " +"imprimir mais de um objeto." + +#: src/libslic3r/PrintConfig.cpp:1775 +msgid "Temperature variation" +msgstr "Variação de temperatura" + +#: src/libslic3r/PrintConfig.cpp:1776 +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 "" +"Diferença de temperatura a ser aplicada quando uma extrusora não está ativa. " +"Permite uma saia \"sacrificial\" em que as ponteiras são limpadas " +"periodicamente." + +#: src/libslic3r/PrintConfig.cpp:1786 +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 "" +"Este procedimento do começo é introduzido no início, depois que a mesa " +"alcançou a temperatura alvo e a extrusora apenas começou o aquecimento, e " +"antes que a extrusora terminasse o aquecimento. Se PrusaSlicer detecta M104 " +"ou M190 em seus códigos personalizados, esses comandos não serão precedidos " +"automaticamente para que você esteja livre para personalizar a ordem dos " +"comandos de aquecimento e outras ações personalizadas. Observe que você pode " +"usar variáveis de espaço reservado para todas as config. de PrusaSlicer, " +"para que você possa colocar um comando \"M109 S " +"[temperatura_primeira_camada]\" onde quiser." + +#: src/libslic3r/PrintConfig.cpp:1801 +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 "" +"Este procedimento de início é inserido no começo, depois de qualquer " +"impressora iniciar Gcode (e depois de qualquer troca de ferramenta para este " +"filamento em caso de impressoras de vários materiais). Isso é usado para " +"substituir as config. de um filamento específico. Se PrusaSlicer detecta " +"M104, M109, M140 ou M190 em seus códigos personalizados, esses comandos não " +"serão precedidos automaticamente para que você esteja livre para " +"personalizar a ordem dos comandos de aquecimento e outras ações " +"personalizadas. Observe que você pode usar variáveis de espaço reservado " +"para todas as config. de PrusaSlicer, para que você possa colocar um comando " +"\"M109 S [temperatura_primeira_camada]\" onde quiser. Se você tiver várias " +"extrusoras, o Gcode é processado em ordem de extrusora." + +#: src/libslic3r/PrintConfig.cpp:1817 +msgid "Single Extruder Multi Material" +msgstr "Única extrusora multi material" + +#: src/libslic3r/PrintConfig.cpp:1818 +msgid "The printer multiplexes filaments into a single hot end." +msgstr "A impressora multiplexes filamentos em uma única extremidade quente." + +#: src/libslic3r/PrintConfig.cpp:1823 +msgid "Prime all printing extruders" +msgstr "Extrusar todas as extrusoras de impressão" + +#: src/libslic3r/PrintConfig.cpp:1824 +msgid "" +"If enabled, all printing extruders will be primed at the front edge of the " +"print bed at the start of the print." +msgstr "" +"Se ativada, todas as extrusoras de impressão extrusarão na aba dianteira da " +"mesa de impressão no início da impressão." + +#: src/libslic3r/PrintConfig.cpp:1829 +msgid "Generate support material" +msgstr "Gerar material de suporte" + +#: src/libslic3r/PrintConfig.cpp:1831 +msgid "Enable support material generation." +msgstr "Habilitar geração de material de suporte." + +#: src/libslic3r/PrintConfig.cpp:1835 +msgid "Auto generated supports" +msgstr "Gerar suportes automaticamente" + +#: src/libslic3r/PrintConfig.cpp:1837 +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 "" +"Se marcada, os suportes serão gerados automaticamente com base no valor do " +"limite de angulação. Se desmarcada, as sustentações serão geradas dentro dos " +"volumes do \"reforçador de suporte\" somente." + +#: src/libslic3r/PrintConfig.cpp:1843 +msgid "XY separation between an object and its support" +msgstr "Separação entre o objeto e seu suporte em XY" + +#: src/libslic3r/PrintConfig.cpp:1845 +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 "" +"Separação entre o objeto e seu suporte em XY. Se expresso como porcentagem " +"(por exemplo, 50%), será calculado com base na espessura do perímetro " +"externo." + +#: src/libslic3r/PrintConfig.cpp:1855 +msgid "Pattern angle" +msgstr "Ângulo do padrão" + +#: src/libslic3r/PrintConfig.cpp:1857 +msgid "" +"Use this setting to rotate the support material pattern on the horizontal " +"plane." +msgstr "" +"Use essa config. para girar o padrão de material de suporte no plano " +"horizontal." + +#: src/libslic3r/PrintConfig.cpp:1867 src/libslic3r/PrintConfig.cpp:2563 +msgid "" +"Only create support if it lies on a build plate. Don't create support on a " +"print." +msgstr "" +"Apenas criar suporte se ele está em uma mesa. Não crie suporte em uma " +"impressão." + +#: src/libslic3r/PrintConfig.cpp:1873 +msgid "Contact Z distance" +msgstr "Distância de contato Z" + +#: src/libslic3r/PrintConfig.cpp:1875 +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 "" +"A distância vertical entre o objeto e a interface de material de suporte. " +"Definir isso como 0 também impedirá Slic3r de usar o fluxo de ponte e a " +"velocidade para a primeira camada de objeto." + +#: src/libslic3r/PrintConfig.cpp:1882 +msgid "0 (soluble)" +msgstr "0 (solúvel)" + +#: src/libslic3r/PrintConfig.cpp:1883 +msgid "0.2 (detachable)" +msgstr "0.2 (destacável)" + +#: src/libslic3r/PrintConfig.cpp:1888 +msgid "Enforce support for the first" +msgstr "Reforçar suportes para a(s) primeira(s)" + +#: src/libslic3r/PrintConfig.cpp:1890 +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 "" +"Gere material de suporte para o número especificado de camadas que contam da " +"parte inferior, independentemente de o material de suporte normal estar " +"ativado ou não e independentemente de qualquer limite de ângulo. Isso é útil " +"para obter mais aderência de objetos com uma pegada muito fina ou fraca na " +"placa de construção." + +#: src/libslic3r/PrintConfig.cpp:1895 +msgid "Enforce support for the first n layers" +msgstr "Reforçar suportes na(s) primera(s) n camada(s)" + +#: src/libslic3r/PrintConfig.cpp:1901 +msgid "Support material/raft/skirt extruder" +msgstr "Extrusora de material de suporte/estrado/saia" + +#: src/libslic3r/PrintConfig.cpp:1903 +msgid "" +"The extruder to use when printing support material, raft and skirt (1+, 0 to " +"use the current extruder to minimize tool changes)." +msgstr "" +"A extrusora a ser usada ao imprimir material de suporte, estrado e saia (1 " +"+, 0 para usar a extrusora atual para minimizar as mudanças na ferramenta)." + +#: src/libslic3r/PrintConfig.cpp:1912 +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 "" +"Defina isso como um valor diferente de zero para definir uma largura de " +"extrusão manual para material de suporte. Se deixada em zero, a largura " +"padrão da extrusão será usada, se não o diâmetro da ponteira será usado. Se " +"expresso em porcentagem(por exemplo, 90%) Ele será calculado sobre a altura " +"da camada." + +#: src/libslic3r/PrintConfig.cpp:1920 +msgid "Interface loops" +msgstr "Voltas da interface" + +#: src/libslic3r/PrintConfig.cpp:1922 +msgid "" +"Cover the top contact layer of the supports with loops. Disabled by default." +msgstr "" +"Cubra a camada de contato superior dos suportes com laços. Desativado por " +"padrão." + +#: src/libslic3r/PrintConfig.cpp:1927 +msgid "Support material/raft interface extruder" +msgstr "Extrusora de material de suporte/estrado" + +#: src/libslic3r/PrintConfig.cpp:1929 +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 "" +"A extrusora para usar ao imprimir a relação material do apoio (1 +, 0 para " +"usar o extrusor atual para minimizar mudanças da ferramenta). Isso afeta o " +"estrado também." + +#: src/libslic3r/PrintConfig.cpp:1936 +msgid "Interface layers" +msgstr "Camadas de interface" + +#: src/libslic3r/PrintConfig.cpp:1938 +msgid "" +"Number of interface layers to insert between the object(s) and support " +"material." +msgstr "" +"Número de camadas de interface para inserir entre o objeto(s) e material de " +"suporte." + +#: src/libslic3r/PrintConfig.cpp:1945 +msgid "Interface pattern spacing" +msgstr "Espaçamento do padrão da interface" + +#: src/libslic3r/PrintConfig.cpp:1947 +msgid "Spacing between interface lines. Set zero to get a solid interface." +msgstr "" +"Espaçamento entre as linhas de interface. Defina zero para obter uma " +"interface sólida." + +#: src/libslic3r/PrintConfig.cpp:1956 +msgid "" +"Speed for printing support material interface layers. If expressed as " +"percentage (for example 50%) it will be calculated over support material " +"speed." +msgstr "" +"Velocidade para camadas de interface de material de suporte de impressão. Se " +"expresso em porcentagem(por exemplo, 50%) Ele será calculado sobre a " +"velocidade do material de suporte." + +#: src/libslic3r/PrintConfig.cpp:1965 +msgid "Pattern" +msgstr "Padrão" + +#: src/libslic3r/PrintConfig.cpp:1967 +msgid "Pattern used to generate support material." +msgstr "Padrão usado para gerar material de suporte." + +#: src/libslic3r/PrintConfig.cpp:1973 +msgid "Rectilinear grid" +msgstr "Grade rectilínea" + +#: src/libslic3r/PrintConfig.cpp:1979 +msgid "Pattern spacing" +msgstr "Padrão de espaçamento" + +#: src/libslic3r/PrintConfig.cpp:1981 +msgid "Spacing between support material lines." +msgstr "Espaçamento entre linhas de material de suporte." + +#: src/libslic3r/PrintConfig.cpp:1990 +msgid "Speed for printing support material." +msgstr "Velocidade para imprimir material de suporte." + +#: src/libslic3r/PrintConfig.cpp:1997 +msgid "Synchronize with object layers" +msgstr "Sincronizar com camadas de objeto" + +#: src/libslic3r/PrintConfig.cpp:1999 +msgid "" +"Synchronize support layers with the object print layers. This is useful with " +"multi-material printers, where the extruder switch is expensive." +msgstr "" +"Sincronize camadas de suporte com as camadas de impressão do objeto. Isto é " +"útil com as impressoras do multi-material, onde o interruptor da extrusora é " +"caro." + +#: src/libslic3r/PrintConfig.cpp:2005 +msgid "Overhang threshold" +msgstr "Limite de angulação" + +#: src/libslic3r/PrintConfig.cpp:2007 +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 "" +"O material de suporte não será gerado para angulações cujo ângulo de " +"inclinação (90 ° = vertical) esteja acima do limite determinado. Em outras " +"palavras, esse valor representa a inclinação mais horizontal (medida a " +"partir do plano horizontal) que você pode imprimir sem material de suporte. " +"Defina como zero para detecção automática (recomendado)." + +#: src/libslic3r/PrintConfig.cpp:2019 +msgid "With sheath around the support" +msgstr "Com bainha em torno do apoio" + +#: src/libslic3r/PrintConfig.cpp:2021 +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 "" +"Adicione uma bainha (uma única linha de perímetro) em torno do suporte base. " +"Isso torna o suporte mais confiável, mas também mais difícil de remover." + +#: src/libslic3r/PrintConfig.cpp:2028 +msgid "" +"Extruder temperature for layers after the first one. Set this to zero to " +"disable temperature control commands in the output." +msgstr "" +"Temperatura da extrusora para camadas após a primeira. Defina como zero para " +"desabilitar os comandos de controle de temperatura na saída." + +#: src/libslic3r/PrintConfig.cpp:2036 +msgid "Detect thin walls" +msgstr "Detectar paredes finas" + +#: src/libslic3r/PrintConfig.cpp:2038 +msgid "" +"Detect single-width walls (parts where two extrusions don't fit and we need " +"to collapse them into a single trace)." +msgstr "" +"Detecte paredes de largura única (partes onde duas extrusões não cabem e " +"precisamos recolhê-las em um único traço)." + +#: src/libslic3r/PrintConfig.cpp:2044 +msgid "Threads" +msgstr "Roscas" + +#: src/libslic3r/PrintConfig.cpp:2045 +msgid "" +"Threads are used to parallelize long-running tasks. Optimal threads number " +"is slightly above the number of available cores/processors." +msgstr "" +"Tópicos são usados para paralelizar tarefas de execução demorada. O número " +"de tópicos ideais está ligeiramente acima do número de núcleos/processadores " +"disponíveis." + +#: src/libslic3r/PrintConfig.cpp:2057 +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 "" +"Este código personalizado é inserido antes de cada troca de ferramenta. " +"Variáveis de espaço reservado para todas as config. de PrusaSlicer, bem como " +"{previous_extruder} e {next_extruder} podem ser usadas. Quando um comando de " +"mudança de ferramenta que muda para a extrusora correta está incluído (como " +"T {next_extruder}), PrusaSlicer emitirá nenhum outro comando tal. Portanto, " +"é possível script comportamento personalizado antes e depois da mudança de " +"ferramenta." + +#: src/libslic3r/PrintConfig.cpp:2070 +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 "" +"Defina isso como um valor diferente de zero para definir uma largura de " +"extrusão manual para preenchimento para superfícies superiores. Você pode " +"querer usar extrusões mais finos para preencher todas as regiões estreitas e " +"obter um acabamento mais suave. Se a esquerda zero, a largura padrão da " +"extrusão será usada se ajustado, se não o diâmetro da ponteira será usado. " +"Se expresso em porcentagem(por exemplo, 90%) Ele será calculado sobre a " +"altura da camada." + +#: src/libslic3r/PrintConfig.cpp:2081 +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 "" +"Velocidade para imprimir camadas sólidas superiores (só se aplica às camadas " +"externas superiores e não às suas camadas sólidas internas). Você pode " +"querer diminuir este para ter um revestimento de superfície mais agradável. " +"Isto pode ser expresso em porcentagem(por exemplo: 80%) sobre a velocidade " +"de preenchimento sólido acima. Defina como zero para auto." + +#: src/libslic3r/PrintConfig.cpp:2096 +msgid "Number of solid layers to generate on top surfaces." +msgstr "Número de camadas sólidas para gerar em superfícies superiores." + +#: src/libslic3r/PrintConfig.cpp:2097 +msgid "Top solid layers" +msgstr "Camadas sólidas de topo" + +#: src/libslic3r/PrintConfig.cpp:2103 +msgid "Speed for travel moves (jumps between distant extrusion points)." +msgstr "" +"Velocidade para movimentos de viagem (saltos entre pontos de extrusão " +"distantes)." + +#: src/libslic3r/PrintConfig.cpp:2111 +msgid "Use firmware retraction" +msgstr "Usar retração do firmware" + +#: src/libslic3r/PrintConfig.cpp:2112 +msgid "" +"This experimental setting uses G10 and G11 commands to have the firmware " +"handle the retraction. This is only supported in recent Marlin." +msgstr "" +"Esta config. experimental usa comandos G10 e G11 para que o firmware " +"manipule a retração. Isso só é suportado no recente Marlin." + +#: src/libslic3r/PrintConfig.cpp:2118 +msgid "Use relative E distances" +msgstr "Utilizar distâncias relativas do E" + +#: src/libslic3r/PrintConfig.cpp:2119 +msgid "" +"If your firmware requires relative E values, check this, otherwise leave it " +"unchecked. Most firmwares use absolute values." +msgstr "" +"Se o firmware necessitar de valores relativos E, verifique isto, caso " +"contrário, deixe-o desmarcado. A maioria dos firmwares usa valores absolutos." + +#: src/libslic3r/PrintConfig.cpp:2125 +msgid "Use volumetric E" +msgstr "Usar E volumétrico" + +#: src/libslic3r/PrintConfig.cpp:2126 +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 "" +"Essa config. experimental usa saídas os valores E em milímetros cúbicos em " +"vez de milímetros lineares. Se o firmware já não souber o diâmetro (s) do " +"filamento, você pode colocar comandos como ' m 200 D [filament_diameter_0] " +"T0 ' no seu G-code inicial para ativar o modo volumétrico e usar o diâmetro " +"do filamento associado ao filamento selecionado em Slic3r. Isso só é " +"suportado no recente Marlin." + +#: src/libslic3r/PrintConfig.cpp:2136 +msgid "Enable variable layer height feature" +msgstr "Habilitar altura de camada variável" + +#: src/libslic3r/PrintConfig.cpp:2137 +msgid "" +"Some printers or printer setups may have difficulties printing with a " +"variable layer height. Enabled by default." +msgstr "" +"Algumas impressoras ou config. de impressora podem ter dificuldades para " +"imprimir com uma altura de camada variável. Ativado por padrão." + +#: src/libslic3r/PrintConfig.cpp:2143 +msgid "Wipe while retracting" +msgstr "Limpe durante a retração" + +#: src/libslic3r/PrintConfig.cpp:2144 +msgid "" +"This flag will move the nozzle while retracting to minimize the possible " +"blob on leaky extruders." +msgstr "" +"Esta bandeira moverá a ponteira ao retrair para minimizar a bolha possível " +"em extrusoras vazando." + +#: src/libslic3r/PrintConfig.cpp:2151 +msgid "" +"Multi material printers may need to prime or purge extruders on tool " +"changes. Extrude the excess material into the wipe tower." +msgstr "" +"Várias impressoras de multi-material podem precisar purgar extrusoras em " +"alterações de ferramenta. EXTRUDE o excesso de material para a torre de " +"limpeza." + +#: src/libslic3r/PrintConfig.cpp:2157 +msgid "Purging volumes - load/unload volumes" +msgstr "Volumes de purga-volumes de carga/descarregamento" + +#: src/libslic3r/PrintConfig.cpp:2158 +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 "" +"Este vetor salva os volumes necessários para mudar de/para cada ferramenta " +"usada na torre de limpeza. Esses valores são usados para simplificar a " +"criação dos volumes de purga completos abaixo." + +#: src/libslic3r/PrintConfig.cpp:2164 +msgid "Purging volumes - matrix" +msgstr "Volumes de purga-matriz" + +#: src/libslic3r/PrintConfig.cpp:2165 +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 "" +"Esta matriz descreve volumes (em milimetros cúbicos) necessários para limpar " +"o novo filamento na torre de limpeza para qualquer dado par de ferramentas." + +#: src/libslic3r/PrintConfig.cpp:2174 +msgid "Position X" +msgstr "Posição X" + +#: src/libslic3r/PrintConfig.cpp:2175 +msgid "X coordinate of the left front corner of a wipe tower" +msgstr "Coordenada X do canto frontal esquerdo de uma torre de limpeza" + +#: src/libslic3r/PrintConfig.cpp:2181 +msgid "Position Y" +msgstr "Posição Y" + +#: src/libslic3r/PrintConfig.cpp:2182 +msgid "Y coordinate of the left front corner of a wipe tower" +msgstr "Coordenada Y do canto dianteiro esquerdo de uma torre de limpeza" + +#: src/libslic3r/PrintConfig.cpp:2189 +msgid "Width of a wipe tower" +msgstr "Largura de uma torre da limpeza" + +#: src/libslic3r/PrintConfig.cpp:2195 +msgid "Wipe tower rotation angle" +msgstr "Ângulo de rotação da torre" + +#: src/libslic3r/PrintConfig.cpp:2196 +msgid "Wipe tower rotation angle with respect to x-axis." +msgstr "Ângulo de rotação da torre de limpeza em relação ao eixo X." + +#: src/libslic3r/PrintConfig.cpp:2203 +msgid "Wipe into this object's infill" +msgstr "Limpe no preenchimento deste objeto" + +#: src/libslic3r/PrintConfig.cpp:2204 +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 "" +"Purga após troca de ferramenta será feito dentro de preenchimentos deste " +"objeto. Isso diminui a quantidade de resíduos, mas pode resultar em tempo de " +"impressão mais longo devido a movimentos de viagem adicionais." + +#: src/libslic3r/PrintConfig.cpp:2211 +msgid "Wipe into this object" +msgstr "Limpar neste objeto" + +#: src/libslic3r/PrintConfig.cpp:2212 +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 "" +"Objeto será usado para limpar o bico após uma troca de ferramenta para " +"salvar o material que de outra forma acabaria na torre de limpeza e diminuir " +"o tempo de impressão. As cores dos objetos serão misturadas como resultado." + +#: src/libslic3r/PrintConfig.cpp:2218 +msgid "Maximal bridging distance" +msgstr "Distância de ponte máxima" + +#: src/libslic3r/PrintConfig.cpp:2219 +msgid "Maximal distance between supports on sparse infill sections." +msgstr "" +"Distância máxima entre as sustentações em seções preenchimento esparsas." + +#: src/libslic3r/PrintConfig.cpp:2225 +msgid "XY Size Compensation" +msgstr "Compensação de tamanho em XY" + +#: src/libslic3r/PrintConfig.cpp:2227 +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 "" +"O objeto será aumentado/encolhido no plano XY pelo valor configurado " +"(negativo = para dentro, positivo = para fora). Isso pode ser útil para " +"ajustar os tamanhos dos furos." + +#: src/libslic3r/PrintConfig.cpp:2235 +msgid "Z offset" +msgstr "Compensamento do Z" + +#: src/libslic3r/PrintConfig.cpp:2236 +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 "" +"Esse valor será adicionado (ou subtraído) de todas as coordenadas Z no G-" +"code de saída. Ele é usado para compensar a posição de final de curso Z " +"ruim: por exemplo, se o seu final de curso zero realmente deixa o bico 0.3 " +"mm longe da mesa de impressão, defina este para-0,3 (ou corrigir o seu final " +"de curso)." + +#: src/libslic3r/PrintConfig.cpp:2294 +msgid "Display width" +msgstr "Largura do display" + +#: src/libslic3r/PrintConfig.cpp:2295 +msgid "Width of the display" +msgstr "Largura do display" + +#: src/libslic3r/PrintConfig.cpp:2300 +msgid "Display height" +msgstr "Altura do display" + +#: src/libslic3r/PrintConfig.cpp:2301 +msgid "Height of the display" +msgstr "Altura do display" + +#: src/libslic3r/PrintConfig.cpp:2306 +msgid "Number of pixels in" +msgstr "Número de pixels em" + +#: src/libslic3r/PrintConfig.cpp:2308 +msgid "Number of pixels in X" +msgstr "Número de pixels em X" + +#: src/libslic3r/PrintConfig.cpp:2314 +msgid "Number of pixels in Y" +msgstr "Número de pixels em Y" + +#: src/libslic3r/PrintConfig.cpp:2319 +msgid "Display horizontal mirroring" +msgstr "Exibir espelhamento horizontal" + +#: src/libslic3r/PrintConfig.cpp:2320 +msgid "Mirror horizontally" +msgstr "Espelhar horizontalmente" + +#: src/libslic3r/PrintConfig.cpp:2321 +msgid "Enable horizontal mirroring of output images" +msgstr "Habilitar espelhamento horizontal de imagens de saída" + +#: src/libslic3r/PrintConfig.cpp:2326 +msgid "Display vertical mirroring" +msgstr "Exibir espelhamento vertical" + +#: src/libslic3r/PrintConfig.cpp:2327 +msgid "Mirror vertically" +msgstr "Espelharvertical" + +#: src/libslic3r/PrintConfig.cpp:2328 +msgid "Enable vertical mirroring of output images" +msgstr "Habilitar espelhamento vertical de imagens de saída" + +#: src/libslic3r/PrintConfig.cpp:2333 +msgid "Display orientation" +msgstr "Orientação do display" + +#: src/libslic3r/PrintConfig.cpp:2334 +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 "" +"Defina a orientação real do visor LCD dentro da impressora SLA. O modo " +"retrato inverterá o significado dos parâmetros de largura e altura da tela e " +"as imagens de saída serão giradas por 90 graus." + +#: src/libslic3r/PrintConfig.cpp:2340 +msgid "Landscape" +msgstr "Paisagem" + +#: src/libslic3r/PrintConfig.cpp:2341 +msgid "Portrait" +msgstr "Retrato" + +#: src/libslic3r/PrintConfig.cpp:2346 +msgid "Fast" +msgstr "Rápido" + +#: src/libslic3r/PrintConfig.cpp:2347 +msgid "Fast tilt" +msgstr "Inclinação rápida" + +#: src/libslic3r/PrintConfig.cpp:2348 +msgid "Time of the fast tilt" +msgstr "Tempo da inclinação rápida" + +#: src/libslic3r/PrintConfig.cpp:2355 +msgid "Slow" +msgstr "Lento" + +#: src/libslic3r/PrintConfig.cpp:2356 +msgid "Slow tilt" +msgstr "Inclinação lenta" + +#: src/libslic3r/PrintConfig.cpp:2357 +msgid "Time of the slow tilt" +msgstr "Tempo da inclinação lenta" + +#: src/libslic3r/PrintConfig.cpp:2364 +msgid "Area fill" +msgstr "Preenchimento de área" + +#: src/libslic3r/PrintConfig.cpp:2365 +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 "" +"A porcentagem da área de mesa. \n" +"Se a área de impressão exceder o valor especificado, \n" +"em seguida, uma inclinação lenta será usada, caso contrário-uma inclinação " +"rápida" + +#: src/libslic3r/PrintConfig.cpp:2372 src/libslic3r/PrintConfig.cpp:2373 +#: src/libslic3r/PrintConfig.cpp:2374 +msgid "Printer scaling correction" +msgstr "Correção de dimensionamento da impressora" + +#: src/libslic3r/PrintConfig.cpp:2380 src/libslic3r/PrintConfig.cpp:2381 +msgid "Printer absolute correction" +msgstr "Correção absoluta da impressora" + +#: src/libslic3r/PrintConfig.cpp:2382 +msgid "" +"Will inflate or deflate the sliced 2D polygons according to the sign of the " +"correction." +msgstr "" +"Irá inflar ou esvaziar os polígonos 2D cortados de acordo com o sinal da " +"correção." + +#: src/libslic3r/PrintConfig.cpp:2388 src/libslic3r/PrintConfig.cpp:2389 +msgid "Printer gamma correction" +msgstr "Correção de gama de impressora" + +#: src/libslic3r/PrintConfig.cpp:2390 +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 "" +"Isso aplicará uma correção de gama para os polígonos 2D rasterizados. Um " +"valor gama de zero significa limiarização com o limiar no meio. Este " +"comportamento elimina suavização sem perder buracos em polígonos." + +#: src/libslic3r/PrintConfig.cpp:2401 src/libslic3r/PrintConfig.cpp:2402 +msgid "Initial layer height" +msgstr "Altura da camada inicial" + +#: src/libslic3r/PrintConfig.cpp:2408 +msgid "Faded layers" +msgstr "Camadas desbotadas" + +#: src/libslic3r/PrintConfig.cpp:2409 +msgid "" +"Number of the layers needed for the exposure time fade from initial exposure " +"time to the exposure time" +msgstr "" +"Número de camadas necessárias para o tempo de exposição desvanecer-se do " +"tempo de exposição inicial ao tempo de exposição" + +#: src/libslic3r/PrintConfig.cpp:2416 src/libslic3r/PrintConfig.cpp:2417 +msgid "Minimum exposure time" +msgstr "Tempo mínimo de exposição" + +#: src/libslic3r/PrintConfig.cpp:2424 src/libslic3r/PrintConfig.cpp:2425 +msgid "Maximum exposure time" +msgstr "Tempo máximo de exposição" + +#: src/libslic3r/PrintConfig.cpp:2432 src/libslic3r/PrintConfig.cpp:2433 +msgid "Exposure time" +msgstr "Tempo de exposição" + +#: src/libslic3r/PrintConfig.cpp:2439 src/libslic3r/PrintConfig.cpp:2440 +msgid "Minimum initial exposure time" +msgstr "Tempo inicial mínimo de exposição" + +#: src/libslic3r/PrintConfig.cpp:2447 src/libslic3r/PrintConfig.cpp:2448 +msgid "Maximum initial exposure time" +msgstr "Tempo inicial máximo de exposição" + +#: src/libslic3r/PrintConfig.cpp:2455 src/libslic3r/PrintConfig.cpp:2456 +msgid "Initial exposure time" +msgstr "Tempo inicial mínimo de exposição" + +#: src/libslic3r/PrintConfig.cpp:2462 src/libslic3r/PrintConfig.cpp:2463 +msgid "Correction for expansion" +msgstr "Correção para expansão" + +#: src/libslic3r/PrintConfig.cpp:2469 +msgid "SLA print material notes" +msgstr "Notas de material de impressão de SLA" + +#: src/libslic3r/PrintConfig.cpp:2470 +msgid "You can put your notes regarding the SLA print material here." +msgstr "" +"Você pode colocar suas anotações sobre o material de impressão de SLA aqui." + +#: src/libslic3r/PrintConfig.cpp:2478 src/libslic3r/PrintConfig.cpp:2489 +msgid "Default SLA material profile" +msgstr "Perfil de material de SLA padrão" + +#: src/libslic3r/PrintConfig.cpp:2500 +msgid "Generate supports" +msgstr "Gerar suportes" + +#: src/libslic3r/PrintConfig.cpp:2502 +msgid "Generate supports for the models" +msgstr "Gere suportes para os modelos" + +#: src/libslic3r/PrintConfig.cpp:2507 +msgid "Support head front diameter" +msgstr "Diâmetro dianteiro principal da sustentação" + +#: src/libslic3r/PrintConfig.cpp:2509 +msgid "Diameter of the pointing side of the head" +msgstr "Diâmetro do lado apontando da cabeça" + +#: src/libslic3r/PrintConfig.cpp:2516 +msgid "Support head penetration" +msgstr "Suporte de penetração da cabeça" + +#: src/libslic3r/PrintConfig.cpp:2518 +msgid "How much the pinhead has to penetrate the model surface" +msgstr "Quanto a cabeça de alfinete tem de penetrar na superfície do modelo" + +#: src/libslic3r/PrintConfig.cpp:2525 +msgid "Support head width" +msgstr "Largura da cabeça de suporte" + +#: src/libslic3r/PrintConfig.cpp:2527 +msgid "Width from the back sphere center to the front sphere center" +msgstr "Largura do centro da esfera traseira ao centro da esfera dianteira" + +#: src/libslic3r/PrintConfig.cpp:2535 +msgid "Support pillar diameter" +msgstr "Diâmetro do pilar do suporte" + +#: src/libslic3r/PrintConfig.cpp:2537 +msgid "Diameter in mm of the support pillars" +msgstr "Diâmetro em mm dos pilares de suporte" + +#: src/libslic3r/PrintConfig.cpp:2545 +msgid "Support pillar connection mode" +msgstr "Modalidade da conexão da coluna da sustentação" + +#: src/libslic3r/PrintConfig.cpp:2546 +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 "" +"Controla o tipo de ponte entre dois pilares vizinhos. Pode ser zig-zag, Cruz " +"(zig-zag dobro) ou dinâmico que comutará automaticamente entre os primeiros " +"dois dependendo da distância dos dois pilares." + +#: src/libslic3r/PrintConfig.cpp:2554 +msgid "Zig-Zag" +msgstr "Zig-Zag" + +#: src/libslic3r/PrintConfig.cpp:2555 +msgid "Cross" +msgstr "Cruz" + +#: src/libslic3r/PrintConfig.cpp:2556 +msgid "Dynamic" +msgstr "Dinâmico" + +#: src/libslic3r/PrintConfig.cpp:2568 +msgid "Pillar widening factor" +msgstr "Fator de alargamento da coluna" + +#: src/libslic3r/PrintConfig.cpp:2570 +msgid "" +"Merging bridges or pillars into another pillars can increase the radius. " +"Zero means no increase, one means full increase." +msgstr "" +"Mesclar pontes ou pilares em outros pilares pode aumentar o raio. Zero " +"significa que não há aumento, um significa aumento total." + +#: src/libslic3r/PrintConfig.cpp:2579 +msgid "Support base diameter" +msgstr "Diâmetro base do suporte" + +#: src/libslic3r/PrintConfig.cpp:2581 +msgid "Diameter in mm of the pillar base" +msgstr "Diâmetro em mm da base do pilar" + +#: src/libslic3r/PrintConfig.cpp:2589 +msgid "Support base height" +msgstr "Altura base do suporte" + +#: src/libslic3r/PrintConfig.cpp:2591 +msgid "The height of the pillar base cone" +msgstr "A altura do cone da base da coluna" + +#: src/libslic3r/PrintConfig.cpp:2598 +msgid "Support base safety distance" +msgstr "Distância da segurança da base da sustentação" + +#: src/libslic3r/PrintConfig.cpp:2601 +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 "" +"A distância mínima da base do pilar do modelo em mm. faz sentido no modo de " +"elevação zero, onde uma lacuna de acordo com este parâmetro é inserida entre " +"o modelo e o pad." + +#: src/libslic3r/PrintConfig.cpp:2611 +msgid "Critical angle" +msgstr "Ângulo crítico" + +#: src/libslic3r/PrintConfig.cpp:2613 +msgid "The default angle for connecting support sticks and junctions." +msgstr "O ângulo padrão para conectar suportes e junções." + +#: src/libslic3r/PrintConfig.cpp:2621 +msgid "Max bridge length" +msgstr "Comprimento máximo da ponte" + +#: src/libslic3r/PrintConfig.cpp:2623 +msgid "The max length of a bridge" +msgstr "O comprimento máximo de uma ponte" + +#: src/libslic3r/PrintConfig.cpp:2630 +msgid "Max pillar linking distance" +msgstr "Distância máxima de conexão entre pilares" + +#: src/libslic3r/PrintConfig.cpp:2632 +msgid "" +"The max distance of two pillars to get linked with each other. A zero value " +"will prohibit pillar cascading." +msgstr "" +"A distância máxima de dois pilares para ficar ligado uns com os outros. Um " +"valor zero irá proibir o pilar em cascata." + +#: src/libslic3r/PrintConfig.cpp:2640 +msgid "Object elevation" +msgstr "Elevação do objeto" + +#: src/libslic3r/PrintConfig.cpp:2642 +msgid "" +"How much the supports should lift up the supported object. If \"Pad around " +"object\" is enabled, this value is ignored." +msgstr "" +"Quanto os suportes devem levantar o objecto suportado. Se \"pad em torno do " +"objeto\" estiver habilitado, esse valor será ignorado." + +#: src/libslic3r/PrintConfig.cpp:2653 +msgid "This is a relative measure of support points density." +msgstr "Esta é uma medida relativa de densidade de pontos de suporte." + +#: src/libslic3r/PrintConfig.cpp:2659 +msgid "Minimal distance of the support points" +msgstr "Distância mínima dos pontos de suporte" + +#: src/libslic3r/PrintConfig.cpp:2661 +msgid "No support points will be placed closer than this threshold." +msgstr "Nenhum ponto de apoio será colocado mais perto do que este limiar." + +#: src/libslic3r/PrintConfig.cpp:2667 +msgid "Use pad" +msgstr "Use pad" + +#: src/libslic3r/PrintConfig.cpp:2669 +msgid "Add a pad underneath the supported model" +msgstr "Adicionar um pad por baixo do modelo suportado" + +#: src/libslic3r/PrintConfig.cpp:2674 +msgid "Pad wall thickness" +msgstr "Espessura da parede do pad" + +#: src/libslic3r/PrintConfig.cpp:2676 +msgid "The thickness of the pad and its optional cavity walls." +msgstr "A espessura da pad e suas paredes de cavidade opcionais." + +#: src/libslic3r/PrintConfig.cpp:2684 +msgid "Pad wall height" +msgstr "Altura da parede do pad" + +#: src/libslic3r/PrintConfig.cpp:2685 +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 "" +"Define a profundidade da cavidade da pad. Defina como zero para desabilitar " +"a cavidade. Tenha cuidado ao ativar este recurso, como algumas resinas podem " +"produzir um efeito de sucção extrema dentro da cavidade, o que torna a " +"descascar a impressão fora da folha de IVA difícil." + +#: src/libslic3r/PrintConfig.cpp:2698 +msgid "Max merge distance" +msgstr "Distância máxima da fusão" + +#: src/libslic3r/PrintConfig.cpp:2700 +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 "" +"Alguns objetos podem se dar bem com algumas pads menores em vez de um único " +"grande. Este parâmetro define até que ponto o centro de duas pads menores " +"deve ser. Se eles estão mais perto, eles vão se fundir em uma pad." + +#: src/libslic3r/PrintConfig.cpp:2720 +msgid "Pad wall slope" +msgstr "Inclinação da parede da pad" + +#: src/libslic3r/PrintConfig.cpp:2722 +msgid "" +"The slope of the pad wall relative to the bed plane. 90 degrees means " +"straight walls." +msgstr "" +"A inclinação da parede da pad em relação ao plano da mesa. 90 graus " +"significa paredes retas." + +#: src/libslic3r/PrintConfig.cpp:2731 +msgid "Pad around object" +msgstr "Pad em torno do objeto" + +#: src/libslic3r/PrintConfig.cpp:2733 +msgid "Create pad around object and ignore the support elevation" +msgstr "Criar pad ao redor do objeto e ignorar a elevação de suporte" + +#: src/libslic3r/PrintConfig.cpp:2738 +msgid "Pad object gap" +msgstr "Vão entre o pad e o objeto" + +#: src/libslic3r/PrintConfig.cpp:2740 +msgid "" +"The gap between the object bottom and the generated pad in zero elevation " +"mode." +msgstr "" +"A lacuna entre a parte inferior do objeto e o pad gerado no modo de elevação " +"zero." + +#: src/libslic3r/PrintConfig.cpp:2749 +msgid "Pad object connector stride" +msgstr "Inserir pad entre o objeto" + +#: src/libslic3r/PrintConfig.cpp:2751 +msgid "" +"Distance between two connector sticks which connect the object and the " +"generated pad." +msgstr "" +"Distância entre duas varas do conector que conectam o objeto e a pad gerada." + +#: src/libslic3r/PrintConfig.cpp:2758 +msgid "Pad object connector width" +msgstr "Largura do conector do objeto pad" + +#: src/libslic3r/PrintConfig.cpp:2760 +msgid "" +"Width of the connector sticks which connect the object and the generated pad." +msgstr "Largura das varas do conector que conectam o objeto e a pad gerada." + +#: src/libslic3r/PrintConfig.cpp:2767 +msgid "Pad object connector penetration" +msgstr "Pad objeto conector de penetração" + +#: src/libslic3r/PrintConfig.cpp:2770 +msgid "How much should the tiny connectors penetrate into the model body." +msgstr "Quanto deve os conectores minúsculos penetrar no corpo do modelo." + +#: src/libslic3r/PrintConfig.cpp:3130 +msgid "Export OBJ" +msgstr "Exportar OBJ" + +#: src/libslic3r/PrintConfig.cpp:3131 +msgid "Export the model(s) as OBJ." +msgstr "Exportar modelo(s) como OBJ." + +#: src/libslic3r/PrintConfig.cpp:3142 +msgid "Export SLA" +msgstr "Exportar SLA" + +#: src/libslic3r/PrintConfig.cpp:3143 +msgid "Slice the model and export SLA printing layers as PNG." +msgstr "Fatiar o modelo e exportar as camadas de impressão SLA como PNG." + +#: src/libslic3r/PrintConfig.cpp:3148 +msgid "Export 3MF" +msgstr "Exportar 3MF" + +#: src/libslic3r/PrintConfig.cpp:3149 +msgid "Export the model(s) as 3MF." +msgstr "Exportar modelo(s) como 3MF." + +#: src/libslic3r/PrintConfig.cpp:3153 +msgid "Export AMF" +msgstr "Exportar AMF" + +#: src/libslic3r/PrintConfig.cpp:3154 +msgid "Export the model(s) as AMF." +msgstr "Exportar modelo(s) como AMF." + +#: src/libslic3r/PrintConfig.cpp:3158 +msgid "Export STL" +msgstr "Exportar STL" + +#: src/libslic3r/PrintConfig.cpp:3159 +msgid "Export the model(s) as STL." +msgstr "Exportar modelo(s) como STL." + +#: src/libslic3r/PrintConfig.cpp:3164 +msgid "Slice the model and export toolpaths as G-code." +msgstr "Fatiar o modelo e exportar o percurso da ferramenta como G-code." + +#: src/libslic3r/PrintConfig.cpp:3169 +msgid "Slice" +msgstr "Fatiar" + +#: src/libslic3r/PrintConfig.cpp:3170 +msgid "" +"Slice the model as FFF or SLA based on the printer_technology configuration " +"value." +msgstr "" +"Divida o modelo como FFF ou SLA com base no valor de config. " +"printer_technology." + +#: src/libslic3r/PrintConfig.cpp:3175 +msgid "Help" +msgstr "Ajuda" + +#: src/libslic3r/PrintConfig.cpp:3176 +msgid "Show this help." +msgstr "Mostrar esta ajuda." + +#: src/libslic3r/PrintConfig.cpp:3181 +msgid "Help (FFF options)" +msgstr "Ajuda (opções FDM)" + +#: src/libslic3r/PrintConfig.cpp:3182 +msgid "Show the full list of print/G-code configuration options." +msgstr "Mostre a lista completa de opções de config. do Print/G-code." + +#: src/libslic3r/PrintConfig.cpp:3186 +msgid "Help (SLA options)" +msgstr "Ajuda (opções SLA)" + +#: src/libslic3r/PrintConfig.cpp:3187 +msgid "Show the full list of SLA print configuration options." +msgstr "Mostrar a lista completa de opções de config. de impressão de SLA." + +#: src/libslic3r/PrintConfig.cpp:3191 +msgid "Output Model Info" +msgstr "Informações do modelo de saída" + +#: src/libslic3r/PrintConfig.cpp:3192 +msgid "Write information about the model to the console." +msgstr "Escreva informações sobre o modelo para o console." + +#: src/libslic3r/PrintConfig.cpp:3196 +msgid "Save config file" +msgstr "Salvar arquivo de config." + +#: src/libslic3r/PrintConfig.cpp:3197 +msgid "Save configuration to the specified file." +msgstr "Salvar config. para o arquivo específico." + +#: src/libslic3r/PrintConfig.cpp:3207 +msgid "Align XY" +msgstr "Alinhar XY" + +#: src/libslic3r/PrintConfig.cpp:3208 +msgid "Align the model to the given point." +msgstr "Alinhar modelo de acordo com o ponto inserido." + +#: src/libslic3r/PrintConfig.cpp:3213 +msgid "Cut model at the given Z." +msgstr "Cortar modelo ao Z fornecido." + +#: src/libslic3r/PrintConfig.cpp:3234 +msgid "Center" +msgstr "Centralizar" + +#: src/libslic3r/PrintConfig.cpp:3235 +msgid "Center the print around the given center." +msgstr "Centralizar a impressão de acordo com o centro informado." + +#: src/libslic3r/PrintConfig.cpp:3239 +msgid "Don't arrange" +msgstr "Não organizar" + +#: src/libslic3r/PrintConfig.cpp:3240 +msgid "" +"Do not rearrange the given models before merging and keep their original XY " +"coordinates." +msgstr "" +"Não reorganize os modelos fornecidos antes de Mesclar e manter suas " +"coordenadas XY originais." + +#: src/libslic3r/PrintConfig.cpp:3243 +msgid "Duplicate" +msgstr "Duplicar" + +#: src/libslic3r/PrintConfig.cpp:3244 +msgid "Multiply copies by this factor." +msgstr "Multiplicar cópias por esse fator." + +#: src/libslic3r/PrintConfig.cpp:3248 +msgid "Duplicate by grid" +msgstr "Duplicar por grade" + +#: src/libslic3r/PrintConfig.cpp:3249 +msgid "Multiply copies by creating a grid." +msgstr "Multiplique cópias criando uma grade." + +#: src/libslic3r/PrintConfig.cpp:3252 +msgid "Merge" +msgstr "Mesclar" + +#: src/libslic3r/PrintConfig.cpp:3253 +msgid "" +"Arrange the supplied models in a plate and merge them in a single model in " +"order to perform actions once." +msgstr "" +"Organize os modelos fornecidos em uma placa e junte-os em um único modelo, a " +"fim de executar ações uma só vez." + +#: src/libslic3r/PrintConfig.cpp:3258 +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 "" +"Tente reparar qualquer malhas não multiplicadas (essa opção é implicitamente " +"adicionada sempre que precisamos cortar o modelo para executar a ação " +"solicitada)." + +#: src/libslic3r/PrintConfig.cpp:3262 +msgid "Rotation angle around the Z axis in degrees." +msgstr "Ângulo de rotação ao redor do eixo Zem graus." + +#: src/libslic3r/PrintConfig.cpp:3266 +msgid "Rotate around X" +msgstr "Rotacionar no X" + +#: src/libslic3r/PrintConfig.cpp:3267 +msgid "Rotation angle around the X axis in degrees." +msgstr "Ângulo de rotação ao redor do eixo X em graus." + +#: src/libslic3r/PrintConfig.cpp:3271 +msgid "Rotate around Y" +msgstr "Rotacionar no Y" + +#: src/libslic3r/PrintConfig.cpp:3272 +msgid "Rotation angle around the Y axis in degrees." +msgstr "Ângulo de rotação ao redor do eixo Y em graus." + +#: src/libslic3r/PrintConfig.cpp:3277 +msgid "Scaling factor or percentage." +msgstr "Escalando fator ou porcentagem." + +#: src/libslic3r/PrintConfig.cpp:3282 +msgid "" +"Detect unconnected parts in the given model(s) and split them into separate " +"objects." +msgstr "" +"Detecte peças não conectadas em um determinado modelo (s) e divida-as em " +"objetos separados." + +#: src/libslic3r/PrintConfig.cpp:3285 +msgid "Scale to Fit" +msgstr "Dimensionar para caber" + +#: src/libslic3r/PrintConfig.cpp:3286 +msgid "Scale to fit the given volume." +msgstr "Escalar para se adequar ao volume informado." + +#: src/libslic3r/PrintConfig.cpp:3295 +msgid "Ignore non-existent config files" +msgstr "Ignorar arquivos de config. não existentes" + +#: src/libslic3r/PrintConfig.cpp:3296 +msgid "Do not fail if a file supplied to --load does not exist." +msgstr "Não falhe se um arquivo fornecido para--carregamento não existe." + +#: src/libslic3r/PrintConfig.cpp:3299 +msgid "Load config file" +msgstr "Carregar arquivo de config." + +#: src/libslic3r/PrintConfig.cpp:3300 +msgid "" +"Load configuration from the specified file. It can be used more than once to " +"load options from multiple files." +msgstr "" +"Carregar a config. do arquivo especificado. Ele pode ser usado mais de uma " +"vez para carregar opções de vários arquivos." + +#: src/libslic3r/PrintConfig.cpp:3303 +msgid "Output File" +msgstr "Arquivo de saída" + +#: src/libslic3r/PrintConfig.cpp:3304 +msgid "" +"The file where the output will be written (if not specified, it will be " +"based on the input file)." +msgstr "" +"O arquivo onde a saída será gravada (se não for especificado, ele será " +"baseado no arquivo de entrada)." + +#: src/libslic3r/PrintConfig.cpp:3314 +msgid "Data directory" +msgstr "Diretório de dados" + +#: src/libslic3r/PrintConfig.cpp:3315 +msgid "" +"Load and store settings at the given directory. This is useful for " +"maintaining different profiles or including configurations from a network " +"storage." +msgstr "" +"Carregar e armazenar as config. no diretório especificado. Isso é útil para " +"manter perfis diferentes ou incluir config. de um armazenamento de rede." + +#: src/libslic3r/PrintConfig.cpp:3318 +msgid "Logging level" +msgstr "Nível de registro" + +#: src/libslic3r/PrintConfig.cpp:3319 +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 "" +"Mensagens com severidade menor ou igual para o LogLevel serão impressos. 0: " +"Trace, 1: debug, 2: info, 3: aviso, 4: erro, 5: fatal" + +#: src/libslic3r/PrintConfig.cpp:3324 +msgid "Render with a software renderer" +msgstr "Renderizar com um software renderizador" + +#: src/libslic3r/PrintConfig.cpp:3325 +msgid "" +"Render with a software renderer. The bundled MESA software renderer is " +"loaded instead of the default OpenGL driver." +msgstr "" +"Renderizar com um software renderizador. O renderizador de software MESA " +"empacotado é carregado em vez do driver OpenGL padrão." + +#: src/libslic3r/PrintObject.cpp:110 +msgid "Processing triangulated mesh" +msgstr "Processando malha triangulada" + +#: src/libslic3r/PrintObject.cpp:141 +msgid "Generating perimeters" +msgstr "Gerando perímetros" + +#: src/libslic3r/PrintObject.cpp:251 +msgid "Preparing infill" +msgstr "Preparando o preenchimento" + +#: src/libslic3r/PrintObject.cpp:391 +msgid "Generating support material" +msgstr "Gerando material de suporte" + +#: src/libslic3r/GCode/PreviewData.cpp:160 +msgid "Mixed" +msgstr "Misto" + +#: src/libslic3r/GCode/PreviewData.cpp:380 +msgid "Height (mm)" +msgstr "Altura (mm)" + +#: src/libslic3r/GCode/PreviewData.cpp:382 +msgid "Width (mm)" +msgstr "Espessura (mm)" + +#: src/libslic3r/GCode/PreviewData.cpp:384 +msgid "Speed (mm/s)" +msgstr "Velocidade (mm/s)" + +#: src/libslic3r/GCode/PreviewData.cpp:386 +msgid "Volumetric flow rate (mm3/s)" +msgstr "Fluxo volumétrico (mm3/s)" + +#: src/libslic3r/GCode/PreviewData.cpp:477 +msgid "Default print color" +msgstr "Cor de impressão padrão" + +#: src/libslic3r/GCode/PreviewData.cpp:484 +#, c-format +msgid "up to %.2f mm" +msgstr "até %.2f mm" + +#: src/libslic3r/GCode/PreviewData.cpp:488 +#, c-format +msgid "above %.2f mm" +msgstr "acima de %.2f mm" + +#: src/libslic3r/GCode/PreviewData.cpp:493 +#, c-format +msgid "%.2f - %.2f mm" +msgstr "%.2f - %.2f mm" diff --git a/resources/models/ender3_bed.stl b/resources/models/ender3_bed.stl new file mode 100644 index 000000000..fb8f86d09 Binary files /dev/null and b/resources/models/ender3_bed.stl differ diff --git a/resources/models/mini_bed.stl b/resources/models/mini_bed.stl new file mode 100644 index 000000000..2f4c45b7b Binary files /dev/null and b/resources/models/mini_bed.stl differ diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index b2bd7f8c8..3b2720300 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,4 +1,12 @@ +min_slic3r_version = 2.2.0-alpha0 +1.1.0 Filament aliases, Creality profiles and other goodies for PrusaSlicer 2.2.0-alpha0 +min_slic3r_version = 2.1.1-beta0 +1.0.6 Added Prusa MINI profiles min_slic3r_version = 2.1.0-alpha0 +1.0.5 Added SLA materials +1.0.4 Updated firmware version and 0.25mm nozzle profiles +1.0.3 Added filament profiles +1.0.2 Added SLA materials 1.0.1 Updated MK3 firmware version check to 3.8.0, new soluble support profiles for 0.6mm nozzle diameter MMU2S printers. 1.0.0 Updated end G-code for the MMU2 profiles to lift the extruder at the end of print. Wipe tower bridging distance was made smaller for soluble supports. 1.0.0-beta1 Updated color for the ASA filaments to differ from the other filaments. Single extruder printers now have no extruder color assigned, obects and toolpaths will be colored with the color of the active filament. @@ -7,6 +15,8 @@ min_slic3r_version = 2.1.0-alpha0 1.0.0-alpha1 Added Prusament ASA profile 1.0.0-alpha0 Filament specific retract for PET and similar copolymers, and for FLEX min_slic3r_version = 1.42.0-alpha6 +0.8.7 Updated firmware version +0.8.6 Updated firmware version for MK2.5/S and MK3/S 0.8.5 Updated SL1 printer and material settings 0.8.4 Added Prusament ASA profile 0.8.3 FW version and SL1 materials update @@ -31,6 +41,8 @@ min_slic3r_version = 1.42.0-alpha 0.4.0-alpha3 Update of SLA profiles 0.4.0-alpha2 First SLA profiles min_slic3r_version = 1.41.3-alpha +0.4.10 Updated firmware version +0.4.9 Updated firmware version for MK2.5/S and MK3/S 0.4.8 MK2.5/3/S FW update 0.4.7 MK2/S/MMU FW update 0.4.6 Updated firmware versions for MK2.5/S and MK3/S @@ -41,6 +53,8 @@ min_slic3r_version = 1.41.3-alpha 0.4.1 New MK2.5S and MK3S FW versions 0.4.0 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt min_slic3r_version = 1.41.1 +0.3.10 Updated firmware version +0.3.9 Updated firmware version for MK2.5/S and MK3/S 0.3.8 MK2.5/3/S FW update 0.3.7 MK2/S/MMU FW update 0.3.6 Updated firmware versions for MK2.5 and MK3 @@ -77,6 +91,8 @@ min_slic3r_version = 1.41.0-alpha 0.2.0-alpha1 added initial profiles for the i3 MK3 Multi Material Upgrade 2.0 0.2.0-alpha moved machine limits from the start G-code to the new print profile parameters min_slic3r_version = 1.40.0 +0.1.18 Updated firmware version +0.1.17 Updated firmware version for MK2.5/S and MK3/S 0.1.16 MK2.5/3/S FW update 0.1.15 MK2/S/MMU FW update 0.1.14 Updated firmware versions for MK2.5 and MK3 diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index ba935bb67..c8c889ca4 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -8,7 +8,7 @@ technologies = FFF; SLA # 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.0.1 +config_version = 1.1.0 # Where to get the updates from? config_update_url = changelog_url = http://files.prusa3d.com/?latest=slicer-profiles&lng=%1% @@ -19,6 +19,13 @@ changelog_url = http://files.prusa3d.com/?latest=slicer-profiles&lng=%1% #for example by the melt zone size, or whether the nozzle is hardened. # Printer model name will be shown by the installation wizard. + +[printer_model:MINI] +name = Original Prusa MINI +variants = 0.4; 0.25; 0.6 +technology = FFF +family = MINI + [printer_model:MK3S] name = Original Prusa i3 MK3S variants = 0.4; 0.25; 0.6 @@ -210,11 +217,26 @@ travel_speed = 180 wipe_tower_x = 170 wipe_tower_y = 125 +## MINI + +[print:*MINI*] +fill_pattern = grid +travel_speed = 150 +wipe_tower = 0 +default_acceleration = 1250 +first_layer_acceleration = 800 +infill_acceleration = 1000 +bridge_acceleration = 1000 +support_material_speed = 40 +max_print_speed = 150 +extruder_clearance_height = 20 +extruder_clearance_radius = 35 + # Print parameters common to a 0.25mm diameter nozzle. [print:*0.25nozzle*] external_perimeter_extrusion_width = 0.25 extrusion_width = 0.25 -first_layer_extrusion_width = 0.25 +first_layer_extrusion_width = 0.3 infill_extrusion_width = 0.25 perimeter_extrusion_width = 0.25 solid_infill_extrusion_width = 0.25 @@ -229,7 +251,7 @@ output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_heig [print:*0.25nozzleMK3*] external_perimeter_extrusion_width = 0.25 extrusion_width = 0.25 -first_layer_extrusion_width = 0.35 +first_layer_extrusion_width = 0.3 infill_extrusion_width = 0.25 perimeter_extrusion_width = 0.25 solid_infill_extrusion_width = 0.25 @@ -259,6 +281,38 @@ fill_pattern = grid fill_density = 20% output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode +[print:*0.25nozzleMINI*] +external_perimeter_extrusion_width = 0.25 +extrusion_width = 0.25 +first_layer_extrusion_width = 0.3 +infill_extrusion_width = 0.25 +perimeter_extrusion_width = 0.25 +solid_infill_extrusion_width = 0.25 +top_infill_extrusion_width = 0.25 +support_material_extrusion_width = 0.2 +support_material_interface_layers = 0 +support_material_interface_spacing = 0.15 +support_material_spacing = 1 +support_material_xy_spacing = 150% +perimeter_speed = 30 +external_perimeter_speed = 20 +small_perimeter_speed = 20 +infill_speed = 40 +solid_infill_speed = 40 +top_solid_infill_speed = 30 +support_material_speed = 40 +bridge_speed = 20 +gap_fill_speed = 30 +perimeter_acceleration = 500 +infill_acceleration = 800 +bridge_acceleration = 500 +first_layer_acceleration = 500 +max_print_speed = 80 +perimeters = 3 +fill_pattern = grid +fill_density = 20% +output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode + # Print parameters common to a 0.6mm diameter nozzle. [print:*0.6nozzle*] external_perimeter_extrusion_width = 0.61 @@ -288,6 +342,30 @@ support_material_contact_distance = 0.15 support_material_xy_spacing = 80% output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode +[print:*0.6nozzleMINI*] +external_perimeter_extrusion_width = 0.65 +extrusion_width = 0.65 +first_layer_extrusion_width = 0.65 +infill_extrusion_width = 0.68 +perimeter_extrusion_width = 0.65 +solid_infill_extrusion_width = 0.68 +top_infill_extrusion_width = 0.6 +support_material_extrusion_width = 0.55 +bridge_flow_ratio = 0.95 +bridge_speed = 25 +support_material_contact_distance = 0.15 +support_material_xy_spacing = 80% +fill_pattern = gyroid +fill_density = 15% +travel_speed = 150 +perimeter_acceleration = 800 +infill_acceleration = 1000 +bridge_acceleration = 1000 +first_layer_acceleration = 1000 +default_acceleration = 1250 +support_material_speed = 40 +output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode + [print:*soluble_support*] overhangs = 1 skirts = 0 @@ -314,7 +392,7 @@ inherits = *common* bottom_solid_layers = 10 bridge_acceleration = 300 bridge_flow_ratio = 0.7 -default_acceleration = 500 +default_acceleration = 1000 external_perimeter_speed = 20 fill_density = 20% first_layer_acceleration = 500 @@ -336,20 +414,23 @@ top_solid_layers = 15 [print:0.05mm ULTRADETAIL] inherits = *0.05mm* +# alias = 0.05mm ULTRADETAIL compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 and num_extruders==1 infill_extrusion_width = 0.5 # MK3 # -[print:0.05mm ULTRADETAIL MK3] +[print:0.05mm ULTRADETAIL @MK3] inherits = *0.05mm*; *MK3* +# alias = 0.05mm ULTRADETAIL fill_pattern = gyroid fill_density = 15% 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 top_infill_extrusion_width = 0.4 # MK2 # -[print:0.05mm ULTRADETAIL 0.25 nozzle] +[print:0.05mm ULTRADETAIL @0.25 nozzle] inherits = *0.05mm*; *0.25nozzle* +# alias = 0.05mm ULTRADETAIL compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.25 and num_extruders==1 fill_density = 20% infill_speed = 20 @@ -360,12 +441,12 @@ solid_infill_speed = 20 support_material_speed = 20 # MK3 # -[print:0.05mm ULTRADETAIL 0.25 nozzle MK3] +[print:0.05mm ULTRADETAIL @0.25 nozzle MK3] inherits = *0.05mm*; *0.25nozzle*; *MK3* +# alias = 0.05mm ULTRADETAIL compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 and num_extruders==1 fill_pattern = grid fill_density = 20% -first_layer_extrusion_width = 0.35 # XXXXXXXXXXXXXXXXXXXX # XXX--- 0.07mm ---XXX @@ -377,7 +458,7 @@ bottom_solid_layers = 8 bridge_acceleration = 300 bridge_flow_ratio = 0.7 bridge_speed = 20 -default_acceleration = 500 +default_acceleration = 1000 external_perimeter_speed = 20 fill_density = 15% first_layer_acceleration = 500 @@ -398,14 +479,16 @@ top_solid_infill_speed = 30 top_solid_layers = 11 # MK3 # -[print:0.07mm ULTRADETAIL MK3] +[print:0.07mm ULTRADETAIL @MK3] inherits = *0.07mm*; *MK3* +# alias = 0.07mm ULTRADETAIL fill_pattern = gyroid 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 top_infill_extrusion_width = 0.4 -[print:0.07mm ULTRADETAIL 0.25 nozzle MK3] +[print:0.07mm ULTRADETAIL @0.25 nozzle MK3] inherits = *0.07mm*; *0.25nozzle*; *MK3* +# alias = 0.07mm ULTRADETAIL compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 and num_extruders==1 infill_speed = 30 solid_infill_speed = 30 @@ -413,7 +496,6 @@ support_material_speed = 30 top_solid_infill_speed = 20 fill_pattern = grid fill_density = 20% -first_layer_extrusion_width = 0.35 # XXXXXXXXXXXXXXXXXXXX # XXX--- 0.10mm ---XXX @@ -432,6 +514,7 @@ top_solid_layers = 9 # MK2 # [print:0.10mm DETAIL] inherits = *0.10mm* +# alias = 0.10mm DETAIL compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 and num_extruders==1 external_perimeter_speed = 40 infill_acceleration = 2000 @@ -440,8 +523,9 @@ perimeter_speed = 50 solid_infill_speed = 50 # MK3 # -[print:0.10mm DETAIL MK3] +[print:0.10mm DETAIL @MK3] inherits = *0.10mm*; *MK3* +# alias = 0.10mm DETAIL 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 @@ -456,12 +540,13 @@ fill_pattern = gyroid fill_density = 15% # MK2 # -[print:0.10mm DETAIL 0.25 nozzle] +[print:0.10mm DETAIL @0.25 nozzle] inherits = *0.10mm*; *0.25nozzle* +# alias = 0.10mm DETAIL bridge_acceleration = 600 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.25 external_perimeter_speed = 20 -infill_acceleration = 1600 +infill_acceleration = 1000 infill_speed = 40 perimeter_acceleration = 600 perimeter_speed = 25 @@ -470,8 +555,9 @@ solid_infill_speed = 40 top_solid_infill_speed = 30 # MK3 # -[print:0.10mm DETAIL 0.25 nozzle MK3] +[print:0.10mm DETAIL @0.25 nozzle MK3] inherits = *0.10mm*; *0.25nozzleMK3*; *MK3* +# alias = 0.10mm DETAIL compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 fill_pattern = grid fill_density = 20% @@ -510,17 +596,19 @@ top_solid_infill_speed = 70 # MK2 # [print:0.15mm OPTIMAL] inherits = *0.15mm* +# alias = 0.15mm OPTIMAL compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 top_infill_extrusion_width = 0.45 # MK2 # -[print:0.15mm OPTIMAL 0.25 nozzle] +[print:0.15mm OPTIMAL @0.25 nozzle] inherits = *0.15mm*; *0.25nozzle* +# alias = 0.15mm OPTIMAL bridge_acceleration = 600 bridge_flow_ratio = 0.7 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.25 external_perimeter_speed = 20 -infill_acceleration = 1600 +infill_acceleration = 1000 infill_speed = 40 perimeter_acceleration = 600 perimeter_speed = 25 @@ -529,13 +617,15 @@ solid_infill_speed = 40 top_solid_infill_speed = 30 # MK2 # -[print:0.15mm OPTIMAL 0.6 nozzle] +[print:0.15mm OPTIMAL @0.6 nozzle] inherits = *0.15mm*; *0.6nozzle* +# alias = 0.15mm OPTIMAL compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 # MK3 # -[print:0.15mm QUALITY MK3] +[print:0.15mm QUALITY @MK3] inherits = *0.15mm*; *MK3* +# alias = 0.15mm QUALITY 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 @@ -548,8 +638,9 @@ top_solid_infill_speed = 40 fill_pattern = gyroid fill_density = 15% -[print:0.15mm SPEED MK3] +[print:0.15mm SPEED @MK3] inherits = *0.15mm*; *MK3* +# alias = 0.15mm SPEED 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 @@ -561,8 +652,9 @@ solid_infill_speed = 200 top_solid_infill_speed = 50 # MK3 MMU # -[print:0.15mm SOLUBLE FULL MK3] -inherits = 0.15mm SPEED MK3; *soluble_support* +[print:0.15mm SOLUBLE FULL @MK3] +inherits = 0.15mm SPEED @MK3; *soluble_support* +# alias = 0.15mm SOLUBLE FULL compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder support_material_extruder = 5 @@ -573,8 +665,9 @@ top_infill_extrusion_width = 0.45 top_solid_infill_speed = 30 # MK3 MMU # -[print:0.15mm SOLUBLE INTERFACE MK3] -inherits = 0.15mm SOLUBLE FULL MK3 +[print:0.15mm SOLUBLE INTERFACE @MK3] +inherits = 0.15mm SOLUBLE FULL @MK3 +# alias = 0.15mm SOLUBLE INTERFACE notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder support_material_extruder = 0 support_material_interface_layers = 3 @@ -583,6 +676,7 @@ support_material_with_sheath = 0 # MK2 MMU # [print:0.15mm OPTIMAL SOLUBLE FULL] inherits = *0.15mm*; *soluble_support* +# alias = 0.15mm OPTIMAL SOLUBLE FULL compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 and num_extruders>1 external_perimeter_speed = 25 notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder @@ -594,6 +688,7 @@ top_solid_infill_speed = 30 # MK2 MMU # [print:0.15mm OPTIMAL SOLUBLE INTERFACE] inherits = 0.15mm OPTIMAL SOLUBLE FULL +# alias = 0.15mm OPTIMAL SOLUBLE INTERFACE notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder support_material_extruder = 0 support_material_interface_layers = 3 @@ -601,15 +696,17 @@ support_material_with_sheath = 0 support_material_xy_spacing = 80% # MK3 # -[print:0.15mm QUALITY 0.25 nozzle MK3] +[print:0.15mm QUALITY @0.25 nozzle MK3] inherits = *0.15mm*; *0.25nozzleMK3*; *MK3* +# alias = 0.15mm QUALITY compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 fill_pattern = grid fill_density = 20% # MK3 # -[print:0.15mm DETAIL 0.6 nozzle MK3] +[print:0.15mm DETAIL @0.6 nozzle MK3] 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 @@ -651,8 +748,9 @@ support_material_speed = 60 top_solid_infill_speed = 70 # MK3 # -[print:0.20mm QUALITY MK3] +[print:0.20mm QUALITY @MK3] inherits = *0.20mm*; *MK3* +# alias = 0.20mm QUALITY 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 @@ -665,8 +763,9 @@ top_solid_infill_speed = 40 fill_pattern = gyroid fill_density = 15% -[print:0.20mm SPEED MK3] +[print:0.20mm SPEED @MK3] inherits = *0.20mm*; *MK3* +# alias = 0.20mm SPEED 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 @@ -678,8 +777,9 @@ solid_infill_speed = 200 top_solid_infill_speed = 50 # MK3 MMU # -[print:0.20mm SOLUBLE FULL MK3] -inherits = 0.20mm SPEED MK3; *soluble_support* +[print:0.20mm SOLUBLE FULL @MK3] +inherits = 0.20mm SPEED @MK3; *soluble_support* +# alias = 0.20mm SOLUBLE FULL compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder support_material_extruder = 5 @@ -690,8 +790,9 @@ top_infill_extrusion_width = 0.45 top_solid_infill_speed = 30 # MK3 MMU # -[print:0.20mm SOLUBLE INTERFACE MK3] -inherits = 0.20mm SOLUBLE FULL MK3 +[print:0.20mm SOLUBLE INTERFACE @MK3] +inherits = 0.20mm SOLUBLE FULL @MK3 +# alias = 0.20mm SOLUBLE INTERFACE notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder support_material_extruder = 0 support_material_interface_layers = 3 @@ -703,8 +804,9 @@ inherits = *0.20mm* compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 # MK2 # -[print:0.20mm NORMAL 0.6 nozzle] +[print:0.20mm NORMAL @0.6 nozzle] inherits = *0.20mm*; *0.6nozzle* +# alias = 0.20mm NORMAL compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 # MK2 MMU # @@ -727,8 +829,9 @@ support_material_with_sheath = 0 support_material_xy_spacing = 80% # MK3 # -[print:0.20mm DETAIL 0.6 nozzle MK3] +[print:0.20mm DETAIL @0.6 nozzle MK3] 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 @@ -738,6 +841,21 @@ perimeter_speed = 45 solid_infill_speed = 70 top_solid_infill_speed = 45 + +# XXXXXXXXXXXXXXXXXXXX +# XXX--- 0.25mm ---XXX +# XXXXXXXXXXXXXXXXXXXX + +[print:*0.25mm*] +inherits = *common* +bottom_solid_layers = 4 +bridge_flow_ratio = 0.95 +external_perimeter_speed = 40 +perimeter_acceleration = 800 +layer_height = 0.25 +perimeter_speed = 50 +top_solid_layers = 4 + # XXXXXXXXXXXXXXXXXXXX # XXX--- 0.30mm ---XXX # XXXXXXXXXXXXXXXXXXXX @@ -756,8 +874,9 @@ solid_infill_speed = 50 top_infill_extrusion_width = 0.4 top_solid_layers = 4 -[print:0.30mm QUALITY 0.6 nozzle MK3] +[print:0.30mm QUALITY @0.6 nozzle MK3] 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 @@ -767,8 +886,9 @@ perimeter_speed = 45 solid_infill_speed = 70 top_solid_infill_speed = 45 -[print:0.30mm SOLUBLE FULL 0.6 nozzle MK3] -inherits = 0.30mm QUALITY 0.6 nozzle MK3; *soluble_support* +[print:0.30mm SOLUBLE FULL @0.6 nozzle MK3] +inherits = 0.30mm QUALITY @0.6 nozzle MK3; *soluble_support* +# alias = 0.30mm SOLUBLE FULL compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 and num_extruders>1 notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder support_material_extruder = 5 @@ -781,15 +901,17 @@ support_material_extrusion_width = 0.6 top_solid_infill_speed = 30 support_material_xy_spacing = 80% -[print:0.30mm SOLUBLE INTERFACE 0.6 nozzle MK3] -inherits = 0.30mm SOLUBLE FULL 0.6 nozzle MK3 +[print:0.30mm SOLUBLE INTERFACE @0.6 nozzle MK3] +inherits = 0.30mm SOLUBLE FULL @0.6 nozzle MK3 +# alias = 0.30mm SOLUBLE INTERFACE notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder support_material_extruder = 0 support_material_interface_layers = 3 support_material_with_sheath = 0 -[print:0.30mm DRAFT MK3] +[print:0.30mm DRAFT @MK3] inherits = *0.30mm*; *MK3* +# alias = 0.30mm DRAFT 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 @@ -841,15 +963,18 @@ first_layer_extrusion_width = 0.42 perimeter_extrusion_width = 0.43 solid_infill_extrusion_width = 0.7 top_infill_extrusion_width = 0.43 +support_material_extrusion_width = 0.37 # MK2 # -[print:0.35mm FAST 0.6 nozzle] +[print:0.35mm FAST @0.6 nozzle] inherits = *0.35mm*; *0.6nozzle* +# alias = 0.35mm FAST compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 # MK2 MMU # -[print:0.35mm FAST sol full 0.6 nozzle] +[print:0.35mm FAST sol full @0.6 nozzle] inherits = *0.35mm*; *0.6nozzle*; *soluble_support* +# alias = 0.35mm FAST SOLUBLE FULL compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_model=="MK2SMM" and nozzle_diameter[0]==0.6 and num_extruders>1 external_perimeter_extrusion_width = 0.6 external_perimeter_speed = 30 @@ -862,16 +987,18 @@ top_infill_extrusion_width = 0.6 support_material_extrusion_width = 0.6 # MK2 MMU # -[print:0.35mm FAST sol int 0.6 nozzle] -inherits = 0.35mm FAST sol full 0.6 nozzle +[print:0.35mm FAST sol int @0.6 nozzle] +inherits = 0.35mm FAST sol full @0.6 nozzle +# alias = 0.35mm FAST SOLUBLE INTERFACE support_material_extruder = 0 support_material_interface_layers = 3 support_material_with_sheath = 0 support_material_xy_spacing = 150% # MK3 # -[print:0.35mm SPEED 0.6 nozzle MK3] +[print:0.35mm SPEED @0.6 nozzle MK3] 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 @@ -905,8 +1032,9 @@ top_solid_infill_speed = 40 top_solid_layers = 4 # MK3 # -[print:0.40mm DRAFT 0.6 nozzle MK3] +[print:0.40mm DRAFT @0.6 nozzle MK3] 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 @@ -925,68 +1053,77 @@ solid_infill_extrusion_width = 0.7 # XXXXXXXXXXXXXXXXXXXXXX # MK2.5 # -[print:0.15mm 100mms Linear Advance MK2.5] +[print:0.15mm 100mms Linear Advance @MK2.5] inherits = 0.15mm 100mms Linear Advance compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 single_extruder_multi_material_priming = 0 # MK2.5 # -[print:0.15mm OPTIMAL MK2.5] +[print:0.15mm OPTIMAL @MK2.5] inherits = 0.15mm OPTIMAL +# alias = 0.15mm OPTIMAL compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 single_extruder_multi_material_priming = 0 # MK2.5 MMU2 # -[print:0.15mm OPTIMAL SOLUBLE FULL MK2.5] +[print:0.15mm OPTIMAL SOLUBLE FULL @MK2.5] inherits = 0.15mm OPTIMAL SOLUBLE FULL +# alias = 0.15mm OPTIMAL SOLUBLE FULL support_material_extruder = 5 support_material_interface_extruder = 5 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 # MK2.5 MMU2 # -[print:0.15mm OPTIMAL SOLUBLE INTERFACE MK2.5] +[print:0.15mm OPTIMAL SOLUBLE INTERFACE @MK2.5] inherits = 0.15mm OPTIMAL SOLUBLE INTERFACE +# alias = 0.15mm OPTIMAL SOLUBLE INTERFACE support_material_extruder = 0 support_material_interface_extruder = 5 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 # MK2.5 # -[print:0.20mm 100mms Linear Advance MK2.5] +[print:0.20mm 100mms Linear Advance @MK2.5] inherits = 0.20mm 100mms Linear Advance +# alias = 0.20mm 100mms Linear Advance compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 single_extruder_multi_material_priming = 0 # MK2.5 # -[print:0.20mm NORMAL MK2.5] +[print:0.20mm NORMAL @MK2.5] inherits = 0.20mm NORMAL +# alias = 0.20mm NORMAL compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 single_extruder_multi_material_priming = 0 # MK2.5 MMU2 # -[print:0.20mm NORMAL SOLUBLE FULL MK2.5] +[print:0.20mm NORMAL SOLUBLE FULL @MK2.5] inherits = 0.20mm NORMAL SOLUBLE FULL +# alias = 0.20mm NORMAL SOLUBLE FULL support_material_extruder = 5 support_material_interface_extruder = 5 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 single_extruder_multi_material_priming = 0 # MK2.5 MMU2 # -[print:0.20mm NORMAL SOLUBLE INTERFACE MK2.5] +[print:0.20mm NORMAL SOLUBLE INTERFACE @MK2.5] inherits = 0.20mm NORMAL SOLUBLE INTERFACE +# alias = 0.20mm NORMAL SOLUBLE INTERFACE support_material_extruder = 0 support_material_interface_extruder = 5 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 single_extruder_multi_material_priming = 0 # MK2.5 # -[print:0.35mm FAST MK2.5] +[print:0.35mm FAST @MK2.5] inherits = 0.35mm FAST +# alias = 0.35mm FAST compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 single_extruder_multi_material_priming = 0 -# MK2.5 MMU2 0.6 nozzle# -[print:0.35mm SOLUBLE FULL 0.6 nozzle MK2.5] +# MK2.5 MMU2 0.6 nozzle # +[print:0.35mm SOLUBLE FULL @0.6 nozzle MK2.5] inherits = *0.35mm*; *0.6nozzle*; *soluble_support* +# alias = 0.35mm SOLUBLE FULL compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and printer_model!="MK2SMM" and nozzle_diameter[0]==0.6 and num_extruders>1 external_perimeter_extrusion_width = 0.6 external_perimeter_speed = 30 @@ -1000,13 +1137,226 @@ support_material_interface_extruder = 5 top_infill_extrusion_width = 0.6 support_material_extrusion_width = 0.6 -[print:0.35mm SOLUBLE INTERFACE 0.6 nozzle MK2.5] -inherits = 0.35mm SOLUBLE FULL 0.6 nozzle MK2.5 +[print:0.35mm SOLUBLE INTERFACE @0.6 nozzle MK2.5] +inherits = 0.35mm SOLUBLE FULL @0.6 nozzle MK2.5 +# alias = 0.35mm SOLUBLE INTERFACE support_material_extruder = 0 support_material_interface_layers = 3 support_material_with_sheath = 0 support_material_xy_spacing = 80% +## MINI print profiles + +# 0.4mm nozzle + +[print:0.05mm ULTRADETAIL @MINI] +inherits = *0.05mm*; *MINI* +# alias = 0.05mm ULTRADETAIL +fill_pattern = gyroid +fill_density = 15% +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.4 +top_infill_extrusion_width = 0.4 +small_perimeter_speed = 15 +perimeter_extrusion_width = 0.4 +external_perimeter_extrusion_width = 0.4 + +[print:0.07mm ULTRADETAIL @MINI] +inherits = *0.07mm*; *MINI* +# alias = 0.07mm ULTRADETAIL +fill_pattern = gyroid +fill_density = 15% +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.4 +top_infill_extrusion_width = 0.4 +small_perimeter_speed = 15 +perimeter_extrusion_width = 0.4 +external_perimeter_extrusion_width = 0.4 + +[print:0.10mm DETAIL @MINI] +inherits = *0.10mm*; *MINI* +# alias = 0.10mm DETAIL +bridge_speed = 30 +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.4 +perimeter_speed = 40 +external_perimeter_speed = 30 +infill_speed = 80 +solid_infill_speed = 80 +top_infill_extrusion_width = 0.4 +top_solid_infill_speed = 40 +fill_pattern = gyroid +fill_density = 15% +perimeters = 3 +bridge_acceleration = 1000 + +[print:0.15mm QUALITY @MINI] +inherits = *0.15mm*; *MINI* +# alias = 0.15mm QUALITY +bridge_speed = 30 +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.4 +perimeter_speed = 40 +external_perimeter_speed = 30 +infill_speed = 80 +solid_infill_speed = 80 +top_solid_infill_speed = 40 +fill_pattern = gyroid +fill_density = 15% +bridge_flow_ratio = 0.85 + +[print:0.15mm SPEED @MINI] +inherits = *0.15mm*; *MINI* +# alias = 0.15mm SPEED +bridge_speed = 30 +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.4 +perimeter_speed = 50 +external_perimeter_speed = 40 +infill_speed = 140 +solid_infill_speed = 140 +top_solid_infill_speed = 40 +bridge_flow_ratio = 0.85 + +[print:0.20mm QUALITY @MINI] +inherits = *0.20mm*; *MINI* +# alias = 0.20mm QUALITY +bridge_speed = 30 +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.4 +perimeter_speed = 40 +external_perimeter_speed = 30 +infill_speed = 80 +solid_infill_speed = 80 +top_solid_infill_speed = 40 +fill_pattern = gyroid +fill_density = 15% + +[print:0.20mm SPEED @MINI] +inherits = *0.20mm*; *MINI* +# alias = 0.20mm SPEED +bridge_speed = 30 +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.4 +perimeter_speed = 50 +external_perimeter_speed = 40 +infill_speed = 140 +max_print_speed = 150 +solid_infill_speed = 140 +top_solid_infill_speed = 40 + +[print:0.25mm DRAFT @MINI] +inherits = *0.25mm*; *MINI* +# alias = 0.25mm DRAFT +bridge_speed = 30 +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.4 +external_perimeter_speed = 40 +infill_speed = 110 +perimeter_speed = 55 +small_perimeter_speed = 25 +solid_infill_speed = 100 +top_solid_infill_speed = 45 +first_layer_extrusion_width = 0.42 +infill_extrusion_width = 0.45 +solid_infill_extrusion_width = 0.45 +top_infill_extrusion_width = 0.4 + +# 0.25mm nozzle + +[print:0.05mm ULTRADETAIL @0.25 nozzle MINI] +inherits = *0.05mm*; *0.25nozzle*; *MINI* +# alias = 0.05mm ULTRADETAIL +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.25 +fill_pattern = grid +fill_density = 20% + +[print:0.07mm ULTRADETAIL @0.25 nozzle MINI] +inherits = *0.07mm*; *0.25nozzle*; *MINI* +# alias = 0.07mm ULTRADETAIL +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.25 +infill_speed = 30 +solid_infill_speed = 30 +support_material_speed = 30 +top_solid_infill_speed = 20 +fill_pattern = grid +fill_density = 20% + +[print:0.10mm DETAIL @0.25 nozzle MINI] +inherits = *0.10mm*; *0.25nozzleMINI*; *MINI* +# alias = 0.10mm DETAIL +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.25 +fill_pattern = grid +fill_density = 20% + +[print:0.15mm QUALITY @0.25 nozzle MINI] +inherits = *0.15mm*; *0.25nozzleMINI*; *MINI* +# alias = 0.15mm QUALITY +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.25 +fill_pattern = grid +fill_density = 20% + +# 0.6mm nozzle + +[print:0.15mm DETAIL @0.6 nozzle MINI] +inherits = *0.15mm*; *0.6nozzleMINI* +# alias = 0.15mm DETAIL +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6 +external_perimeter_speed = 35 +infill_speed = 70 +max_print_speed = 100 +perimeter_speed = 45 +solid_infill_speed = 70 +top_solid_infill_speed = 45 +infill_extrusion_width = 0.65 +solid_infill_extrusion_width = 0.65 + +[print:0.20mm DETAIL @0.6 nozzle MINI] +inherits = *0.20mm*; *0.6nozzleMINI* +# alias = 0.20mm DETAIL +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6 +external_perimeter_speed = 35 +infill_speed = 70 +max_print_speed = 100 +perimeter_speed = 45 +solid_infill_speed = 70 +top_solid_infill_speed = 45 +infill_extrusion_width = 0.65 +solid_infill_extrusion_width = 0.65 + +[print:0.30mm QUALITY @0.6 nozzle MINI] +inherits = *0.30mm*; *0.6nozzleMINI* +# alias = 0.30mm QUALITY +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6 +external_perimeter_speed = 35 +infill_speed = 65 +max_print_speed = 100 +perimeter_speed = 45 +solid_infill_speed = 65 +top_solid_infill_speed = 45 +external_perimeter_extrusion_width = 0.68 +perimeter_extrusion_width = 0.68 + +[print:0.35mm SPEED @0.6 nozzle MINI] +inherits = *0.35mm*; *0.6nozzleMINI* +# alias = 0.35mm SPEED +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6 +external_perimeter_speed = 35 +infill_speed = 60 +max_print_speed = 100 +perimeter_speed = 45 +solid_infill_speed = 60 +top_solid_infill_speed = 45 +external_perimeter_extrusion_width = 0.68 +perimeter_extrusion_width = 0.68 + +[print:0.40mm DRAFT @0.6 nozzle MINI] +inherits = *0.40mm*; *0.6nozzleMINI* +# alias = 0.40mm DRAFT +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6 +external_perimeter_speed = 35 +infill_speed = 50 +max_print_speed = 100 +perimeter_speed = 45 +solid_infill_speed = 50 +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 + # XXXXXXxxXXXXXXXXXXXXXX # XXX--- filament ---XXX # XXXXXXXXxxXXXXXXXXXXXX @@ -1038,7 +1388,7 @@ filament_settings_id = "" filament_soluble = 0 min_print_speed = 15 slowdown_below_layer_time = 20 -start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}30{endif} ; Filament gcode" +start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}30{endif} ; Filament gcode" [filament:*PLA*] inherits = *common* @@ -1047,7 +1397,7 @@ bridge_fan_speed = 100 disable_fan_first_layers = 1 fan_always_on = 1 fan_below_layer_time = 100 -filament_colour = #FF3232 +filament_colour = #FF8000 filament_max_volumetric_speed = 15 filament_type = PLA first_layer_bed_temperature = 60 @@ -1055,7 +1405,7 @@ first_layer_temperature = 215 max_fan_speed = 100 min_fan_speed = 100 temperature = 210 -start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{elsif nozzle_diameter[0]==0.6}18{else}30{endif} ; Filament gcode" +start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{elsif nozzle_diameter[0]==0.6}18{else}30{endif} ; Filament gcode" [filament:*PET*] inherits = *common* @@ -1066,7 +1416,7 @@ fan_always_on = 1 fan_below_layer_time = 20 filament_colour = #FF8000 filament_max_volumetric_speed = 8 -filament_type = PET +filament_type = PETG first_layer_bed_temperature = 85 first_layer_temperature = 230 max_fan_speed = 50 @@ -1075,11 +1425,11 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{el temperature = 240 filament_retract_length = 1.4 filament_retract_lift = 0.2 -compatible_printers_condition = printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) [filament:*PET06*] inherits = *PET* -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) filament_max_volumetric_speed = 15 [filament:*PETMMU1*] @@ -1089,6 +1439,64 @@ filament_retract_speed = nil filament_retract_lift = 0.2 compatible_printers_condition = printer_model=="MK2SMM" +[filament:*PETMINI*] +inherits = *PET* +filament_retract_length = nil +filament_retract_speed = 40 +filament_deretract_speed = 25 +filament_retract_lift = nil +filament_retract_before_travel = 1 +filament_max_volumetric_speed = 7 +compatible_printers_condition = printer_model=="MINI" +start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.6}0.12{else}0.2{endif} ; Filament gcode" + +[filament:*PETMINI06*] +inherits = *PET* +filament_retract_length = nil +filament_retract_speed = 40 +filament_deretract_speed = 25 +filament_retract_lift = nil +filament_retract_before_travel = 1 +compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]==0.6 +start_filament_gcode = "M900 K0.12 ; Filament gcode" +filament_max_volumetric_speed = 13 + +[filament:*ABSMINI*] +inherits = *ABS* +bed_temperature = 100 +filament_retract_length = 2.7 +filament_retract_speed = nil +filament_deretract_speed = nil +filament_retract_lift = nil +filament_retract_before_travel = 3 +filament_wipe = 0 +filament_max_volumetric_speed = 10 +compatible_printers_condition = printer_model=="MINI" +start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.6}0.12{else}0.2{endif} ; Filament gcode" + +[filament:*FLEXMINI*] +inherits = *FLEX* +first_layer_temperature = 245 +temperature = 245 +filament_retract_length = 4 +filament_retract_speed = 40 +filament_deretract_speed = 15 +filament_retract_lift = 0 +filament_retract_before_travel = 7 +filament_wipe = 0 +bridge_fan_speed = 80 +fan_always_on = 1 +cooling = 0 +max_fan_speed = 50 +min_fan_speed = 50 +filament_max_volumetric_speed = 1.35 +compatible_printers_condition = nozzle_diameter[0]>0.35 and printer_model=="MINI" +disable_fan_first_layers = 4 +extrusion_multiplier = 1.15 +filament_density = 1.22 +filament_colour = #F2F200 +start_filament_gcode = "M900 K0 ; Filament gcode" + [filament:*ABS*] inherits = *common* bed_temperature = 110 @@ -1107,15 +1515,16 @@ max_fan_speed = 30 min_fan_speed = 20 temperature = 255 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{elsif nozzle_diameter[0]==0.6}18{else}30{endif} ; Filament gcode" +compatible_printers_condition = printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) [filament:*FLEX*] inherits = *common* bed_temperature = 50 -bridge_fan_speed = 100 +bridge_fan_speed = 80 # For now, all but selected filaments are disabled for the MMU 2.0 -compatible_printers_condition = nozzle_diameter[0]>0.35 and printer_model!="MK2SMM" and num_extruders==1 && ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]>0.35 and printer_model!="MK2SMM" and printer_model!="MINI" and num_extruders==1 && ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material) cooling = 0 -disable_fan_first_layers = 1 +disable_fan_first_layers = 3 extrusion_multiplier = 1.2 fan_always_on = 0 fan_below_layer_time = 100 @@ -1128,29 +1537,51 @@ max_fan_speed = 90 min_fan_speed = 70 start_filament_gcode = "M900 K0"; Filament gcode" temperature = 240 -filament_retract_length = 0.4 +filament_retract_length = 0.8 +filament_deretract_speed = 25 filament_retract_lift = 0 +filament_wipe = 0 -[filament:ColorFabb Brass Bronze] +[filament:ColorFabb bronzeFill] inherits = *PLA* -# For now, all but selected filaments are disabled for the MMU 2.0 +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 -filament_cost = 56.64 +filament_cost = 72.89 filament_density = 3.9 filament_colour = #804040 filament_max_volumetric_speed = 9 -filament_notes = "List of materials tested with standard print settings:\n\nColorFabb bronzeFill\nColorFabb brassFill\nColorFabb steelFill\nColorFabb copperFill" + +[filament:ColorFabb steelFill] +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 +filament_cost = 72.89 +filament_density = 3.13 +filament_colour = #808080 +filament_max_volumetric_speed = 8 + +[filament:ColorFabb copperFill] +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 +filament_cost = 72.89 +filament_density = 3.9 +filament_colour = #82603E +filament_max_volumetric_speed = 9 [filament:ColorFabb HT] inherits = *PET* +filament_vendor = ColorFabb bed_temperature = 110 bridge_fan_speed = 30 cooling = 1 disable_fan_first_layers = 3 fan_always_on = 0 fan_below_layer_time = 10 -filament_cost = 58.66 +filament_cost = 65.66 filament_density = 1.18 first_layer_bed_temperature = 105 first_layer_temperature = 270 @@ -1161,38 +1592,40 @@ temperature = 270 [filament:ColorFabb PLA-PHA] inherits = *PLA* -filament_cost = 55.5 +filament_vendor = ColorFabb +filament_cost = 52.46 filament_density = 1.24 [filament:ColorFabb woodFill] inherits = *PLA* -# For now, all but selected filaments are disabled for the MMU 2.0 +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 -filament_cost = 62.9 +filament_cost = 58.30 filament_density = 1.15 filament_colour = #dfc287 -filament_max_volumetric_speed = 10 +filament_max_volumetric_speed = 9 first_layer_temperature = 200 -start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode" +start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/}0{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}0{else}10{endif}; Filament gcode" temperature = 200 [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 -filament_cost = 45.45 +filament_cost = 58.30 filament_density = 1.18 filament_colour = #634d33 filament_max_volumetric_speed = 6 first_layer_temperature = 220 -start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode" +start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/}0{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}0{else}10{endif}; Filament gcode" temperature = 220 [filament:ColorFabb XT] inherits = *PET* -filament_type = PET -filament_cost = 62.9 +filament_vendor = ColorFabb +filament_cost = 58.30 filament_density = 1.27 first_layer_bed_temperature = 90 first_layer_temperature = 260 @@ -1200,9 +1633,9 @@ temperature = 270 [filament:ColorFabb XT-CF20] inherits = *PET* -compatible_printers_condition = nozzle_diameter[0]>0.35 and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +filament_vendor = ColorFabb extrusion_multiplier = 1.2 -filament_cost = 80.65 +filament_cost = 72.89 filament_density = 1.35 filament_colour = #804040 filament_max_volumetric_speed = 1 @@ -1215,7 +1648,8 @@ filament_retract_lift = 0.2 [filament:ColorFabb nGen] inherits = *PET* -filament_cost = 21.2 +filament_vendor = ColorFabb +filament_cost = 52.46 filament_density = 1.2 bridge_fan_speed = 40 fan_always_on = 0 @@ -1227,7 +1661,8 @@ min_fan_speed = 20 [filament:ColorFabb nGen flex] inherits = *FLEX* -filament_cost = 0 +filament_vendor = ColorFabb +filament_cost = 58.30 filament_density = 1 bed_temperature = 85 bridge_fan_speed = 40 @@ -1243,24 +1678,34 @@ min_fan_speed = 20 temperature = 260 filament_retract_length = nil filament_retract_lift = 0 -compatible_printers_condition = nozzle_diameter[0]>0.35 and num_extruders==1 && ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]>0.35 and printer_model!="MINI" and num_extruders==1 && ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material) [filament:E3D Edge] inherits = *PET* +filament_vendor = E3D filament_cost = 56.9 filament_density = 1.26 filament_type = EDGE +compatible_printers_condition = printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) [filament:E3D PC-ABS] inherits = *ABS* +filament_vendor = E3D filament_cost = 0 filament_type = PC filament_density = 1.05 first_layer_temperature = 270 temperature = 270 +[filament:Fillamentum PLA] +inherits = *PLA* +filament_vendor = Fillamentum +filament_cost = 25.4 +filament_density = 1.24 + [filament:Fillamentum ABS] inherits = *ABS* +filament_vendor = Fillamentum filament_cost = 32.4 filament_density = 1.04 first_layer_temperature = 240 @@ -1268,6 +1713,7 @@ temperature = 240 [filament:Fillamentum ASA] inherits = *ABS* +filament_vendor = Fillamentum filament_cost = 38.7 filament_density = 1.07 fan_always_on = 1 @@ -1282,6 +1728,7 @@ filament_type = ASA [filament:Prusament ASA] inherits = *ABS* +filament_vendor = Prusa Research filament_cost = 35.28 filament_density = 1.07 fan_always_on = 1 @@ -1301,6 +1748,7 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{el [filament:Fillamentum CPE] inherits = *PET* +filament_vendor = Fillamentum filament_cost = 54.1 filament_density = 1.25 filament_type = CPE @@ -1312,7 +1760,7 @@ temperature = 275 [filament:Fillamentum Timberfill] inherits = *PLA* -# For now, all but selected filaments are disabled for the MMU 2.0 +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 filament_cost = 68 @@ -1320,29 +1768,33 @@ filament_density = 1.15 filament_colour = #804040 filament_max_volumetric_speed = 10 first_layer_temperature = 190 -start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode" +start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/}0{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}0{else}10{endif}; Filament gcode" temperature = 190 [filament:Generic ABS] inherits = *ABS* +filament_vendor = Generic filament_cost = 27.82 filament_density = 1.04 filament_notes = "List of materials tested with standard ABS print settings:\n\nEsun ABS\nFil-A-Gehr ABS\nHatchboxABS\nPlasty Mladec ABS" [filament:Generic PET] inherits = *PET* +filament_vendor = Generic filament_cost = 27.82 filament_density = 1.27 filament_notes = "List of manufacturers tested with standard PET print settings:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladec PETG" [filament:Generic PLA] inherits = *PLA* +filament_vendor = Generic filament_cost = 25.4 filament_density = 1.24 filament_notes = "List of materials tested with standard PLA print settings:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladec PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nVerbatim PLA\nVerbatim BVOH" [filament:Generic FLEX] inherits = *FLEX* +filament_vendor = Generic filament_cost = 82 filament_density = 1.22 filament_max_volumetric_speed = 1.2 @@ -1350,8 +1802,61 @@ filament_retract_length = 0 filament_retract_speed = nil filament_retract_lift = nil +[filament:SainSmart TPU] +inherits = *FLEX* +filament_vendor = SainSmart +fan_always_on = 1 +filament_max_volumetric_speed = 2.5 +extrusion_multiplier = 1.15 +first_layer_temperature = 230 +first_layer_bed_temperature = 50 +temperature = 230 +bed_temperature = 50 +bridge_fan_speed = 100 +max_fan_speed = 80 +min_fan_speed = 80 +filament_retract_before_travel = 3 +filament_cost = 32.99 +filament_density = 1.21 +filament_retract_length = 1 +filament_retract_speed = nil +filament_deretract_speed = 25 +filament_retract_lift = 0 +filament_wipe = 0 +disable_fan_first_layers = 3 +min_print_speed = 15 +slowdown_below_layer_time = 10 +cooling = 1 + +[filament:Filatech FilaFlex40] +inherits = *FLEX* +filament_vendor = Filatech +fan_always_on = 1 +filament_max_volumetric_speed = 2.5 +extrusion_multiplier = 1.15 +first_layer_temperature = 230 +first_layer_bed_temperature = 50 +temperature = 230 +bed_temperature = 50 +bridge_fan_speed = 100 +max_fan_speed = 50 +min_fan_speed = 50 +filament_retract_before_travel = 3 +filament_cost = 51.45 +filament_density = 1.22 +filament_retract_length = 2 +filament_retract_speed = 50 +filament_deretract_speed = 25 +filament_retract_lift = 0 +filament_wipe = 0 +disable_fan_first_layers = 3 +min_print_speed = 15 +slowdown_below_layer_time = 10 +cooling = 1 + [filament:Polymaker PC-Max] inherits = *ABS* +filament_vendor = Polymaker filament_cost = 77.3 filament_density = 1.20 filament_type = PC @@ -1360,9 +1865,11 @@ filament_colour = #3A80CA first_layer_bed_temperature = 100 first_layer_temperature = 270 temperature = 270 +bridge_fan_speed = 0 [filament:PrimaSelect PVA+] inherits = *PLA* +filament_vendor = PrimaSelect filament_cost = 108 filament_density = 1.23 cooling = 0 @@ -1374,11 +1881,12 @@ filament_ramming_parameters = "120 100 8.3871 8.6129 8.93548 9.22581 9.48387 9.7 filament_soluble = 1 filament_type = PVA first_layer_temperature = 195 -start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode" +start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/}0{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}0{else}10{endif}; Filament gcode" temperature = 195 [filament:Prusa ABS] inherits = *ABS* +filament_vendor = Made for Prusa filament_cost = 27.82 filament_density = 1.08 filament_notes = "List of materials tested with standard ABS print settings:\n\nEsun ABS\nFil-A-Gehr ABS\nHatchboxABS\nPlasty Mladec ABS" @@ -1396,11 +1904,15 @@ filament_unload_time = 12 filament_loading_speed = 14 filament_unloading_speed = 20 -[filament:Generic ABS MMU2] +[filament:Generic ABS @MMU2] inherits = *ABS MMU2* +# alias = Generic ABS +filament_vendor = Generic -[filament:Prusament ASA MMU2] +[filament:Prusament ASA @MMU2] inherits = *ABS MMU2* +# alias = Prusament ASA +filament_vendor = Prusa Research filament_cost = 35.28 filament_density = 1.07 fan_always_on = 1 @@ -1421,11 +1933,14 @@ filament_type = ASA filament_colour = #FFF2EC start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{elsif nozzle_diameter[0]==0.6}12{else}20{endif} ; Filament gcode" -[filament:Prusa ABS MMU2] +[filament:Prusa ABS @MMU2] inherits = *ABS MMU2* +# alias = Prusa ABS +filament_vendor = Made for Prusa [filament:Prusa HIPS] inherits = *ABS* +filament_vendor = Made for Prusa filament_cost = 27.3 filament_density = 1.04 bridge_fan_speed = 50 @@ -1439,33 +1954,39 @@ filament_type = HIPS first_layer_temperature = 220 max_fan_speed = 20 min_fan_speed = 20 -start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode" +start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/}0{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}0{else}10{endif}; Filament gcode" temperature = 220 [filament:Prusa PET] inherits = *PET* +filament_vendor = Made for Prusa filament_cost = 27.82 filament_density = 1.27 filament_notes = "List of manufacturers tested with standard PET print settings:\n\nE3D Edge\nPlasty Mladec PETG" -compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) [filament:Prusament PETG] inherits = *PET* +filament_vendor = Prusa Research first_layer_temperature = 240 temperature = 250 filament_cost = 24.99 filament_density = 1.27 filament_type = PETG -compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Prusa PET 0.6 nozzle] +[filament:Prusa PET @0.6 nozzle] inherits = *PET06* +# alias = Prusa PET +filament_vendor = Made for Prusa filament_cost = 27.82 filament_density = 1.27 filament_notes = "List of manufacturers tested with standard PET print settings:\n\nE3D Edge\nPlasty Mladec PETG" -[filament:Prusament PETG 0.6 nozzle] +[filament:Prusament PETG @0.6 nozzle] inherits = *PET06* +# alias = Prusament PETG +filament_vendor = Prusa Research first_layer_temperature = 240 temperature = 250 filament_cost = 24.99 @@ -1496,34 +2017,48 @@ inherits = *PET MMU2* compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material filament_max_volumetric_speed = 13 -[filament:Generic PET MMU2] +[filament:Generic PET @MMU2] inherits = *PET MMU2* +# alias = Generic PET +filament_vendor = Generic -[filament:Prusa PET MMU2] +[filament:Prusa PET @MMU2] inherits = *PET MMU2* +# alias = Prusa PET +filament_vendor = Made for Prusa -[filament:Prusament PETG MMU2] +[filament:Prusament PETG @MMU2] inherits = *PET MMU2* filament_type = PETG +# alias = Prusament PETG +filament_vendor = Prusa Research -[filament:Generic PET MMU2 0.6 nozzle] +[filament:Generic PET @MMU2 0.6 nozzle] inherits = *PET MMU2 06* +# alias = Generic PET +filament_vendor = Generic -[filament:Prusa PET MMU2 0.6 nozzle] +[filament:Prusa PET @MMU2 0.6 nozzle] inherits = *PET MMU2 06* +# alias = Prusa PET +filament_vendor = Made for Prusa -[filament:Prusament PETG MMU2 0.6 nozzle] +[filament:Prusament PETG @MMU2 0.6 nozzle] inherits = *PET MMU2 06* filament_type = PETG +# alias = Prusament PETG +filament_vendor = Prusa Research [filament:Prusa PLA] inherits = *PLA* +filament_vendor = Made for Prusa filament_cost = 25.4 filament_density = 1.24 -filament_notes = "List of materials tested with standard PLA print settings:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFiberlogy PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladec PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nVerbatim PLA\nAmazonBasics PLA" +filament_notes = "List of materials tested with standard PLA print settings:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFiberlogy PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladec PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nAmazonBasics PLA" [filament:Prusament PLA] inherits = *PLA* +filament_vendor = Prusa Research temperature = 215 filament_cost = 24.99 filament_density = 1.24 @@ -1545,23 +2080,28 @@ filament_loading_speed_start = 19 filament_minimal_purge_on_wipe_tower = 15 filament_unloading_speed_start = 100 -[filament:Generic PLA MMU2] +[filament:Generic PLA @MMU2] inherits = *PLA MMU2* +filament_vendor = Generic -[filament:Prusa PLA MMU2] +[filament:Prusa PLA @MMU2] inherits = *PLA MMU2* +filament_vendor = Made for Prusa -[filament:Prusament PLA MMU2] +[filament:Prusament PLA @MMU2] inherits = *PLA MMU2* +filament_vendor = Prusa Research [filament:SemiFlex or Flexfill 98A] inherits = *FLEX* +filament_vendor = Generic filament_cost = 82 filament_density = 1.22 filament_max_volumetric_speed = 1.35 [filament:Taulman Bridge] inherits = *common* +filament_vendor = Taulman filament_cost = 40 filament_density = 1.13 bed_temperature = 90 @@ -1578,11 +2118,12 @@ first_layer_bed_temperature = 60 first_layer_temperature = 240 max_fan_speed = 5 min_fan_speed = 0 -start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode" +start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/}0{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}0{else}10{endif}; Filament gcode" temperature = 250 [filament:Taulman T-Glase] inherits = *PET* +filament_vendor = Taulman filament_cost = 40 filament_density = 1.27 bridge_fan_speed = 40 @@ -1594,8 +2135,15 @@ max_fan_speed = 5 min_fan_speed = 0 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}30{endif}; Filament gcode" +[filament:Verbatim PLA] +inherits = *PLA* +filament_vendor = Verbatim +filament_cost = 42.99 +filament_density = 1.24 + [filament:Verbatim BVOH] inherits = *common* +filament_vendor = Verbatim filament_cost = 218 filament_density = 1.23 bed_temperature = 60 @@ -1614,11 +2162,12 @@ first_layer_bed_temperature = 60 first_layer_temperature = 215 max_fan_speed = 100 min_fan_speed = 100 -start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode" +start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/}0{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}0{else}10{endif}; Filament gcode" temperature = 210 -[filament:Verbatim BVOH MMU2] +[filament:Verbatim BVOH @MMU2] inherits = Verbatim BVOH +filament_vendor = Verbatim compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material temperature = 195 fan_always_on = 1 @@ -1636,8 +2185,9 @@ filament_unloading_speed = 20 filament_unloading_speed_start = 100 filament_loading_speed_start = 19 -[filament:PrimaSelect PVA+ MMU2] +[filament:PrimaSelect PVA+ @MMU2] inherits = *common* +filament_vendor = PrimaSelect compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material bed_temperature = 60 bridge_fan_speed = 100 @@ -1676,6 +2226,7 @@ temperature = 195 [filament:Verbatim PP] inherits = *common* +filament_vendor = Verbatim filament_cost = 72 filament_density = 0.89 bed_temperature = 100 @@ -1693,13 +2244,14 @@ first_layer_bed_temperature = 100 first_layer_temperature = 220 max_fan_speed = 100 min_fan_speed = 100 -start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode" +start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/}0{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}0{else}10{endif}; Filament gcode" temperature = 220 ## Filaments MMU1 -[filament:ColorFabb HT MMU1] +[filament:ColorFabb HT @MMU1] inherits = *PETMMU1* +filament_vendor = ColorFabb bed_temperature = 110 bridge_fan_speed = 30 cooling = 1 @@ -1715,17 +2267,20 @@ min_fan_speed = 10 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}45{endif}; Filament gcode" temperature = 270 -[filament:ColorFabb XT MMU1] +[filament:ColorFabb XT @MMU1] inherits = *PETMMU1* -filament_type = PET +filament_vendor = ColorFabb +filament_type = PETG filament_cost = 62.9 filament_density = 1.27 first_layer_bed_temperature = 90 first_layer_temperature = 260 temperature = 270 -[filament:ColorFabb XT-CF20 MMU1] +[filament:ColorFabb XT-CF20 @MMU1] inherits = *PETMMU1* +# alias = ColorFabb XT-CF20 +filament_vendor = ColorFabb compatible_printers_condition = nozzle_diameter[0]>0.35 and printer_model=="MK2SMM" extrusion_multiplier = 1.2 filament_cost = 80.65 @@ -1737,8 +2292,10 @@ first_layer_temperature = 260 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}30{endif}; Filament gcode" temperature = 260 -[filament:ColorFabb nGen MMU1] +[filament:ColorFabb nGen @MMU1] inherits = *PETMMU1* +# alias = ColorFabb nGen +filament_vendor = ColorFabb filament_cost = 21.2 filament_density = 1.2 bridge_fan_speed = 40 @@ -1749,15 +2306,19 @@ first_layer_temperature = 240 max_fan_speed = 35 min_fan_speed = 20 -[filament:E3D Edge MMU1] +[filament:E3D Edge @MMU1] inherits = *PETMMU1* +# alias = E3D Edge +filament_vendor = E3D filament_cost = 56.9 filament_density = 1.26 filament_type = EDGE filament_notes = "List of manufacturers tested with standard PET print settings:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladec PETG" -[filament:Fillamentum CPE MMU1] +[filament:Fillamentum CPE @MMU1] inherits = *PETMMU1* +# alias = Fillamentum CPE +filament_vendor = Fillamentum filament_cost = 54.1 filament_density = 1.25 filament_type = CPE @@ -1767,28 +2328,36 @@ max_fan_speed = 50 min_fan_speed = 50 temperature = 275 -[filament:Generic PET MMU1] +[filament:Generic PET @MMU1] inherits = *PETMMU1* +# alias = Generic PET +filament_vendor = Generic filament_cost = 27.82 filament_density = 1.27 filament_notes = "List of manufacturers tested with standard PET print settings:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladec PETG" -[filament:Prusa PET MMU1] +[filament:Prusa PET @MMU1] inherits = *PETMMU1* +# alias = Prusa PET +filament_vendor = Made for Prusa filament_cost = 27.82 filament_density = 1.27 filament_notes = "List of manufacturers tested with standard PET print settings:\n\nE3D Edge\nPlasty Mladec PETG" -[filament:Prusament PETG MMU1] +[filament:Prusament PETG @MMU1] inherits = *PETMMU1* +# alias = Prusament PETG +filament_vendor = Prusa Research first_layer_temperature = 240 temperature = 250 filament_cost = 24.99 filament_density = 1.27 filament_type = PETG -[filament:Taulman T-Glase MMU1] +[filament:Taulman T-Glase @MMU1] inherits = *PETMMU1* +# alias = Taulman T-Glase +filament_vendor = Taulman filament_cost = 40 filament_density = 1.27 bridge_fan_speed = 40 @@ -1800,8 +2369,9 @@ max_fan_speed = 5 min_fan_speed = 0 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}30{endif}; Filament gcode" -[filament:SemiFlex or Flexfill 98A MMU1] +[filament:SemiFlex or Flexfill 98A @MMU1] inherits = *FLEX* +filament_vendor = Generic filament_cost = 82 filament_density = 1.22 filament_max_volumetric_speed = 1.35 @@ -1810,8 +2380,10 @@ filament_retract_speed = nil filament_retract_lift = nil compatible_printers_condition = printer_model=="MK2SMM" -[filament:Generic FLEX MMU1] +[filament:Generic FLEX @MMU1] inherits = *FLEX* +# alias = Generic FLEX +filament_vendor = Generic filament_cost = 82 filament_density = 1.22 filament_max_volumetric_speed = 1.2 @@ -1820,6 +2392,326 @@ filament_retract_speed = nil filament_retract_lift = nil compatible_printers_condition = printer_model=="MK2SMM" +## Filaments MINI + +[filament:Generic PET @MINI] +inherits = Generic PET; *PETMINI* +# alias = Generic PET +filament_cost = 27.82 +filament_density = 1.27 +compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.6 + +[filament:Generic ABS @MINI] +inherits = Generic ABS; *ABSMINI* +# alias = Generic ABS +filament_cost = 27.82 +filament_density = 1.08 + +[filament:Prusament PETG @MINI] +inherits = Prusament PETG; *PETMINI* +# alias = Prusament PETG +first_layer_temperature = 240 +temperature = 250 +filament_density = 1.27 +filament_cost = 24.99 +compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.6 + +[filament:Prusament PETG @0.6 nozzle MINI] +inherits = Prusament PETG; *PETMINI06* +# alias = Prusament PETG +first_layer_temperature = 240 +temperature = 250 +filament_density = 1.27 +filament_cost = 24.99 + +[filament:Generic PET @0.6 nozzle MINI] +inherits = Generic PET; *PETMINI06* +# alias = Generic PET + +[filament:Prusament ASA @MINI] +inherits = Prusament ASA; *ABSMINI* +# alias = Prusament ASA +first_layer_temperature = 260 +first_layer_bed_temperature = 100 +temperature = 260 +bed_temperature = 100 +fan_always_on = 1 +cooling = 1 +min_fan_speed = 20 +max_fan_speed = 20 +min_print_speed = 15 +slowdown_below_layer_time = 15 +disable_fan_first_layers = 4 +filament_type = ASA +filament_colour = #FFF2EC +filament_cost = 35.28 +filament_density = 1.07 + +[filament:Fillamentum Flexfill 98A @MINI] +inherits = SemiFlex or Flexfill 98A; *FLEXMINI* +# alias = Fillamentum Flexfill 98A +filament_vendor = Fillamentum +first_layer_temperature = 240 +temperature = 240 +filament_max_volumetric_speed = 1.35 + +[filament:Generic FLEX @MINI] +inherits = SemiFlex or Flexfill 98A; *FLEXMINI* +# alias = Semiflex or Flex 98A +filament_vendor = Generic +fan_always_on = 0 +bridge_fan_speed = 80 +first_layer_temperature = 240 +temperature = 240 +filament_retract_length = 3 +filament_max_volumetric_speed = 1.35 + +[filament:AmazonBasics TPU @MINI] +inherits = *FLEXMINI* +# alias = AmazonBasics TPU +filament_vendor = AmazonBasics +filament_max_volumetric_speed = 1.5 +first_layer_temperature = 235 +first_layer_bed_temperature = 50 +temperature = 235 +bed_temperature = 50 +bridge_fan_speed = 100 +max_fan_speed = 80 +min_fan_speed = 80 +filament_retract_before_travel = 5 +filament_cost = 19.99 +filament_density = 1.21 + +[filament:SainSmart TPU @MINI] +inherits = *FLEXMINI* +# alias = SainSmart TPU +filament_vendor = SainSmart +filament_max_volumetric_speed = 1.8 +first_layer_temperature = 235 +first_layer_bed_temperature = 50 +temperature = 235 +bed_temperature = 50 +bridge_fan_speed = 100 +max_fan_speed = 80 +min_fan_speed = 80 +filament_retract_before_travel = 5 +min_print_speed = 15 +slowdown_below_layer_time = 10 +cooling = 1 +filament_cost = 32.99 +filament_density = 1.21 + +[filament:Filatech FilaFlex40 @MINI] +inherits = *FLEXMINI* +# alias = Filatech FilaFlex40 +filament_vendor = Filatech +filament_max_volumetric_speed = 1.8 +fan_always_on = 1 +first_layer_temperature = 240 +first_layer_bed_temperature = 55 +temperature = 240 +bed_temperature = 55 +filament_retract_length = 4 +filament_retract_before_travel = 5 +bridge_fan_speed = 80 +max_fan_speed = 50 +min_fan_speed = 50 +min_print_speed = 15 +slowdown_below_layer_time = 10 +cooling = 1 +filament_cost = 51.45 + +[filament:Fillamentum Flexfill 92A @MINI] +inherits = *FLEXMINI* +# alias = Fillamentum Flexfill 92A +filament_vendor = Fillamentum +first_layer_temperature = 245 +temperature = 245 +filament_retract_length = 3 +filament_retract_speed = 40 +filament_deretract_speed = 15 +filament_retract_lift = 0 +filament_retract_before_travel = 7 +filament_wipe = 0 +filament_density = 1.20 +filament_cost = 33.95 +bridge_fan_speed = 70 +fan_always_on = 1 +cooling = 0 +max_fan_speed = 50 +min_fan_speed = 50 +filament_max_volumetric_speed = 1.2 +compatible_printers_condition = nozzle_diameter[0]>0.35 and printer_model=="MINI" +disable_fan_first_layers = 4 +extrusion_multiplier = 1.2 +start_filament_gcode = "M900 K0 ; Filament gcode" + +[filament:Fillamentum CPE @MINI] +inherits = Fillamentum CPE; *PETMINI* +# alias = Fillamentum CPE +first_layer_temperature = 265 +first_layer_bed_temperature = 90 +temperature = 265 +filament_type = CPE +filament_cost = 54.1 +filament_density = 1.25 + +[filament:ColorFabb nGen @MINI] +inherits = ColorFabb nGen; *PETMINI* +# alias = ColorFabb nGen +filament_cost = 52.46 +filament_density = 1.2 + +[filament:E3D PC-ABS @MINI] +inherits = E3D PC-ABS; *ABSMINI* +# alias = E3D PC-ABS +filament_density = 1.05 +filament_cost = 28.80 + +[filament:Fillamentum ABS @MINI] +inherits = Fillamentum ABS; *ABSMINI* +# alias = Fillamentum ABS +filament_cost = 32.4 +filament_density = 1.04 + +[filament:Fillamentum ASA @MINI] +inherits = Fillamentum ASA; *ABSMINI* +# alias = Fillamentum ASA +first_layer_temperature = 255 +first_layer_bed_temperature = 100 +temperature = 255 +bed_temperature = 100 +fan_always_on = 1 +cooling = 1 +min_fan_speed = 20 +max_fan_speed = 20 +min_print_speed = 15 +slowdown_below_layer_time = 15 +disable_fan_first_layers = 4 +filament_type = ASA +filament_colour = #FFF2EC +filament_cost = 38.7 +filament_density = 1.07 + +[filament:Polymaker PC-Max @MINI] +inherits = Polymaker PC-Max; *ABSMINI* +# alias = Polymaker PC-Max +filament_type = PC +bed_temperature = 100 +filament_colour = #3A80CA +first_layer_bed_temperature = 100 +first_layer_temperature = 270 +temperature = 270 +bridge_fan_speed = 0 +filament_cost = 77.3 +filament_density = 1.20 + +[filament:Prusa ABS @MINI] +inherits = *ABSMINI* +# alias = Prusa ABS +filament_vendor = Made for Prusa +filament_cost = 27.82 +filament_density = 1.08 + +[filament:Generic HIPS @MINI] +inherits = *ABSMINI* +# alias = Generic HIPS +filament_vendor = Generic +filament_cost = 27.3 +filament_density = 1.04 +bridge_fan_speed = 50 +cooling = 1 +extrusion_multiplier = 0.9 +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 +temperature = 230 + +[filament:ColorFabb HT @MINI] +inherits = *PETMINI* +# alias = ColorFabb HT +filament_vendor = ColorFabb +bed_temperature = 100 +bridge_fan_speed = 30 +cooling = 1 +disable_fan_first_layers = 3 +fan_always_on = 0 +fan_below_layer_time = 10 +filament_cost = 58.66 +filament_density = 1.18 +first_layer_bed_temperature = 100 +first_layer_temperature = 270 +max_fan_speed = 20 +min_fan_speed = 10 +temperature = 270 + +[filament:ColorFabb XT @MINI] +inherits = *PETMINI* +# alias = ColorFabb XT +filament_vendor = ColorFabb +filament_type = PETG +filament_cost = 62.9 +filament_density = 1.27 +first_layer_bed_temperature = 90 +first_layer_temperature = 260 +temperature = 270 + +[filament:ColorFabb XT-CF20 @MINI] +inherits = *PETMINI* +# alias = ColorFabb XT-CF20 +filament_vendor = ColorFabb +compatible_printers_condition = nozzle_diameter[0]>0.35 and printer_model=="MINI" +extrusion_multiplier = 1.2 +filament_cost = 80.65 +filament_density = 1.35 +filament_colour = #804040 +filament_max_volumetric_speed = 1 +first_layer_bed_temperature = 90 +first_layer_temperature = 260 +temperature = 260 + +[filament:Taulman T-Glase @MINI] +inherits = *PETMINI* +# alias = Taulman T-Glase +filament_vendor = Taulman +filament_cost = 40 +filament_density = 1.27 +bridge_fan_speed = 40 +cooling = 0 +fan_always_on = 0 +first_layer_bed_temperature = 90 +first_layer_temperature = 240 +max_fan_speed = 5 +min_fan_speed = 0 + +[filament:E3D Edge @MINI] +inherits = *PETMINI* +# alias = E3D Edge +filament_vendor = E3D +filament_cost = 56.9 +filament_density = 1.26 +filament_type = EDGE + +[filament:Prusa PET @MINI] +inherits = *PETMINI* +# alias = Prusa PET +filament_vendor = Made for Prusa +filament_cost = 27.82 +filament_density = 1.27 +compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.6 + +[filament:Prusa PET @0.6 nozzle MINI] +inherits = *PETMINI06* +# alias = Prusa PET +filament_vendor = Made for Prusa +filament_cost = 27.82 +filament_density = 1.27 + [sla_print:*common*] compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_SL1.*/ layer_height = 0.05 @@ -1867,7 +2759,7 @@ support_head_front_diameter = 0.5 support_head_penetration = 0.5 support_pillar_diameter = 1.3 -########### Materials 0.025 +########### Materials [sla_material:*common 0.05*] compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_SL1.*/ @@ -1901,358 +2793,725 @@ initial_layer_height = 0.1 ########### Materials 0.025 -[sla_material:BlueCast Phrozen Wax 0.025] +[sla_material:3DM-ABS @0.025] +inherits = *common 0.025* +exposure_time = 12 +initial_exposure_time = 35 +material_type = Tough +material_vendor = 3DM + +[sla_material:3DM-Vulcan Gold @0.025] +inherits = *common 0.025* +exposure_time = 12 +initial_exposure_time = 30 +material_type = Tough +material_vendor = 3DM + +[sla_material:BlueCast Phrozen Wax @0.025] inherits = *common 0.025* exposure_time = 15 initial_exposure_time = 50 +material_type = Tough +material_vendor = BlueCast -[sla_material:BlueCast EcoGray 0.025] +[sla_material:BlueCast EcoGray @0.025] inherits = *common 0.025* exposure_time = 6 initial_exposure_time = 40 +material_type = Tough +material_vendor = BlueCast -[sla_material:BlueCast Keramaster Dental 0.025] +[sla_material:BlueCast Kera Master Dental @0.025] inherits = *common 0.025* exposure_time = 6 initial_exposure_time = 45 +material_type = Dental +material_vendor = BlueCast -[sla_material:BlueCast X10 0.025] +[sla_material:BlueCast X10 @0.025] inherits = *common 0.025* exposure_time = 4 initial_exposure_time = 100 +material_type = Tough +material_vendor = BlueCast -[sla_material:Prusa Orange Tough 0.025] +[sla_material:Esun Bio-Photopolymer Resin White @0.025] +inherits = *common 0.025* +exposure_time = 5 +initial_exposure_time = 30 +material_type = Tough +material_vendor = Esun + +[sla_material:Esun Standard Resin Black @0.025] +inherits = *common 0.025* +exposure_time = 6 +initial_exposure_time = 30 +material_type = Tough +material_vendor = Esun + +[sla_material:Photocentric Ash Grey @0.025] +inherits = *common 0.025* +exposure_time = 9 +initial_exposure_time = 30 +material_type = Tough +material_vendor = Photocentric + +[sla_material:Resinworks 3D Violet @0.025] +inherits = *common 0.025* +exposure_time = 15 +initial_exposure_time = 30 +material_type = Tough +material_vendor = Resinworks 3D + +[sla_material:Resinworks 3D Green @0.025] +inherits = *common 0.025* +exposure_time = 17 +initial_exposure_time = 30 +material_type = Tough +material_vendor = Resinworks 3D + +## Prusa +[sla_material:Prusa Orange Tough @0.025] inherits = *common 0.025* exposure_time = 6 initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa Grey Tough 0.025] +[sla_material:Prusa Grey Tough @0.025] inherits = *common 0.025* exposure_time = 7 initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa Azure Blue Tough 0.025] +[sla_material:Prusa Azure Blue Tough @0.025] inherits = *common 0.025* exposure_time = 7 initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa Maroon Tough 0.025] + +[sla_material:Prusa Maroon Tough @0.025] inherits = *common 0.025* exposure_time = 6 initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa Beige Tough 0.025] +[sla_material:Prusa Beige Tough @0.025] inherits = *common 0.025* exposure_time = 6 initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa Pink Tough 0.025] +[sla_material:Prusa Pink Tough @0.025] inherits = *common 0.025* exposure_time = 7 initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa White Tough 0.025] +[sla_material:Prusa White Tough @0.025] inherits = *common 0.025* exposure_time = 6.5 initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa Transparent Tough 0.025] +[sla_material:Prusa Transparent Tough @0.025] inherits = *common 0.025* exposure_time = 6 initial_exposure_time = 15 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa Green Casting 0.025] +[sla_material:Prusa Green Dental Casting @0.025] inherits = *common 0.025* exposure_time = 12 -initial_exposure_time = 35 +initial_exposure_time = 40 +material_type = Casting +material_vendor = Prusa -## [sla_material:Prusa Transparent Green Tough 0.025] +[sla_material:Prusa Transparent Green Tough @0.025] +inherits = *common 0.025* +exposure_time = 5 +initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa + +[sla_material:Prusa Clear ABS like @0.025] +inherits = *common 0.025* +exposure_time = 6 +initial_exposure_time = 30 +material_type = Tough +material_vendor = Prusa + +## [sla_material:Prusa ABS like White @0.025] ## inherits = *common 0.025* -## exposure_time = 5 -## initial_exposure_time = 35 +## exposure_time = 6 +## initial_exposure_time = 30 + +[sla_material:Prusa Grey High Tenacity @0.025] +inherits = *common 0.025* +exposure_time = 5 +initial_exposure_time = 30 +material_type = Tough +material_vendor = Prusa + +[sla_material:Prusa Super Low Odor Cyan Tough @0.025] +inherits = *common 0.025* +exposure_time = 5 +initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa + +[sla_material:Prusa Super Low Odor Magenta Tough @0.025] +inherits = *common 0.025* +exposure_time = 5 +initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa + +[sla_material:Prusa Super Low Odor Yellow Tough @0.025] +inherits = *common 0.025* +exposure_time = 5 +initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa + +[sla_material:Prusa Orange-Yellow Teeth Model @0.025] +inherits = *common 0.025* +exposure_time = 5 +initial_exposure_time = 30 +material_type = Tough +material_vendor = Prusa + ########### Materials 0.05 -[sla_material:BlueCast EcoGray 0.05] +[sla_material:Asiga Denta Model @0.05] +inherits = *common 0.05* +exposure_time = 15 +initial_exposure_time = 30 +material_type = Dental +material_vendor = Asiga + +[sla_material:Ameralabs AMD 3 LED @0.05] +inherits = *common 0.05* +exposure_time = 5 +initial_exposure_time = 30 +material_type = Tough +material_vendor = Ameralabs + +[sla_material:BlueCast EcoGray @0.05] inherits = *common 0.05* exposure_time = 7 initial_exposure_time = 35 +material_type = Tough +material_vendor = BlueCast -[sla_material:BlueCast Keramaster 0.05] -inherits = *common 0.05* -exposure_time = 8 -initial_exposure_time = 45 - -[sla_material:BlueCast Keramaster Dental 0.05] +[sla_material:BlueCast Kera Master Dental @0.05] inherits = *common 0.05* exposure_time = 7 initial_exposure_time = 50 +material_type = Dental +material_vendor = BlueCast -[sla_material:BlueCast LCD-DLP Original 0.05] +[sla_material:BlueCast LCD-DLP Original @0.05] inherits = *common 0.05* exposure_time = 10 initial_exposure_time = 60 +material_type = Tough +material_vendor = BlueCast -[sla_material:BlueCast Phrozen Wax 0.05] +[sla_material:BlueCast Phrozen Wax @0.05] inherits = *common 0.05* exposure_time = 16 initial_exposure_time = 50 +material_type = Tough +material_vendor = BlueCast -[sla_material:BlueCast S+ 0.05] +[sla_material:BlueCast S+ @0.05] inherits = *common 0.05* exposure_time = 9 initial_exposure_time = 45 +material_type = Tough +material_vendor = BlueCast -[sla_material:BlueCast X10 0.05] +[sla_material:BlueCast X5 @0.05] +inherits = *common 0.05* +exposure_time = 9 +initial_exposure_time = 100 +material_type = Tough +material_vendor = BlueCast + +[sla_material:BlueCast X10 @0.05] inherits = *common 0.05* exposure_time = 6 initial_exposure_time = 100 +material_type = Tough +material_vendor = BlueCast -[sla_material:Monocure 3D Black Rapid Resin 0.05] +[sla_material:BlueCast 23LS @0.05] +inherits = *common 0.05* +exposure_time = 8 +initial_exposure_time = 50 +material_type = Tough +material_vendor = BlueCast + +[sla_material:Monocure 3D Black Rapid Resin @0.05] inherits = *common 0.05* exposure_time = 6 initial_exposure_time = 40 +material_type = Tough +material_vendor = Monocure -[sla_material:Monocure 3D Blue Rapid Resin 0.05] +[sla_material:Monocure 3D Blue Rapid Resin @0.05] inherits = *common 0.05* exposure_time = 7 initial_exposure_time = 40 +material_type = Tough +material_vendor = Monocure -[sla_material:Monocure 3D Clear Rapid Resin 0.05] +[sla_material:Monocure 3D Clear Rapid Resin @0.05] inherits = *common 0.05* exposure_time = 8 initial_exposure_time = 40 +material_type = Tough +material_vendor = Monocure -[sla_material:Monocure 3D Grey Rapid Resin 0.05] +[sla_material:Monocure 3D Grey Rapid Resin @0.05] inherits = *common 0.05* exposure_time = 10 initial_exposure_time = 30 +material_type = Tough +material_vendor = Monocure -[sla_material:Monocure 3D White Rapid Resin 0.05] +[sla_material:Monocure 3D White Rapid Resin @0.05] inherits = *common 0.05* exposure_time = 7 initial_exposure_time = 40 +material_type = Tough +material_vendor = Monocure -[sla_material:3DM-HTR140 (high temperature) 0.05] +[sla_material:3DM-HTR140 (high temperature) @0.05] inherits = *common 0.05* exposure_time = 12 initial_exposure_time = 45 +material_type = Tough +material_vendor = Monocure -[sla_material:3DM-ABS 0.05] +[sla_material:Esun Bio-Photopolymer Resin White @0.05] +inherits = *common 0.05* +exposure_time = 8 +initial_exposure_time = 30 +material_type = Tough +material_vendor = Esun + +[sla_material:Esun Standard Resin Black @0.05] +inherits = *common 0.05* +exposure_time = 7 +initial_exposure_time = 30 +material_type = Tough +material_vendor = Esun + +[sla_material:3DM-ABS @0.05] inherits = *common 0.05* exposure_time = 13 initial_exposure_time = 25 +material_type = Tough +material_vendor = 3DM -[sla_material:3DM-BLACK 0.05] +[sla_material:3DM-BLACK @0.05] inherits = *common 0.05* exposure_time = 20 initial_exposure_time = 40 +material_type = Tough +material_vendor = 3DM -[sla_material:3DM-DENT 0.05] +[sla_material:3DM-DENT @0.05] inherits = *common 0.05* exposure_time = 7 initial_exposure_time = 45 +material_type = Dental +material_vendor = 3DM -[sla_material:3DM-HR Green 0.05] +[sla_material:3DM-HR Green @0.05] inherits = *common 0.05* exposure_time = 15 initial_exposure_time = 40 +material_type = Tough +material_vendor = 3DM -[sla_material:3DM-HR Red Wine 0.05] +[sla_material:3DM-HR Red Wine @0.05] inherits = *common 0.05* exposure_time = 9 initial_exposure_time = 35 +material_type = Tough +material_vendor = 3DM -[sla_material:3DM-XPRO White 0.05] +[sla_material:3DM-XPRO White @0.05] inherits = *common 0.05* exposure_time = 9 initial_exposure_time = 35 +material_type = Tough +material_vendor = 3DM -[sla_material:FTD Ash Grey 0.05] -inherits = *common 0.05* -exposure_time = 9 -initial_exposure_time = 40 - -[sla_material:Harz Labs Model Resin Cherry 0.05] -inherits = *common 0.05* -exposure_time = 8 -initial_exposure_time = 45 - -[sla_material:Photocentric Hard Grey 0.05] +[sla_material:3DM-Vulcan Gold @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 +initial_exposure_time = 40 +material_type = Tough +material_vendor = FTD + +[sla_material:Harz Labs Model Resin Cherry @0.05] +inherits = *common 0.05* +exposure_time = 8 +initial_exposure_time = 45 +material_type = Tough +material_vendor = Harz Labs + +[sla_material:Resinworks 3D Violet @0.05] +inherits = *common 0.05* +exposure_time = 17 +initial_exposure_time = 30 +material_type = Tough +material_vendor = Resinworks 3D + +[sla_material:Resinworks 3D Green @0.05] +inherits = *common 0.05* +exposure_time = 21 +initial_exposure_time = 35 +material_type = Tough +material_vendor = Resinworks 3D + +[sla_material:Photocentric Hard Grey @0.05] +inherits = *common 0.05* +exposure_time = 15 +initial_exposure_time = 30 +material_type = Tough +material_vendor = Photocentric + +[sla_material:Photocentric Ash Grey @0.05] +inherits = *common 0.05* +exposure_time = 10 +initial_exposure_time = 30 +material_type = Tough +material_vendor = Photocentric ## Prusa -[sla_material:Prusa Beige Tough 0.05] +[sla_material:Prusa Beige Tough @0.05] inherits = *common 0.05* exposure_time = 7 initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa Orange Tough 0.05] +[sla_material:Prusa Orange Tough @0.05] inherits = *common 0.05* exposure_time = 7.5 initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa Grey Tough 0.05] +[sla_material:Prusa Grey Tough @0.05] inherits = *common 0.05* exposure_time = 8.5 initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa Black Tough 0.05] +[sla_material:Prusa Black Tough @0.05] inherits = *common 0.05* exposure_time = 6 initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa -## [sla_material:Prusa Beige Super Low Odor 0.05] +## [sla_material:Prusa Super Low Odor Beige Tough @0.05] ## inherits = *common 0.05* ## exposure_time = 7.5 ## initial_exposure_time = 35 +## material_type = Tough +## material_vendor = Prusa -## [sla_material:Prusa White Super Low Odor 0.05] +## [sla_material:Prusa Super Low Odor White Tough @0.05] ## inherits = *common 0.05* ## exposure_time = 6.5 ## initial_exposure_time = 35 +## material_type = Tough +## material_vendor = Prusa -## [sla_material:Prusa Grey Super Low Odor 0.05] +## [sla_material:Prusa Super Low Odor Grey Tough @0.05] ## inherits = *common 0.05* ## exposure_time = 6.5 ## initial_exposure_time = 35 +## material_type = Tough +## material_vendor = Prusa -## [sla_material:Prusa Black High Tenacity 0.05] -## inherits = *common 0.05* -## exposure_time = 7 -## initial_exposure_time = 35 - -[sla_material:Prusa Green Casting 0.05] -inherits = *common 0.05* -exposure_time = 13 -initial_exposure_time = 40 - -## [sla_material:Prusa Yellow Solid 0.05] -## inherits = *common 0.05* -## exposure_time = 7 -## initial_exposure_time = 35 - -[sla_material:Prusa White Tough 0.05] -inherits = *common 0.05* -exposure_time = 7.5 -initial_exposure_time = 35 - -## [sla_material:Prusa Transparent Green Tough 0.05] -## inherits = *common 0.05* -## exposure_time = 6 -## initial_exposure_time = 35 - -[sla_material:Prusa Transparent Red Tough 0.05] +[sla_material:Prusa Super Low Odor Cyan Tough @0.05] inherits = *common 0.05* exposure_time = 6 initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa Maroon Tough 0.05] +[sla_material:Prusa Super Low Odor Magenta Tough @0.05] +inherits = *common 0.05* +exposure_time = 6 +initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa + +[sla_material:Prusa Super Low Odor Yellow Tough @0.05] +inherits = *common 0.05* +exposure_time = 6 +initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa + +## [sla_material:Prusa Black High Tenacity @0.05] +## inherits = *common 0.05* +## exposure_time = 7 +## initial_exposure_time = 35 +## material_type = Tough +## material_vendor = Prusa + +[sla_material:Prusa Orange-Yellow Teeth Model @0.05] +inherits = *common 0.05* +exposure_time = 7 +initial_exposure_time = 30 +material_type = Tough +material_vendor = Prusa + +[sla_material:Prusa Green Dental Casting @0.05] +inherits = *common 0.05* +exposure_time = 13 +initial_exposure_time = 50 +material_type = Casting +material_vendor = Prusa + +## [sla_material:Prusa Yellow Solid @0.05] +## inherits = *common 0.05* +## exposure_time = 7 +## initial_exposure_time = 35 + +[sla_material:Prusa White Tough @0.05] inherits = *common 0.05* exposure_time = 7.5 initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa Pink Tough 0.05] +[sla_material:Prusa Transparent Green Tough @0.05] +inherits = *common 0.05* +exposure_time = 6 +initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa + +[sla_material:Prusa Transparent Red Tough @0.05] +inherits = *common 0.05* +exposure_time = 6 +initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa + +[sla_material:Prusa Maroon Tough @0.05] +inherits = *common 0.05* +exposure_time = 7.5 +initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa + +[sla_material:Prusa Pink Tough @0.05] inherits = *common 0.05* exposure_time = 8 initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa Azure Blue Tough 0.05] +[sla_material:Prusa Azure Blue Tough @0.05] inherits = *common 0.05* exposure_time = 8 initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa Transparent Tough 0.05] +[sla_material:Prusa Transparent Tough @0.05] inherits = *common 0.05* exposure_time = 7 initial_exposure_time = 15 +material_type = Tough +material_vendor = Prusa -## [sla_material:Prusa Yellow Flexible 0.05] +## [sla_material:Prusa Yellow Flexible @0.05] ## inherits = *common 0.05* ## exposure_time = 9 ## initial_exposure_time = 35 -## [sla_material:Prusa Clear Flexible 0.05] -## inherits = *common 0.05* -## exposure_time = 5 -## initial_exposure_time = 15 +[sla_material:Prusa Transparent Flexible @0.05] +inherits = *common 0.05* +exposure_time = 5 +initial_exposure_time = 15 +material_type = Flexible +material_vendor = Prusa -## [sla_material:Prusa White Flexible 0.05] +## [sla_material:Prusa White Flexible @0.05] ## inherits = *common 0.05* ## exposure_time = 9 ## initial_exposure_time = 35 -## [sla_material:Prusa Blue Flexible 0.05] +[sla_material:Prusa Blue Flexible @0.05] +inherits = *common 0.05* +exposure_time = 5 +initial_exposure_time = 15 +material_type = Flexible +material_vendor = Prusa + +## [sla_material:Prusa Black Flexible @0.05] ## inherits = *common 0.05* ## exposure_time = 9 ## initial_exposure_time = 35 -## [sla_material:Prusa Black Flexible 0.05] +## [sla_material:Prusa Red Flexible @0.05] ## inherits = *common 0.05* ## exposure_time = 9 ## initial_exposure_time = 35 -## [sla_material:Prusa Red Flexible 0.05] +[sla_material:Prusa Clear ABS like @0.05] +inherits = *common 0.05* +exposure_time = 8 +initial_exposure_time = 30 +material_type = Tough +material_vendor = Prusa + +## [sla_material:Prusa ABS like White @0.05] ## inherits = *common 0.05* -## exposure_time = 9 -## initial_exposure_time = 35 +## exposure_time = 8 +## initial_exposure_time = 30 + +[sla_material:Prusa Yellow Jewelry Casting @0.05] +inherits = *common 0.05* +exposure_time = 13 +initial_exposure_time = 45 +material_type = Casting +material_vendor = Prusa + +[sla_material:Prusa Grey High Tenacity @0.05] +inherits = *common 0.05* +exposure_time = 7 +initial_exposure_time = 30 +material_type = Tough +material_vendor = Prusa ########### Materials 0.035 -[sla_material:Prusa Orange Tough 0.035] +[sla_material:Prusa Orange Tough @0.035] inherits = *common 0.035* exposure_time = 6 initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa ########### Materials 0.1 -[sla_material:Prusa Orange Tough 0.1] +[sla_material:BlueCast EcoGray @0.1] +inherits = *common 0.1* +exposure_time = 10 +initial_exposure_time = 35 +material_type = Tough +material_vendor = BlueCast + +[sla_material:BlueCast Kera Master Dental @0.1] +inherits = *common 0.1* +exposure_time = 13 +initial_exposure_time = 50 +material_type = Tough +material_vendor = BlueCast + +## Prusa + +[sla_material:Prusa Orange Tough @0.1] inherits = *common 0.1* exposure_time = 13 initial_exposure_time = 45 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa Beige Tough 0.1] +[sla_material:Prusa Beige Tough @0.1] inherits = *common 0.1* exposure_time = 13 initial_exposure_time = 45 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa Pink Tough 0.1] +[sla_material:Prusa Pink Tough @0.1] inherits = *common 0.1* exposure_time = 13 initial_exposure_time = 45 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa Azure Blue Tough 0.1] +[sla_material:Prusa Azure Blue Tough @0.1] inherits = *common 0.1* exposure_time = 13 initial_exposure_time = 45 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa Maroon Tough 0.1] +[sla_material:Prusa Maroon Tough @0.1] inherits = *common 0.1* exposure_time = 13 initial_exposure_time = 45 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa White Tough 0.1] +[sla_material:Prusa White Tough @0.1] inherits = *common 0.1* exposure_time = 13 initial_exposure_time = 45 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa Black Tough 0.1] +[sla_material:Prusa Black Tough @0.1] inherits = *common 0.1* exposure_time = 13 initial_exposure_time = 55 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa Transparent Tough 0.1] +[sla_material:Prusa Transparent Tough @0.1] inherits = *common 0.1* exposure_time = 8 initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa -[sla_material:Prusa Green Casting 0.1] +[sla_material:Prusa Green Dental Casting @0.1] inherits = *common 0.1* exposure_time = 15 initial_exposure_time = 50 +material_type = Casting +material_vendor = Prusa + +[sla_material:Prusa Transparent Green Tough @0.1] +inherits = *common 0.1* +exposure_time = 7 +initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa [printer:*common*] printer_technology = FFF @@ -2341,7 +3600,7 @@ printer_model = MK2SMM inherits = *multimaterial* end_gcode = G1 E-4 F2100.00000\nG91\nG1 Z1 F7200.000\nG90\nG1 X245 Y1\nG1 X240 E4\nG1 F4000\nG1 X190 E2.7 \nG1 F4600\nG1 X110 E2.8\nG1 F5200\nG1 X40 E3 \nG1 E-15.0000 F5000\nG1 E-50.0000 F5400\nG1 E-15.0000 F3000\nG1 E-12.0000 F2000\nG1 F1600\nG1 X0 Y1 E3.0000\nG1 X50 Y1 E-5.0000\nG1 F2000\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-3.0000\nG4 S0\nM107 ; turn off fan\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nG28 X0 ; home X axis\nM84 ; disable motors\n\n printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2\nPRINTER_HAS_BOWDEN -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.2.3 ; tell printer latest fw version\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting] ; MK2 firmware only supports the old M204 format\n; Start G-Code sequence START\nT?\nM104 S[first_layer_temperature]\nM140 S[first_layer_bed_temperature]\nM109 S[first_layer_temperature]\nM190 S[first_layer_bed_temperature]\nG21 ; set units to millimeters\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0.0\nM203 E100\nM92 E140\nG1 Z0.250 F7200.000\nG1 X50.0 E80.0 F1000.0\nG1 X160.0 E20.0 F1000.0\nG1 Z0.200 F7200.000\nG1 X220.0 E13 F1000.0\nG1 X240.0 E0 F1000.0\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.2.3 ; tell printer latest fw version\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting] ; MK2 firmware only supports the old M204 format\n; Start G-Code sequence START\nT?\nM104 S[first_layer_temperature]\nM140 S[first_layer_bed_temperature]\nM109 S[first_layer_temperature]\nM190 S[first_layer_bed_temperature]\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0.0\nM203 E100\nM92 E140\nG1 Z0.250 F7200.000\nG1 X50.0 E80.0 F1000.0\nG1 X160.0 E20.0 F1000.0\nG1 Z0.200 F7200.000\nG1 X220.0 E13 F1000.0\nG1 X240.0 E0 F1000.0\nG92 E0.0 default_print_profile = 0.15mm OPTIMAL [printer:*mm-multi*] @@ -2351,7 +3610,7 @@ end_gcode = {if not has_wipe_tower}\n; Pull the filament into the cooling tubes. extruder_colour = #FFAA55;#E37BA0;#4ECDD3;#FB7259 nozzle_diameter = 0.4,0.4,0.4,0.4 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_MK2\nPRINTER_HAS_BOWDEN -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.2.3 ; tell printer latest fw version\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting] ; MK2 firmware only supports the old M204 format\n; Start G-Code sequence START\nT[initial_tool]\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\nG21 ; set units to millimeters\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0.0\nM203 E100 ; set max feedrate\nM92 E140 ; E-steps per filament milimeter\n{if not has_single_extruder_multi_material_priming}\nG1 Z0.250 F7200.000\nG1 X50.0 E80.0 F1000.0\nG1 X160.0 E20.0 F1000.0\nG1 Z0.200 F7200.000\nG1 X220.0 E13 F1000.0\nG1 X240.0 E0 F1000.0\n{endif}\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.2.3 ; tell printer latest fw version\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting] ; MK2 firmware only supports the old M204 format\n; Start G-Code sequence START\nT[initial_tool]\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\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0.0\nM203 E100 ; set max feedrate\nM92 E140 ; E-steps per filament milimeter\n{if not has_single_extruder_multi_material_priming}\nG1 Z0.250 F7200.000\nG1 X50.0 E80.0 F1000.0\nG1 X160.0 E20.0 F1000.0\nG1 Z0.200 F7200.000\nG1 X220.0 E13 F1000.0\nG1 X240.0 E0 F1000.0\n{endif}\nG92 E0.0 default_print_profile = 0.15mm OPTIMAL # XXXXXXXXXXXXXXXXX @@ -2370,6 +3629,7 @@ retract_length = 1 retract_speed = 50 variable_layer_height = 1 printer_variant = 0.25 +retract_lift = 0.15 default_print_profile = 0.10mm DETAIL 0.25 nozzle [printer:Original Prusa i3 MK2S 0.6 nozzle] @@ -2378,7 +3638,7 @@ max_layer_height = 0.35 min_layer_height = 0.1 nozzle_diameter = 0.6 printer_variant = 0.6 -default_print_profile = 0.20mm NORMAL 0.6 nozzle +default_print_profile = 0.20mm NORMAL @0.6 nozzle # XXXXXXXXXXXXXXXXXXX # XXX--- MK2MM ---XXX @@ -2393,7 +3653,7 @@ min_layer_height = 0.07 inherits = *mm-single* nozzle_diameter = 0.6 printer_variant = 0.6 -default_print_profile = 0.20mm NORMAL 0.6 nozzle +default_print_profile = 0.20mm NORMAL @0.6 nozzle max_layer_height = 0.35 min_layer_height = 0.1 @@ -2407,7 +3667,7 @@ min_layer_height = 0.07 inherits = *mm-multi* nozzle_diameter = 0.6,0.6,0.6,0.6 printer_variant = 0.6 -default_print_profile = 0.20mm NORMAL 0.6 nozzle +default_print_profile = 0.20mm NORMAL @0.6 nozzle max_layer_height = 0.35 min_layer_height = 0.1 @@ -2419,19 +3679,19 @@ min_layer_height = 0.1 inherits = Original Prusa i3 MK2S printer_model = MK2.5 remaining_times = 1 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.1 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 [printer:Original Prusa i3 MK2.5 0.25 nozzle] inherits = Original Prusa i3 MK2S 0.25 nozzle printer_model = MK2.5 remaining_times = 1 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.1 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 [printer:Original Prusa i3 MK2.5 0.6 nozzle] inherits = Original Prusa i3 MK2S 0.6 nozzle printer_model = MK2.5 remaining_times = 1 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.1 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 [printer:Original Prusa i3 MK2.5 MMU2 Single] inherits = Original Prusa i3 MK2.5; *mm2* @@ -2457,10 +3717,10 @@ machine_max_jerk_y = 10 machine_max_jerk_z = 0.2 machine_min_extruding_rate = 0 machine_min_travel_rate = 0 -default_print_profile = 0.15mm OPTIMAL MK2.5 +default_print_profile = 0.15mm OPTIMAL @MK2.5 default_filament_profile = Prusament PLA 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_MK2.5\n -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; 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\n; select extruder\nTx\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\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; load to nozzle\nTc\n; purge line\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n +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\n; select extruder\nTx\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\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; load to nozzle\nTc\n; purge line\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n end_gcode = G1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n; Lift print head a bit\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200 F3000 ; home X axis\nM84 ; disable motors [printer:Original Prusa i3 MK2.5 MMU2 Single 0.6 nozzle] @@ -2494,7 +3754,7 @@ machine_max_jerk_y = 10 machine_max_jerk_z = 0.2 machine_min_extruding_rate = 0 machine_min_travel_rate = 0 -default_print_profile = 0.15mm OPTIMAL MK2.5 +default_print_profile = 0.15mm OPTIMAL @MK2.5 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_MK2.5\n single_extruder_multi_material = 1 # The 5x nozzle diameter defines the number of extruders. Other extruder parameters @@ -2502,23 +3762,23 @@ single_extruder_multi_material = 1 # to be defined explicitely. nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#DB5182;#00FFFF;#FF4F4F;#9FFF9F -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; 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\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG92 E0.0\n +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\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG92 E0.0\n end_gcode = {if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n; Lift print head a bit\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200 F3000 ; home X axis\nM84 ; disable motors\n [printer:Original Prusa i3 MK2.5S] inherits = Original Prusa i3 MK2.5 printer_model = MK2.5S -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.1 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 [printer:Original Prusa i3 MK2.5S 0.25 nozzle] inherits = Original Prusa i3 MK2.5 0.25 nozzle printer_model = MK2.5S -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.1 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 [printer:Original Prusa i3 MK2.5S 0.6 nozzle] inherits = Original Prusa i3 MK2.5 0.6 nozzle printer_model = MK2.5S -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.1 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 [printer:Original Prusa i3 MK2.5S MMU2S Single] inherits = Original Prusa i3 MK2.5; *mm2s* @@ -2544,10 +3804,10 @@ machine_max_jerk_y = 10 machine_max_jerk_z = 0.2 machine_min_extruding_rate = 0 machine_min_travel_rate = 0 -default_print_profile = 0.15mm OPTIMAL MK2.5 +default_print_profile = 0.15mm OPTIMAL @MK2.5 default_filament_profile = Prusament PLA 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_MK2.5\n -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; 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\nTx\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\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n +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\nTx\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\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n end_gcode = G1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n; Lift print head a bit\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200 F3000 ; home X axis\nM84 ; disable motors [printer:Original Prusa i3 MK2.5S MMU2S Single 0.6 nozzle] @@ -2557,7 +3817,7 @@ max_layer_height = 0.35 min_layer_height = 0.1 nozzle_diameter = 0.6 printer_variant = 0.6 -default_print_profile = 0.20mm NORMAL 0.6 nozzle +default_print_profile = 0.20mm NORMAL @0.6 nozzle [printer:Original Prusa i3 MK2.5S MMU2S Single 0.25 nozzle] inherits = Original Prusa i3 MK2.5S MMU2S Single @@ -2566,8 +3826,9 @@ max_layer_height = 0.15 min_layer_height = 0.05 nozzle_diameter = 0.25 printer_variant = 0.25 +retract_lift = 0.15 default_print_profile = 0.10mm DETAIL 0.25 nozzle -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; 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\nTx\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\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F1400.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n +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\nTx\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\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F1400.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n [printer:Original Prusa i3 MK2.5S MMU2S] inherits = Original Prusa i3 MK2.5; *mm2s* @@ -2592,7 +3853,7 @@ machine_max_jerk_y = 10 machine_max_jerk_z = 0.2 machine_min_extruding_rate = 0 machine_min_travel_rate = 0 -default_print_profile = 0.15mm OPTIMAL MK2.5 +default_print_profile = 0.15mm OPTIMAL @MK2.5 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_MK2.5\n single_extruder_multi_material = 1 # The 5x nozzle diameter defines the number of extruders. Other extruder parameters @@ -2600,7 +3861,7 @@ single_extruder_multi_material = 1 # to be defined explicitely. nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#DB5182;#00FFFF;#FF4F4F;#9FFF9F -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; 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\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG92 E0.0\n +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\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG92 E0.0\n end_gcode = {if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n; Lift print head a bit\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200 F3000 ; home X axis\nM84 ; disable motors\n [printer:Original Prusa i3 MK2.5S MMU2S 0.6 nozzle] @@ -2609,7 +3870,7 @@ nozzle_diameter = 0.6,0.6,0.6,0.6,0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -default_print_profile = 0.20mm NORMAL 0.6 nozzle +default_print_profile = 0.20mm NORMAL @0.6 nozzle [printer:Original Prusa i3 MK2.5 MMU2 0.6 nozzle] inherits = Original Prusa i3 MK2.5 MMU2 @@ -2617,7 +3878,7 @@ nozzle_diameter = 0.6,0.6,0.6,0.6,0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -default_print_profile = 0.20mm NORMAL 0.6 nozzle +default_print_profile = 0.20mm NORMAL @0.6 nozzle # XXXXXXXXXXXXXXXXX # XXX--- MK3 ---XXX @@ -2647,9 +3908,9 @@ remaining_times = 1 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_MK3\n retract_lift_below = 209 max_print_height = 210 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; 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\nM221 S{if layer_height<0.075}100{else}95{endif} +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\nM221 S{if layer_height<0.075}100{else}95{endif} printer_model = MK3 -default_print_profile = 0.15mm QUALITY MK3 +default_print_profile = 0.15mm QUALITY @MK3 [printer:Original Prusa i3 MK3 0.25 nozzle] inherits = Original Prusa i3 MK3 @@ -2657,8 +3918,9 @@ nozzle_diameter = 0.25 max_layer_height = 0.15 min_layer_height = 0.05 printer_variant = 0.25 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; 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 E8.0 F700.0 ; intro line\nG1 X100.0 E12.5 F700.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif} -default_print_profile = 0.10mm DETAIL 0.25 nozzle MK3 +retract_lift = 0.15 +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 E8.0 F700.0 ; intro line\nG1 X100.0 E12.5 F700.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif} +default_print_profile = 0.10mm DETAIL @0.25 nozzle MK3 [printer:Original Prusa i3 MK3 0.6 nozzle] inherits = Original Prusa i3 MK3 @@ -2666,22 +3928,22 @@ nozzle_diameter = 0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -default_print_profile = 0.30mm QUALITY 0.6 nozzle MK3 +default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 [printer:Original Prusa i3 MK3S] inherits = Original Prusa i3 MK3 printer_model = MK3S -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; 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\nM221 S{if layer_height<0.075}100{else}95{endif} +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\nM221 S{if layer_height<0.075}100{else}95{endif} [printer:Original Prusa i3 MK3S 0.25 nozzle] inherits = Original Prusa i3 MK3 0.25 nozzle printer_model = MK3S -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; 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 E8.0 F700.0 ; intro line\nG1 X100.0 E12.5 F700.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif} +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 E8.0 F700.0 ; intro line\nG1 X100.0 E12.5 F700.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif} [printer:Original Prusa i3 MK3S 0.6 nozzle] inherits = Original Prusa i3 MK3 0.6 nozzle printer_model = MK3S -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; 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\nM221 S{if layer_height<0.075}100{else}95{endif} +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\nM221 S{if layer_height<0.075}100{else}95{endif} [printer:*mm2*] inherits = Original Prusa i3 MK3 @@ -2692,8 +3954,8 @@ parking_pos_retraction = 85 retract_length_toolchange = 3 extra_loading_move = -13 printer_model = MK3MMU2 -default_print_profile = 0.15mm QUALITY MK3 -default_filament_profile = Prusament PLA MMU2 +default_print_profile = 0.15mm QUALITY @MK3 +default_filament_profile = Prusament PLA @MMU2 [printer:*mm2s*] inherits = Original Prusa i3 MK3 @@ -2704,14 +3966,14 @@ parking_pos_retraction = 85 retract_length_toolchange = 3 extra_loading_move = -25 printer_model = MK3SMMU2S -default_print_profile = 0.15mm QUALITY MK3 -default_filament_profile = Prusament PLA MMU2 +default_print_profile = 0.15mm QUALITY @MK3 +default_filament_profile = Prusament PLA @MMU2 [printer:Original Prusa i3 MK3 MMU2 Single] inherits = *mm2* single_extruder_multi_material = 0 default_filament_profile = Prusament PLA -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; 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\nTx\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\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n +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\nTx\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\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n end_gcode = G1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n; Lift print head a bit\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200 F3000 ; home X axis\nM84 ; disable motors [printer:Original Prusa i3 MK3 MMU2 Single 0.6 nozzle] @@ -2721,7 +3983,7 @@ nozzle_diameter = 0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -default_print_profile = 0.30mm QUALITY 0.6 nozzle MK3 +default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 [printer:Original Prusa i3 MK3 MMU2 Single 0.25 nozzle] inherits = Original Prusa i3 MK3 MMU2 Single @@ -2730,8 +3992,9 @@ nozzle_diameter = 0.25 max_layer_height = 0.15 min_layer_height = 0.05 printer_variant = 0.25 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; 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\nTx\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\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 E8.0 F1000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F1400.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n -default_print_profile = 0.10mm DETAIL 0.25 nozzle MK3 +retract_lift = 0.15 +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\nTx\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\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 E8.0 F1000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F1400.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n +default_print_profile = 0.10mm DETAIL @0.25 nozzle MK3 [printer:Original Prusa i3 MK3 MMU2] inherits = *mm2* @@ -2741,14 +4004,14 @@ inherits = *mm2* machine_max_acceleration_e = 8000,8000 nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#DB5182;#00FFFF;#FF4F4F;#9FFF9F -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; 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\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n +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\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n end_gcode = {if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n; Lift print head a bit\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200 F3000 ; home X axis\nM84 ; disable motors\n [printer:Original Prusa i3 MK3S MMU2S Single] inherits = *mm2s* single_extruder_multi_material = 0 default_filament_profile = Prusament PLA -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; 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\nTx\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\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n +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\nTx\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\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n end_gcode = G1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n; Lift print head a bit\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200 F3000 ; home X axis\nM84 ; disable motors [printer:Original Prusa i3 MK3S MMU2S Single 0.6 nozzle] @@ -2758,7 +4021,7 @@ nozzle_diameter = 0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -default_print_profile = 0.30mm QUALITY 0.6 nozzle MK3 +default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 [printer:Original Prusa i3 MK3S MMU2S Single 0.25 nozzle] inherits = Original Prusa i3 MK3S MMU2S Single @@ -2767,15 +4030,16 @@ nozzle_diameter = 0.25 max_layer_height = 0.15 min_layer_height = 0.05 printer_variant = 0.25 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; 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\nTx\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\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F1400.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n -default_print_profile = 0.10mm DETAIL 0.25 nozzle MK3 +retract_lift = 0.15 +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\nTx\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\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F1400.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n +default_print_profile = 0.10mm DETAIL @0.25 nozzle MK3 [printer:Original Prusa i3 MK3S MMU2S] inherits = *mm2s* machine_max_acceleration_e = 8000,8000 nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#DB5182;#00FFFF;#FF4F4F;#9FFF9F -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.0 ; 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\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n +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\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n end_gcode = {if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n; Lift print head a bit\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200 F3000 ; home X axis\nM84 ; disable motors\n ## 0.6mm nozzle MMU2/S printer profiles @@ -2786,7 +4050,7 @@ nozzle_diameter = 0.6,0.6,0.6,0.6,0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -default_print_profile = 0.30mm QUALITY 0.6 nozzle MK3 +default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 [printer:Original Prusa i3 MK3 MMU2 0.6 nozzle] inherits = Original Prusa i3 MK3 MMU2 @@ -2794,7 +4058,88 @@ nozzle_diameter = 0.6,0.6,0.6,0.6,0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -default_print_profile = 0.30mm QUALITY 0.6 nozzle MK3 +default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 + +## MINI + +[printer:Original Prusa MINI] +inherits = *common* +printer_model = MINI +printer_technology = FFF +printer_variant = 0.4 +printer_vendor = +thumbnails = 16x16,220x124 +bed_shape = 0x0,180x0,180x180,0x180 +default_filament_profile = "Prusament PLA" +default_print_profile = 0.15mm QUALITY @MINI +gcode_flavor = marlin +machine_max_acceleration_e = 5000 +machine_max_acceleration_extruding = 1250 +machine_max_acceleration_retracting = 1250 +machine_max_acceleration_x = 1250 +machine_max_acceleration_y = 1250 +machine_max_acceleration_z = 400 +machine_max_feedrate_e = 80 +machine_max_feedrate_x = 180 +machine_max_feedrate_y = 180 +machine_max_feedrate_z = 12 +machine_max_jerk_e = 10 +machine_max_jerk_x = 8 +machine_max_jerk_y = 8 +machine_max_jerk_z = 2 +machine_min_extruding_rate = 0 +machine_min_travel_rate = 0 +max_layer_height = 0.25 +max_print_height = 180 +min_layer_height = 0.07 +nozzle_diameter = 0.4 +retract_length = 3.2 +retract_lift = 0.2 +retract_speed = 70 +deretract_speed = 40 +wipe = 1 +retract_before_wipe = 70% +retract_before_travel = 1.5 +retract_lift_above = 0 +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 +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 = + +[printer:Original Prusa MINI 0.25 nozzle] +inherits = Original Prusa MINI +printer_variant = 0.25 +nozzle_diameter = 0.25 +max_layer_height = 0.15 +min_layer_height = 0.05 +default_print_profile = 0.10mm DETAIL @0.25 nozzle MINI +retract_length = 3 +retract_lift = 0.15 +retract_speed = 70 +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 + +[printer:Original Prusa MINI 0.6 nozzle] +inherits = Original Prusa MINI +printer_variant = 0.6 +nozzle_diameter = 0.6 +max_layer_height = 0.40 +min_layer_height = 0.15 +default_print_profile = 0.30mm QUALITY @0.6 nozzle MINI +retract_length = 3.5 +retract_lift = 0.2 +retract_speed = 70 +deretract_speed = 40 +wipe = 1 +retract_before_wipe = 70% +retract_before_travel = 1 [printer:Original Prusa SL1] printer_technology = SLA @@ -2802,6 +4147,7 @@ printer_model = SL1 printer_variant = default default_sla_material_profile = Prusa Orange Tough 0.05 default_sla_print_profile = 0.05 Normal +thumbnails = 400x400,800x480 bed_shape = 1.48x1.02,119.48x1.02,119.48x67.02,1.48x67.02 display_height = 68.04 display_orientation = portrait diff --git a/resources/udev/90-3dconnexion.rules b/resources/udev/90-3dconnexion.rules new file mode 100644 index 000000000..04d581498 --- /dev/null +++ b/resources/udev/90-3dconnexion.rules @@ -0,0 +1,45 @@ +# See src/slic3r/GUI/Mouse3DController.cpp for the list of devices + +# Logitech vendor devices +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c603", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c605", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c606", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c621", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c623", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c625", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c626", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c627", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c628", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c629", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62b", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62e", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62f", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c631", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c632", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c633", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c635", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c636", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c640", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c652", MODE="0666" + +# 3D Connexion vendor devices +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c603", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c605", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c606", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c621", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c623", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c625", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c626", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c627", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c628", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c629", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62b", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62e", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62f", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c631", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c632", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c633", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c635", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c636", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c640", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c652", MODE="0666" diff --git a/sandboxes/CMakeLists.txt b/sandboxes/CMakeLists.txt index 5905c438e..3372698c3 100644 --- a/sandboxes/CMakeLists.txt +++ b/sandboxes/CMakeLists.txt @@ -1,2 +1,2 @@ -add_subdirectory(slabasebed) add_subdirectory(slasupporttree) +add_subdirectory(openvdb) diff --git a/sandboxes/openvdb/CMakeLists.txt b/sandboxes/openvdb/CMakeLists.txt new file mode 100644 index 000000000..184452e83 --- /dev/null +++ b/sandboxes/openvdb/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(openvdb_example openvdb_example.cpp) +target_link_libraries(openvdb_example libslic3r) diff --git a/sandboxes/openvdb/openvdb_example.cpp b/sandboxes/openvdb/openvdb_example.cpp new file mode 100644 index 000000000..0df60d8aa --- /dev/null +++ b/sandboxes/openvdb/openvdb_example.cpp @@ -0,0 +1,37 @@ +#include +#include + +int main() +{ + // Initialize the OpenVDB library. This must be called at least + // once per program and may safely be called multiple times. + openvdb::initialize(); + // Create an empty floating-point grid with background value 0. + openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(); + std::cout << "Testing random access:" << std::endl; + // Get an accessor for coordinate-based access to voxels. + openvdb::FloatGrid::Accessor accessor = grid->getAccessor(); + // Define a coordinate with large signed indices. + openvdb::Coord xyz(1000, -200000000, 30000000); + // Set the voxel value at (1000, -200000000, 30000000) to 1. + accessor.setValue(xyz, 1.0); + // Verify that the voxel value at (1000, -200000000, 30000000) is 1. + std::cout << "Grid" << xyz << " = " << accessor.getValue(xyz) << std::endl; + // Reset the coordinates to those of a different voxel. + xyz.reset(1000, 200000000, -30000000); + // Verify that the voxel value at (1000, 200000000, -30000000) is + // the background value, 0. + std::cout << "Grid" << xyz << " = " << accessor.getValue(xyz) << std::endl; + // Set the voxel value at (1000, 200000000, -30000000) to 2. + accessor.setValue(xyz, 2.0); + // Set the voxels at the two extremes of the available coordinate space. + // For 32-bit signed coordinates these are (-2147483648, -2147483648, -2147483648) + // and (2147483647, 2147483647, 2147483647). + accessor.setValue(openvdb::Coord::min(), 3.0f); + accessor.setValue(openvdb::Coord::max(), 4.0f); + std::cout << "Testing sequential access:" << std::endl; + // Print all active ("on") voxels by means of an iterator. + for (openvdb::FloatGrid::ValueOnCIter iter = grid->cbeginValueOn(); iter; ++iter) { + std::cout << "Grid" << iter.getCoord() << " = " << *iter << std::endl; + } +} diff --git a/sandboxes/slabasebed/CMakeLists.txt b/sandboxes/slabasebed/CMakeLists.txt deleted file mode 100644 index 9d731a133..000000000 --- a/sandboxes/slabasebed/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -add_executable(slabasebed EXCLUDE_FROM_ALL slabasebed.cpp) -target_link_libraries(slabasebed libslic3r ${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) diff --git a/sandboxes/slabasebed/slabasebed.cpp b/sandboxes/slabasebed/slabasebed.cpp deleted file mode 100644 index b8b94d86f..000000000 --- a/sandboxes/slabasebed/slabasebed.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -const std::string USAGE_STR = { - "Usage: slabasebed stlfilename.stl" -}; - -namespace Slic3r { namespace sla { - -Contour3D create_base_pool(const Polygons &ground_layer, - const ExPolygons &holes = {}, - const PoolConfig& cfg = PoolConfig()); - -Contour3D walls(const Polygon& floor_plate, const Polygon& ceiling, - double floor_z_mm, double ceiling_z_mm, - double offset_difference_mm, ThrowOnCancel thr); - -void offset(ExPolygon& sh, coord_t distance); - -} -} - -int main(const int argc, const char *argv[]) { - using namespace Slic3r; - using std::cout; using std::endl; - - if(argc < 2) { - cout << USAGE_STR << endl; - return EXIT_SUCCESS; - } - - TriangleMesh model; - Benchmark bench; - - model.ReadSTLFile(argv[1]); - model.align_to_origin(); - - ExPolygons ground_slice; - sla::base_plate(model, ground_slice, 0.1f); - if(ground_slice.empty()) return EXIT_FAILURE; - - ground_slice = offset_ex(ground_slice, 0.5); - ExPolygon gndfirst; gndfirst = ground_slice.front(); - sla::breakstick_holes(gndfirst, 0.5, 10, 0.3); - - sla::Contour3D mesh; - - bench.start(); - - sla::PoolConfig cfg; - cfg.min_wall_height_mm = 0; - cfg.edge_radius_mm = 0; - mesh = sla::create_base_pool(to_polygons(ground_slice), {}, cfg); - - bench.stop(); - - cout << "Base pool creation time: " << std::setprecision(10) - << bench.getElapsedSec() << " seconds." << endl; - - for(auto& trind : mesh.indices) { - Vec3d p0 = mesh.points[size_t(trind[0])]; - Vec3d p1 = mesh.points[size_t(trind[1])]; - Vec3d p2 = mesh.points[size_t(trind[2])]; - Vec3d p01 = p1 - p0; - Vec3d p02 = p2 - p0; - auto a = p01.cross(p02).norm() / 2.0; - if(std::abs(a) < 1e-6) std::cout << "degenerate triangle" << std::endl; - } - - // basepool.write_ascii("out.stl"); - - std::fstream outstream("out.obj", std::fstream::out); - mesh.to_obj(outstream); - - return EXIT_SUCCESS; -} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1582654f0..bc6f93694 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -17,13 +17,14 @@ add_subdirectory(libigl) add_subdirectory(test) # Adding libnest2d project for bin packing... -set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d") add_subdirectory(libnest2d) add_subdirectory(libslic3r) if (SLIC3R_GUI) add_subdirectory(imgui) + add_subdirectory(hidapi) + include_directories(hidapi/include) if(WIN32) message(STATUS "WXWIN environment set to: $ENV{WXWIN}") diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 49d6c741c..da9b7a46e 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -162,6 +162,7 @@ int CLI::run(int argc, char **argv) // sla_print_config.apply(m_print_config, true); // Loop through transform options. + bool user_center_specified = false; for (auto const &opt_key : m_transforms) { if (opt_key == "merge") { Model m; @@ -204,6 +205,7 @@ int CLI::run(int argc, char **argv) for (auto &model : m_models) model.duplicate_objects_grid(x, y, (distance > 0) ? distance : 6); // TODO: this is not the right place for setting a default } else if (opt_key == "center") { + user_center_specified = true; for (auto &model : m_models) { model.add_default_instances(); // this affects instances: @@ -398,7 +400,9 @@ int CLI::run(int argc, char **argv) if (! m_config.opt_bool("dont_arrange")) { //FIXME make the min_object_distance configurable. model.arrange_objects(fff_print.config().min_object_distance()); - model.center_instances_around_point(m_config.option("center")->value); + model.center_instances_around_point((! user_center_specified && m_print_config.has("bed_shape")) ? + BoundingBoxf(m_print_config.opt("bed_shape")->values).center() : + m_config.option("center")->value); } if (printer_technology == ptFFF) { for (auto* mo : model.objects) @@ -415,37 +419,37 @@ int CLI::run(int argc, char **argv) else try { std::string outfile_final; - print->process(); + print->process(); if (printer_technology == ptFFF) { // The outfile is processed by a PlaceholderParser. outfile = fff_print.export_gcode(outfile, nullptr); outfile_final = fff_print.print_statistics().finalize_output_path(outfile); } else { - outfile = sla_print.output_filepath(outfile); + outfile = sla_print.output_filepath(outfile); // We need to finalize the filename beforehand because the export function sets the filename inside the zip metadata outfile_final = sla_print.print_statistics().finalize_output_path(outfile); - sla_print.export_raster(outfile_final); + sla_print.export_raster(outfile_final); } if (outfile != outfile_final && Slic3r::rename_file(outfile, outfile_final)) { - boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl; + boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl; return 1; } boost::nowide::cout << "Slicing result exported to " << outfile << std::endl; } catch (const std::exception &ex) { - boost::nowide::cerr << ex.what() << std::endl; - return 1; + boost::nowide::cerr << ex.what() << std::endl; + return 1; } /* print.center = ! m_config.has("center") && ! m_config.has("align_xy") && ! m_config.opt_bool("dont_arrange"); print.set_model(model); - + // start chronometer typedef std::chrono::high_resolution_clock clock_; typedef std::chrono::duration > second_; std::chrono::time_point t0{ clock_::now() }; - + const std::string outfile = this->output_filepath(model, IO::Gcode); try { print.export_gcode(outfile); @@ -454,7 +458,7 @@ int CLI::run(int argc, char **argv) return 1; } boost::nowide::cout << "G-code exported to " << outfile << std::endl; - + // output some statistics double duration { std::chrono::duration_cast(clock_::now() - t0).count() }; boost::nowide::cout << std::fixed << std::setprecision(0) @@ -471,48 +475,48 @@ int CLI::run(int argc, char **argv) return 1; } } - - if (start_gui) { + + if (start_gui) { #ifdef SLIC3R_GUI // #ifdef USE_WX - GUI::GUI_App *gui = new GUI::GUI_App(); + GUI::GUI_App *gui = new GUI::GUI_App(); // gui->autosave = m_config.opt_string("autosave"); - GUI::GUI_App::SetInstance(gui); - gui->CallAfter([gui, this, &load_configs] { - if (!gui->initialized()) { - return; - } + GUI::GUI_App::SetInstance(gui); + gui->CallAfter([gui, this, &load_configs] { + if (!gui->initialized()) { + return; + } #if 0 - // Load the cummulative config over the currently active profiles. - //FIXME if multiple configs are loaded, only the last one will have an effect. - // We need to decide what to do about loading of separate presets (just print preset, just filament preset etc). - // As of now only the full configs are supported here. - if (!m_print_config.empty()) - gui->mainframe->load_config(m_print_config); + // Load the cummulative config over the currently active profiles. + //FIXME if multiple configs are loaded, only the last one will have an effect. + // We need to decide what to do about loading of separate presets (just print preset, just filament preset etc). + // As of now only the full configs are supported here. + if (!m_print_config.empty()) + gui->mainframe->load_config(m_print_config); #endif - if (! load_configs.empty()) - // Load the last config to give it a name at the UI. The name of the preset may be later - // changed by loading an AMF or 3MF. - //FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config. - gui->mainframe->load_config_file(load_configs.back()); - // If loading a 3MF file, the config is loaded from the last one. - if (! m_input_files.empty()) - gui->plater()->load_files(m_input_files, true, true); - if (! m_extra_config.empty()) - gui->mainframe->load_config(m_extra_config); - }); + if (! load_configs.empty()) + // Load the last config to give it a name at the UI. The name of the preset may be later + // changed by loading an AMF or 3MF. + //FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config. + gui->mainframe->load_config_file(load_configs.back()); + // If loading a 3MF file, the config is loaded from the last one. + if (! m_input_files.empty()) + gui->plater()->load_files(m_input_files, true, true); + if (! m_extra_config.empty()) + gui->mainframe->load_config(m_extra_config); + }); int result = wxEntry(argc, argv); //FIXME this is a workaround for the PrusaSlicer 2.1 release. - _3DScene::destroy(); + _3DScene::destroy(); return result; #else /* SLIC3R_GUI */ - // No GUI support. Just print out a help. - this->print_help(false); - // If started without a parameter, consider it to be OK, otherwise report an error code (no action etc). - return (argc == 0) ? 0 : 1; + // No GUI support. Just print out a help. + this->print_help(false); + // If started without a parameter, consider it to be OK, otherwise report an error code (no action etc). + return (argc == 0) ? 0 : 1; #endif /* SLIC3R_GUI */ } - + return 0; } @@ -560,18 +564,18 @@ bool CLI::setup(int argc, char **argv) // If any option is unsupported, print usage and abort immediately. t_config_option_keys opt_order; if (! m_config.read_cli(argc, argv, &m_input_files, &opt_order)) { - // Separate error message reported by the CLI parser from the help. - boost::nowide::cerr << std::endl; + // Separate error message reported by the CLI parser from the help. + boost::nowide::cerr << std::endl; this->print_help(); - return false; + return false; + } + // Parse actions and transform options. + for (auto const &opt_key : opt_order) { + if (cli_actions_config_def.has(opt_key)) + m_actions.emplace_back(opt_key); + else if (cli_transform_config_def.has(opt_key)) + m_transforms.emplace_back(opt_key); } - // Parse actions and transform options. - for (auto const &opt_key : opt_order) { - if (cli_actions_config_def.has(opt_key)) - m_actions.emplace_back(opt_key); - else if (cli_transform_config_def.has(opt_key)) - m_transforms.emplace_back(opt_key); - } { const ConfigOptionInt *opt_loglevel = m_config.opt("loglevel"); @@ -584,41 +588,41 @@ bool CLI::setup(int argc, char **argv) for (const std::pair &optdef : *options) m_config.optptr(optdef.first, true); - set_data_dir(m_config.opt_string("datadir")); + set_data_dir(m_config.opt_string("datadir")); - return true; + return true; } -void CLI::print_help(bool include_print_options, PrinterTechnology printer_technology) const +void CLI::print_help(bool include_print_options, PrinterTechnology printer_technology) const { boost::nowide::cout - << SLIC3R_BUILD_ID << " " << "based on Slic3r" + << SLIC3R_BUILD_ID << " " << "based on Slic3r" #ifdef SLIC3R_GUI << " (with GUI support)" #else /* SLIC3R_GUI */ << " (without GUI support)" #endif /* SLIC3R_GUI */ << std::endl - << "https://github.com/supermerill/slic3r" << std::endl << std::endl + << "https://github.com/prusa3d/PrusaSlicer" << std::endl << std::endl << "Usage: prusa-slicer [ ACTIONS ] [ TRANSFORM ] [ OPTIONS ] [ file.stl ... ]" << std::endl << std::endl << "Actions:" << std::endl; cli_actions_config_def.print_cli_help(boost::nowide::cout, false); - + boost::nowide::cout << std::endl << "Transform options:" << std::endl; cli_transform_config_def.print_cli_help(boost::nowide::cout, false); - + boost::nowide::cout << std::endl << "Other options:" << std::endl; cli_misc_config_def.print_cli_help(boost::nowide::cout, false); - + if (include_print_options) { boost::nowide::cout << std::endl; - print_config_def.print_cli_help(boost::nowide::cout, true, [printer_technology](const ConfigOptionDef &def) - { return (printer_technology & def.printer_technology) != 0; }); + print_config_def.print_cli_help(boost::nowide::cout, true, [printer_technology](const ConfigOptionDef &def) + { return printer_technology == ptAny || def.printer_technology == ptAny || printer_technology == def.printer_technology; }); } else { boost::nowide::cout << std::endl @@ -634,14 +638,14 @@ bool CLI::export_models(IO::ExportFormat format) switch (format) { case IO::AMF: success = Slic3r::store_amf(path, &model, nullptr); break; case IO::OBJ: success = Slic3r::store_obj(path.c_str(), &model); break; - case IO::STL: success = Slic3r::store_stl(path.c_str(), &model, true); break; - case IO::TMF: success = Slic3r::store_3mf(path.c_str(), &model, nullptr); break; + case IO::STL: success = Slic3r::store_stl(path.c_str(), &model, true); break; + case IO::TMF: success = Slic3r::store_3mf(path.c_str(), &model, nullptr); break; default: assert(false); break; } if (success) - std::cout << "File exported to " << path << std::endl; + std::cout << "File exported to " << path << std::endl; else { - std::cerr << "File export to " << path << " failed" << std::endl; + std::cerr << "File export to " << path << " failed" << std::endl; return false; } } @@ -655,12 +659,12 @@ std::string CLI::output_filepath(const Model &model, IO::ExportFormat format) co case IO::AMF: ext = ".zip.amf"; break; case IO::OBJ: ext = ".obj"; break; case IO::STL: ext = ".stl"; break; - case IO::TMF: ext = ".3mf"; break; + case IO::TMF: ext = ".3mf"; break; default: assert(false); break; }; auto proposed_path = boost::filesystem::path(model.propose_export_file_name_and_path(ext)); // use --output when available - std::string cmdline_param = m_config.opt_string("output"); + std::string cmdline_param = m_config.opt_string("output"); if (! cmdline_param.empty()) { // if we were supplied a directory, use it and append our automatically generated filename boost::filesystem::path cmdline_path(cmdline_param); @@ -674,18 +678,18 @@ std::string CLI::output_filepath(const Model &model, IO::ExportFormat format) co #if defined(_MSC_VER) || defined(__MINGW32__) extern "C" { - __declspec(dllexport) int __stdcall slic3r_main(int argc, wchar_t **argv) - { - // Convert wchar_t arguments to UTF8. - std::vector argv_narrow; - std::vector argv_ptrs(argc + 1, nullptr); - for (size_t i = 0; i < argc; ++ i) - argv_narrow.emplace_back(boost::nowide::narrow(argv[i])); - for (size_t i = 0; i < argc; ++ i) - argv_ptrs[i] = const_cast(argv_narrow[i].data()); - // Call the UTF8 main. - return CLI().run(argc, argv_ptrs.data()); - } + __declspec(dllexport) int __stdcall slic3r_main(int argc, wchar_t **argv) + { + // Convert wchar_t arguments to UTF8. + std::vector argv_narrow; + std::vector argv_ptrs(argc + 1, nullptr); + for (size_t i = 0; i < argc; ++ i) + argv_narrow.emplace_back(boost::nowide::narrow(argv[i])); + for (size_t i = 0; i < argc; ++ i) + argv_ptrs[i] = const_cast(argv_narrow[i].data()); + // Call the UTF8 main. + return CLI().run(argc, argv_ptrs.data()); + } } #else /* _MSC_VER */ int main(int argc, char **argv) diff --git a/src/admesh/stl.h b/src/admesh/stl.h index 98c85722d..82053ce8f 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -89,33 +89,34 @@ struct stl_neighbors { }; struct stl_stats { - stl_stats() { this->reset(); } - void reset() { memset(this, 0, sizeof(stl_stats)); this->volume = -1.0; } - char header[81]; - stl_type type; - uint32_t number_of_facets; - stl_vertex max; - stl_vertex min; - stl_vertex size; - float bounding_diameter; - float shortest_edge; - float volume; - int connected_edges; - int connected_facets_1_edge; - int connected_facets_2_edge; - int connected_facets_3_edge; - int facets_w_1_bad_edge; - int facets_w_2_bad_edge; - int facets_w_3_bad_edge; - int original_num_facets; - int edges_fixed; - int degenerate_facets; - int facets_removed; - int facets_added; - int facets_reversed; - int backwards_edges; - int normals_fixed; - int number_of_parts; + stl_stats() { memset(&header, 0, 81); } + char header[81]; + stl_type type = (stl_type)0; + uint32_t number_of_facets = 0; + stl_vertex max = stl_vertex::Zero(); + stl_vertex min = stl_vertex::Zero(); + stl_vertex size = stl_vertex::Zero(); + float bounding_diameter = 0.f; + float shortest_edge = 0.f; + float volume = -1.f; + int connected_edges = 0; + int connected_facets_1_edge = 0; + int connected_facets_2_edge = 0; + int connected_facets_3_edge = 0; + int facets_w_1_bad_edge = 0; + int facets_w_2_bad_edge = 0; + int facets_w_3_bad_edge = 0; + int original_num_facets = 0; + int edges_fixed = 0; + int degenerate_facets = 0; + int facets_removed = 0; + int facets_added = 0; + int facets_reversed = 0; + int backwards_edges = 0; + int normals_fixed = 0; + int number_of_parts = 0; + + void clear() { *this = stl_stats(); } }; struct stl_file { @@ -124,7 +125,7 @@ struct stl_file { void clear() { this->facet_start.clear(); this->neighbors_start.clear(); - this->stats.reset(); + this->stats.clear(); } size_t memsize() const { @@ -183,10 +184,21 @@ extern void stl_mirror_xz(stl_file *stl); extern void stl_get_size(stl_file *stl); +// the following function is not used +/* template extern void stl_transform(stl_file *stl, T *trafo3x4) { - for (uint32_t i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) { + Eigen::Matrix trafo3x3; + for (int i = 0; i < 3; ++i) + { + for (int j = 0; j < 3; ++j) + { + trafo3x3(i, j) = (i * 4) + j; + } + } + Eigen::Matrix r = trafo3x3.inverse().transpose(); + for (uint32_t i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) { stl_facet &face = stl->facet_start[i_face]; for (int i_vertex = 0; i_vertex < 3; ++ i_vertex) { stl_vertex &v_dst = face.vertex[i_vertex]; @@ -195,21 +207,18 @@ extern void stl_transform(stl_file *stl, T *trafo3x4) v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7]); v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11]); } - stl_vertex &v_dst = face.normal; - stl_vertex v_src = v_dst; - v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2)); - v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2)); - v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2)); - } + face.normal = (r * face.normal.template cast()).template cast().eval(); + } stl_get_size(stl); } +*/ template inline void stl_transform(stl_file *stl, const Eigen::Transform& t) { - const Eigen::Matrix r = t.matrix().template block<3, 3>(0, 0); - for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) { + const Eigen::Matrix r = t.matrix().template block<3, 3>(0, 0).inverse().transpose(); + for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) { stl_facet &f = stl->facet_start[i]; for (size_t j = 0; j < 3; ++j) f.vertex[j] = (t * f.vertex[j].template cast()).template cast().eval(); @@ -222,12 +231,13 @@ inline void stl_transform(stl_file *stl, const Eigen::Transform inline void stl_transform(stl_file *stl, const Eigen::Matrix& m) { - for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) { + const Eigen::Matrix r = m.inverse().transpose(); + for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) { stl_facet &f = stl->facet_start[i]; for (size_t j = 0; j < 3; ++j) f.vertex[j] = (m * f.vertex[j].template cast()).template cast().eval(); - f.normal = (m * f.normal.template cast()).template cast().eval(); - } + f.normal = (r * f.normal.template cast()).template cast().eval(); + } stl_get_size(stl); } diff --git a/src/admesh/stlinit.cpp b/src/admesh/stlinit.cpp index 693aad086..390fe56a4 100644 --- a/src/admesh/stlinit.cpp +++ b/src/admesh/stlinit.cpp @@ -36,6 +36,10 @@ #error "SEEK_SET not defined" #endif +#ifndef BOOST_LITTLE_ENDIAN +extern void stl_internal_reverse_quads(char *buf, size_t cnt); +#endif /* BOOST_LITTLE_ENDIAN */ + static FILE* stl_open_count_facets(stl_file *stl, const char *file) { // Open the file in binary mode first. @@ -238,10 +242,6 @@ bool stl_open(stl_file *stl, const char *file) return result; } -#ifndef BOOST_LITTLE_ENDIAN -extern void stl_internal_reverse_quads(char *buf, size_t cnt); -#endif /* BOOST_LITTLE_ENDIAN */ - void stl_allocate(stl_file *stl) { // Allocate memory for the entire .STL file. diff --git a/src/agg/agg_rasterizer_scanline_aa.h b/src/agg/agg_rasterizer_scanline_aa.h index ffc2ddf94..4925ca209 100644 --- a/src/agg/agg_rasterizer_scanline_aa.h +++ b/src/agg/agg_rasterizer_scanline_aa.h @@ -156,7 +156,7 @@ namespace agg //------------------------------------------------------------------- template - void add_path(VertexSource& vs, unsigned path_id=0) + void add_path(VertexSource &&vs, unsigned path_id=0) { double x; double y; diff --git a/src/clipper/clipper_z.hpp b/src/clipper/clipper_z.hpp index 0f31ac11c..e5e7d48ce 100644 --- a/src/clipper/clipper_z.hpp +++ b/src/clipper/clipper_z.hpp @@ -15,4 +15,4 @@ #undef clipper_hpp #undef use_xyz -#endif clipper_z_hpp +#endif // clipper_z_hpp diff --git a/src/hidapi/CMakeLists.txt b/src/hidapi/CMakeLists.txt new file mode 100644 index 000000000..1f53c9b69 --- /dev/null +++ b/src/hidapi/CMakeLists.txt @@ -0,0 +1,19 @@ + +if (WIN32) + set(HIDAPI_IMPL win/hid.c) +elseif (APPLE) + set(HIDAPI_IMPL mac/hid.c) +else () + # Assume Linux or Unix other than Mac OS + set(HIDAPI_IMPL linux/hid.c) +endif() + +include_directories(include) + +add_library(hidapi STATIC ${HIDAPI_IMPL}) + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + # Don't link the udev library, as there are two versions out there (libudev.so.0, libudev.so.1), so they are linked explicitely. +# target_link_libraries(hidapi udev) + target_link_libraries(hidapi) +endif() diff --git a/src/hidapi/include/hidapi.h b/src/hidapi/include/hidapi.h new file mode 100644 index 000000000..1819f8de0 --- /dev/null +++ b/src/hidapi/include/hidapi.h @@ -0,0 +1,395 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +/** @file + * @defgroup API hidapi API + */ + +#ifndef HIDAPI_H__ +#define HIDAPI_H__ + +#include + +#ifdef _WIN32 + #define HID_API_EXPORT __declspec(dllexport) + #define HID_API_CALL +#else + #define HID_API_EXPORT /**< API export macro */ + #define HID_API_CALL /**< API call macro */ +#endif + +#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/ + +#ifdef __cplusplus +extern "C" { +#endif + struct hid_device_; + typedef struct hid_device_ hid_device; /**< opaque hidapi structure */ + + /** hidapi info structure */ + struct hid_device_info { + /** Platform-specific device path */ + char *path; + /** Device Vendor ID */ + unsigned short vendor_id; + /** Device Product ID */ + unsigned short product_id; + /** Serial Number */ + wchar_t *serial_number; + /** Device Release Number in binary-coded decimal, + also known as Device Version Number */ + unsigned short release_number; + /** Manufacturer String */ + wchar_t *manufacturer_string; + /** Product string */ + wchar_t *product_string; + /** Usage Page for this Device/Interface + (Windows/Mac only). */ + unsigned short usage_page; + /** Usage for this Device/Interface + (Windows/Mac only).*/ + unsigned short usage; + /** The USB interface which this logical device + represents. + + * Valid on both Linux implementations in all cases. + * Valid on the Windows implementation only if the device + contains more than one interface. + * Valid on the Mac implementation if and only if the device + is a USB HID device. */ + int interface_number; + + /** Pointer to the next device */ + struct hid_device_info *next; + }; + + + /** @brief Initialize the HIDAPI library. + + This function initializes the HIDAPI library. Calling it is not + strictly necessary, as it will be called automatically by + hid_enumerate() and any of the hid_open_*() functions if it is + needed. This function should be called at the beginning of + execution however, if there is a chance of HIDAPI handles + being opened by different threads simultaneously. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_init(void); + + /** @brief Finalize the HIDAPI library. + + This function frees all of the static data associated with + HIDAPI. It should be called at the end of execution to avoid + memory leaks. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_exit(void); + + /** @brief Enumerate the HID Devices. + + This function returns a linked list of all the HID devices + attached to the system which match vendor_id and product_id. + If @p vendor_id is set to 0 then any vendor matches. + If @p product_id is set to 0 then any product matches. + If @p vendor_id and @p product_id are both set to 0, then + all HID devices will be returned. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the types of device + to open. + @param product_id The Product ID (PID) of the types of + device to open. + + @returns + This function returns a pointer to a linked list of type + struct #hid_device_info, containing information about the HID devices + attached to the system, or NULL in the case of failure. Free + this linked list by calling hid_free_enumeration(). + */ + struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); + + /** @brief Free an enumeration Linked List + + This function frees a linked list created by hid_enumerate(). + + @ingroup API + @param devs Pointer to a list of struct_device returned from + hid_enumerate(). + */ + void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); + + /** @brief Open a HID device using a Vendor ID (VID), Product ID + (PID) and optionally a serial number. + + If @p serial_number is NULL, the first device with the + specified VID and PID is opened. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the device to open. + @param product_id The Product ID (PID) of the device to open. + @param serial_number The Serial Number of the device to open + (Optionally NULL). + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number); + + /** @brief Open a HID device by its path name. + + The path name be determined by calling hid_enumerate(), or a + platform-specific path name can be used (eg: /dev/hidraw0 on + Linux). + + @ingroup API + @param path The path name of the device to open + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path); + + /** @brief Write an Output report to a HID device. + + The first byte of @p data[] must contain the Report ID. For + devices which only support a single report, this must be set + to 0x0. The remaining bytes contain the report data. Since + the Report ID is mandatory, calls to hid_write() will always + contain one more byte than the report contains. For example, + if a hid report is 16 bytes long, 17 bytes must be passed to + hid_write(), the Report ID (or 0x0, for devices with a + single report), followed by the report data (16 bytes). In + this example, the length passed in would be 17. + + hid_write() will send the data on the first OUT endpoint, if + one exists. If it does not, it will send the data through + the Control Endpoint (Endpoint 0). + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length); + + /** @brief Read an Input report from a HID device with timeout. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + @param milliseconds timeout in milliseconds or -1 for blocking wait. + + @returns + This function returns the actual number of bytes read and + -1 on error. If no packet was available to be read within + the timeout period, this function returns 0. + */ + int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); + + /** @brief Read an Input report from a HID device. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + + @returns + This function returns the actual number of bytes read and + -1 on error. If no packet was available to be read and + the handle is in non-blocking mode, this function returns 0. + */ + int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length); + + /** @brief Set the device handle to be non-blocking. + + In non-blocking mode calls to hid_read() will return + immediately with a value of 0 if there is no data to be + read. In blocking mode, hid_read() will wait (block) until + there is data to read before returning. + + Nonblocking can be turned on and off at any time. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param nonblock enable or not the nonblocking reads + - 1 to enable nonblocking + - 0 to disable nonblocking. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock); + + /** @brief Send a Feature report to the device. + + Feature reports are sent over the Control endpoint as a + Set_Report transfer. The first byte of @p data[] must + contain the Report ID. For devices which only support a + single report, this must be set to 0x0. The remaining bytes + contain the report data. Since the Report ID is mandatory, + calls to hid_send_feature_report() will always contain one + more byte than the report contains. For example, if a hid + report is 16 bytes long, 17 bytes must be passed to + hid_send_feature_report(): the Report ID (or 0x0, for + devices which do not use numbered reports), followed by the + report data (16 bytes). In this example, the length passed + in would be 17. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send, including + the report number. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length); + + /** @brief Get a feature report from a HID device. + + Set the first byte of @p data[] to the Report ID of the + report to be read. Make sure to allow space for this + extra byte in @p data[]. Upon return, the first byte will + still contain the Report ID, and the report data will + start in data[1]. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data A buffer to put the read data into, including + the Report ID. Set the first byte of @p data[] to the + Report ID of the report to be read, or set it to zero + if your device does not use numbered reports. + @param length The number of bytes to read, including an + extra byte for the report ID. The buffer can be longer + than the actual report. + + @returns + This function returns the number of bytes read plus + one for the report ID (which is still in the first + byte), or -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length); + + /** @brief Close a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + */ + void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev); + + /** @brief Get The Manufacturer String from a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen); + + /** @brief Get The Product String from a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen); + + /** @brief Get The Serial Number String from a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen); + + /** @brief Get a string from a HID device, based on its string index. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string_index The index of the string to get. + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen); + + /** @brief Get a string describing the last error which occurred. + + @ingroup API + @param dev A device handle returned from hid_open(). + + @returns + This function returns a string containing the last error + which occurred or NULL if none has occurred. + */ + HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/hidapi/linux/hid.c b/src/hidapi/linux/hid.c new file mode 100644 index 000000000..a62bcffb6 --- /dev/null +++ b/src/hidapi/linux/hid.c @@ -0,0 +1,918 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + Linux Version - 6/2/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +/* C */ +#include +#include +#include +#include +#include + +/* Unix */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* Linux */ +#include +#include +#include +#include + +#include "hidapi.h" + +typedef const char* (*hid_wrapper_udev_device_get_devnode_type)(struct udev_device *udev_device); +typedef struct udev_device* (*hid_wrapper_udev_device_get_parent_with_subsystem_devtype_type)(struct udev_device *udev_device, const char *subsystem, const char *devtype); +typedef const char* (*hid_wrapper_udev_device_get_sysattr_value_type)(struct udev_device *udev_device, const char *sysattr); +typedef struct udev_device* (*hid_wrapper_udev_device_new_from_devnum_type)(struct udev *udev, char type, dev_t devnum); +typedef struct udev_device* (*hid_wrapper_udev_device_new_from_syspath_type)(struct udev *udev, const char *syspath); +typedef struct udev_device* (*hid_wrapper_udev_device_unref_type)(struct udev_device *udev_device); +typedef int (*hid_wrapper_udev_enumerate_add_match_subsystem_type)(struct udev_enumerate *udev_enumerate, const char *subsystem); +typedef struct udev_list_entry* (*hid_wrapper_udev_enumerate_get_list_entry_type)(struct udev_enumerate *udev_enumerate); +typedef struct udev_enumerate* (*hid_wrapper_udev_enumerate_new_type)(struct udev *udev); +typedef int (*hid_wrapper_udev_enumerate_scan_devices_type)(struct udev_enumerate *udev_enumerate); +typedef struct udev_enumerate* (*hid_wrapper_udev_enumerate_unref_type)(struct udev_enumerate *udev_enumerate); +typedef const char* (*hid_wrapper_udev_list_entry_get_name_type)(struct udev_list_entry *list_entry); +typedef struct udev_list_entry* (*hid_wrapper_udev_list_entry_get_next_type)(struct udev_list_entry *list_entry); +typedef struct udev* (*hid_wrapper_udev_new_type)(void); +typedef struct udev* (*hid_wrapper_udev_unref_type)(struct udev *udev); + +void *hid_wrapper_handle = NULL; +static hid_wrapper_udev_device_get_devnode_type hid_wrapper_udev_device_get_devnode = NULL; +static hid_wrapper_udev_device_get_parent_with_subsystem_devtype_type hid_wrapper_udev_device_get_parent_with_subsystem_devtype = NULL; +static hid_wrapper_udev_device_get_sysattr_value_type hid_wrapper_udev_device_get_sysattr_value = NULL; +static hid_wrapper_udev_device_new_from_devnum_type hid_wrapper_udev_device_new_from_devnum = NULL; +static hid_wrapper_udev_device_new_from_syspath_type hid_wrapper_udev_device_new_from_syspath = NULL; +static hid_wrapper_udev_device_unref_type hid_wrapper_udev_device_unref = NULL; +static hid_wrapper_udev_enumerate_add_match_subsystem_type hid_wrapper_udev_enumerate_add_match_subsystem = NULL; +static hid_wrapper_udev_enumerate_get_list_entry_type hid_wrapper_udev_enumerate_get_list_entry = NULL; +static hid_wrapper_udev_enumerate_new_type hid_wrapper_udev_enumerate_new = NULL; +static hid_wrapper_udev_enumerate_scan_devices_type hid_wrapper_udev_enumerate_scan_devices = NULL; +static hid_wrapper_udev_enumerate_unref_type hid_wrapper_udev_enumerate_unref = NULL; +static hid_wrapper_udev_list_entry_get_name_type hid_wrapper_udev_list_entry_get_name = NULL; +static hid_wrapper_udev_list_entry_get_next_type hid_wrapper_udev_list_entry_get_next = NULL; +static hid_wrapper_udev_new_type hid_wrapper_udev_new = NULL; +static hid_wrapper_udev_unref_type hid_wrapper_udev_unref = NULL; + +static void hid_wrapper_udev_close() +{ + if (hid_wrapper_handle) + dlclose(hid_wrapper_handle); + hid_wrapper_handle = NULL; + hid_wrapper_udev_device_get_devnode = NULL; + hid_wrapper_udev_device_get_parent_with_subsystem_devtype = NULL; + hid_wrapper_udev_device_get_sysattr_value = NULL; + hid_wrapper_udev_device_new_from_devnum = NULL; + hid_wrapper_udev_device_new_from_syspath = NULL; + hid_wrapper_udev_device_unref = NULL; + hid_wrapper_udev_enumerate_add_match_subsystem = NULL; + hid_wrapper_udev_enumerate_get_list_entry = NULL; + hid_wrapper_udev_enumerate_new = NULL; + hid_wrapper_udev_enumerate_scan_devices = NULL; + hid_wrapper_udev_enumerate_unref = NULL; + hid_wrapper_udev_list_entry_get_name = NULL; + hid_wrapper_udev_list_entry_get_next = NULL; + hid_wrapper_udev_new = NULL; + hid_wrapper_udev_unref = NULL; +} + +static const char *hid_wrapper_libudev_paths[] = { + "libudev.so.1", "libudev.so.0" +}; + +static int hid_wrapper_udev_init() +{ + int i; + + hid_wrapper_udev_close(); + + // Search for the libudev0 or libudev1 library. + for (i = 0; i < sizeof(hid_wrapper_libudev_paths) / sizeof(hid_wrapper_libudev_paths[0]); ++ i) + if ((hid_wrapper_handle = dlopen(hid_wrapper_libudev_paths[i], RTLD_NOW | RTLD_GLOBAL)) != NULL) + break; + + if (hid_wrapper_handle == NULL) { + // Error, close the shared library handle and finish. + hid_wrapper_udev_close(); + return -1; + } + + // Resolve the functions. + hid_wrapper_udev_device_get_devnode = (hid_wrapper_udev_device_get_devnode_type) dlsym(hid_wrapper_handle, "udev_device_get_devnode"); + hid_wrapper_udev_device_get_parent_with_subsystem_devtype = (hid_wrapper_udev_device_get_parent_with_subsystem_devtype_type) dlsym(hid_wrapper_handle, "udev_device_get_parent_with_subsystem_devtype"); + hid_wrapper_udev_device_get_sysattr_value = (hid_wrapper_udev_device_get_sysattr_value_type) dlsym(hid_wrapper_handle, "udev_device_get_sysattr_value"); + hid_wrapper_udev_device_new_from_devnum = (hid_wrapper_udev_device_new_from_devnum_type) dlsym(hid_wrapper_handle, "udev_device_new_from_devnum"); + hid_wrapper_udev_device_new_from_syspath = (hid_wrapper_udev_device_new_from_syspath_type) dlsym(hid_wrapper_handle, "udev_device_new_from_syspath"); + hid_wrapper_udev_device_unref = (hid_wrapper_udev_device_unref_type) dlsym(hid_wrapper_handle, "udev_device_unref"); + hid_wrapper_udev_enumerate_add_match_subsystem = (hid_wrapper_udev_enumerate_add_match_subsystem_type) dlsym(hid_wrapper_handle, "udev_enumerate_add_match_subsystem"); + hid_wrapper_udev_enumerate_get_list_entry = (hid_wrapper_udev_enumerate_get_list_entry_type) dlsym(hid_wrapper_handle, "udev_enumerate_get_list_entry"); + hid_wrapper_udev_enumerate_new = (hid_wrapper_udev_enumerate_new_type) dlsym(hid_wrapper_handle, "udev_enumerate_new"); + hid_wrapper_udev_enumerate_scan_devices = (hid_wrapper_udev_enumerate_scan_devices_type) dlsym(hid_wrapper_handle, "udev_enumerate_scan_devices"); + hid_wrapper_udev_enumerate_unref = (hid_wrapper_udev_enumerate_unref_type) dlsym(hid_wrapper_handle, "udev_enumerate_unref"); + hid_wrapper_udev_list_entry_get_name = (hid_wrapper_udev_list_entry_get_name_type) dlsym(hid_wrapper_handle, "udev_list_entry_get_name"); + hid_wrapper_udev_list_entry_get_next = (hid_wrapper_udev_list_entry_get_next_type) dlsym(hid_wrapper_handle, "udev_list_entry_get_next"); + hid_wrapper_udev_new = (hid_wrapper_udev_new_type) dlsym(hid_wrapper_handle, "udev_new"); + hid_wrapper_udev_unref = (hid_wrapper_udev_unref_type) dlsym(hid_wrapper_handle, "udev_unref"); + + // Were all the funcions resolved? + if (hid_wrapper_handle == NULL || + hid_wrapper_udev_device_get_devnode == NULL || + hid_wrapper_udev_device_get_parent_with_subsystem_devtype == NULL || + hid_wrapper_udev_device_get_sysattr_value == NULL || + hid_wrapper_udev_device_new_from_devnum == NULL || + hid_wrapper_udev_device_new_from_syspath == NULL || + hid_wrapper_udev_device_unref == NULL || + hid_wrapper_udev_enumerate_add_match_subsystem == NULL || + hid_wrapper_udev_enumerate_get_list_entry == NULL || + hid_wrapper_udev_enumerate_new == NULL || + hid_wrapper_udev_enumerate_scan_devices == NULL || + hid_wrapper_udev_enumerate_unref == NULL || + hid_wrapper_udev_list_entry_get_name == NULL || + hid_wrapper_udev_list_entry_get_next == NULL || + hid_wrapper_udev_new == NULL || + hid_wrapper_udev_unref == NULL) + { + // Error, close the shared library handle and finish. + hid_wrapper_udev_close(); + return -1; + } + + // Success. + return 0; +} + +/* Definitions from linux/hidraw.h. Since these are new, some distros + may not have header files which contain them. */ +#ifndef HIDIOCSFEATURE +#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len) +#endif +#ifndef HIDIOCGFEATURE +#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len) +#endif + + +/* USB HID device property names */ +const char *device_string_names[] = { + "manufacturer", + "product", + "serial", +}; + +/* Symbolic names for the properties above */ +enum device_string_id { + DEVICE_STRING_MANUFACTURER, + DEVICE_STRING_PRODUCT, + DEVICE_STRING_SERIAL, + + DEVICE_STRING_COUNT, +}; + +struct hid_device_ { + int device_handle; + int blocking; + int uses_numbered_reports; +}; + + +static __u32 kernel_version = 0; + +static __u32 detect_kernel_version(void) +{ + struct utsname name; + int major, minor, release; + int ret; + + uname(&name); + ret = sscanf(name.release, "%d.%d.%d", &major, &minor, &release); + if (ret == 3) { + return KERNEL_VERSION(major, minor, release); + } + + ret = sscanf(name.release, "%d.%d", &major, &minor); + if (ret == 2) { + return KERNEL_VERSION(major, minor, 0); + } + + printf("Couldn't determine kernel version from version string \"%s\"\n", name.release); + return 0; +} + +static hid_device *new_hid_device(void) +{ + hid_device *dev = calloc(1, sizeof(hid_device)); + dev->device_handle = -1; + dev->blocking = 1; + dev->uses_numbered_reports = 0; + + return dev; +} + + +/* The caller must free the returned string with free(). */ +static wchar_t *utf8_to_wchar_t(const char *utf8) +{ + wchar_t *ret = NULL; + + if (utf8) { + size_t wlen = mbstowcs(NULL, utf8, 0); + if ((size_t) -1 == wlen) { + return wcsdup(L""); + } + ret = calloc(wlen+1, sizeof(wchar_t)); + mbstowcs(ret, utf8, wlen+1); + ret[wlen] = 0x0000; + } + + return ret; +} + +/* Get an attribute value from a udev_device and return it as a whar_t + string. The returned string must be freed with free() when done.*/ +static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name) +{ + return utf8_to_wchar_t(hid_wrapper_udev_device_get_sysattr_value(dev, udev_name)); +} + +/* uses_numbered_reports() returns 1 if report_descriptor describes a device + which contains numbered reports. */ +static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) { + unsigned int i = 0; + int size_code; + int data_len, key_size; + + while (i < size) { + int key = report_descriptor[i]; + + /* Check for the Report ID key */ + if (key == 0x85/*Report ID*/) { + /* This device has a Report ID, which means it uses + numbered reports. */ + return 1; + } + + //printf("key: %02hhx\n", key); + + if ((key & 0xf0) == 0xf0) { + /* This is a Long Item. The next byte contains the + length of the data section (value) for this key. + See the HID specification, version 1.11, section + 6.2.2.3, titled "Long Items." */ + if (i+1 < size) + data_len = report_descriptor[i+1]; + else + data_len = 0; /* malformed report */ + key_size = 3; + } + else { + /* This is a Short Item. The bottom two bits of the + key contain the size code for the data section + (value) for this key. Refer to the HID + specification, version 1.11, section 6.2.2.2, + titled "Short Items." */ + size_code = key & 0x3; + switch (size_code) { + case 0: + case 1: + case 2: + data_len = size_code; + break; + case 3: + data_len = 4; + break; + default: + /* Can't ever happen since size_code is & 0x3 */ + data_len = 0; + break; + }; + key_size = 1; + } + + /* Skip over this key and it's associated data */ + i += data_len + key_size; + } + + /* Didn't find a Report ID key. Device doesn't use numbered reports. */ + return 0; +} + +/* + * The caller is responsible for free()ing the (newly-allocated) character + * strings pointed to by serial_number_utf8 and product_name_utf8 after use. + */ +static int +parse_uevent_info(const char *uevent, int *bus_type, + unsigned short *vendor_id, unsigned short *product_id, + char **serial_number_utf8, char **product_name_utf8) +{ + char *tmp = strdup(uevent); + char *saveptr = NULL; + char *line; + char *key; + char *value; + + int found_id = 0; + int found_serial = 0; + int found_name = 0; + + line = strtok_r(tmp, "\n", &saveptr); + while (line != NULL) { + /* line: "KEY=value" */ + key = line; + value = strchr(line, '='); + if (!value) { + goto next_line; + } + *value = '\0'; + value++; + + if (strcmp(key, "HID_ID") == 0) { + /** + * type vendor product + * HID_ID=0003:000005AC:00008242 + **/ + int ret = sscanf(value, "%x:%hx:%hx", bus_type, vendor_id, product_id); + if (ret == 3) { + found_id = 1; + } + } else if (strcmp(key, "HID_NAME") == 0) { + /* The caller has to free the product name */ + *product_name_utf8 = strdup(value); + found_name = 1; + } else if (strcmp(key, "HID_UNIQ") == 0) { + /* The caller has to free the serial number */ + *serial_number_utf8 = strdup(value); + found_serial = 1; + } + +next_line: + line = strtok_r(NULL, "\n", &saveptr); + } + + free(tmp); + return (found_id && found_name && found_serial); +} + + +static int get_device_string(hid_device *dev, enum device_string_id key, wchar_t *string, size_t maxlen) +{ + struct udev *udev; + struct udev_device *udev_dev, *parent, *hid_dev; + struct stat s; + int ret = -1; + char *serial_number_utf8 = NULL; + char *product_name_utf8 = NULL; + + /* Create the udev object */ + udev = hid_wrapper_udev_new(); + if (!udev) { + printf("Can't create udev\n"); + return -1; + } + + /* Get the dev_t (major/minor numbers) from the file handle. */ + ret = fstat(dev->device_handle, &s); + if (-1 == ret) + return ret; + /* Open a udev device from the dev_t. 'c' means character device. */ + udev_dev = hid_wrapper_udev_device_new_from_devnum(udev, 'c', s.st_rdev); + if (udev_dev) { + hid_dev = hid_wrapper_udev_device_get_parent_with_subsystem_devtype( + udev_dev, + "hid", + NULL); + if (hid_dev) { + unsigned short dev_vid; + unsigned short dev_pid; + int bus_type; + size_t retm; + + ret = parse_uevent_info( + hid_wrapper_udev_device_get_sysattr_value(hid_dev, "uevent"), + &bus_type, + &dev_vid, + &dev_pid, + &serial_number_utf8, + &product_name_utf8); + + if (bus_type == BUS_BLUETOOTH) { + switch (key) { + case DEVICE_STRING_MANUFACTURER: + wcsncpy(string, L"", maxlen); + ret = 0; + break; + case DEVICE_STRING_PRODUCT: + retm = mbstowcs(string, product_name_utf8, maxlen); + ret = (retm == (size_t)-1)? -1: 0; + break; + case DEVICE_STRING_SERIAL: + retm = mbstowcs(string, serial_number_utf8, maxlen); + ret = (retm == (size_t)-1)? -1: 0; + break; + case DEVICE_STRING_COUNT: + default: + ret = -1; + break; + } + } + else { + /* This is a USB device. Find its parent USB Device node. */ + parent = hid_wrapper_udev_device_get_parent_with_subsystem_devtype( + udev_dev, + "usb", + "usb_device"); + if (parent) { + const char *str; + const char *key_str = NULL; + + if (key >= 0 && key < DEVICE_STRING_COUNT) { + key_str = device_string_names[key]; + } else { + ret = -1; + goto end; + } + + str = hid_wrapper_udev_device_get_sysattr_value(parent, key_str); + if (str) { + /* Convert the string from UTF-8 to wchar_t */ + retm = mbstowcs(string, str, maxlen); + ret = (retm == (size_t)-1)? -1: 0; + goto end; + } + } + } + } + } + +end: + free(serial_number_utf8); + free(product_name_utf8); + + hid_wrapper_udev_device_unref(udev_dev); + /* parent and hid_dev don't need to be (and can't be) unref'd. + I'm not sure why, but they'll throw double-free() errors. */ + hid_wrapper_udev_unref(udev); + + return ret; +} + +int HID_API_EXPORT hid_init(void) +{ + const char *locale; + + /* Set the locale if it's not set. */ + locale = setlocale(LC_CTYPE, NULL); + if (!locale) + setlocale(LC_CTYPE, ""); + + kernel_version = detect_kernel_version(); + + return hid_wrapper_udev_init(); +} + +int HID_API_EXPORT hid_exit(void) +{ + /* Nothing to do for this in the Linux/hidraw implementation. */ + hid_wrapper_udev_close(); + return 0; +} + +struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + struct udev *udev; + struct udev_enumerate *enumerate; + struct udev_list_entry *devices, *dev_list_entry; + + struct hid_device_info *root = NULL; /* return object */ + struct hid_device_info *cur_dev = NULL; + struct hid_device_info *prev_dev = NULL; /* previous device */ + + hid_init(); + + /* Create the udev object */ + udev = hid_wrapper_udev_new(); + if (!udev) { + printf("Can't create udev\n"); + return NULL; + } + + /* Create a list of the devices in the 'hidraw' subsystem. */ + enumerate = hid_wrapper_udev_enumerate_new(udev); + hid_wrapper_udev_enumerate_add_match_subsystem(enumerate, "hidraw"); + hid_wrapper_udev_enumerate_scan_devices(enumerate); + devices = hid_wrapper_udev_enumerate_get_list_entry(enumerate); + /* For each item, see if it matches the vid/pid, and if so + create a udev_device record for it */ + for (dev_list_entry = devices; dev_list_entry; dev_list_entry = hid_wrapper_udev_list_entry_get_next(dev_list_entry)) { + const char *sysfs_path; + const char *dev_path; + const char *str; + struct udev_device *raw_dev; /* The device's hidraw udev node. */ + struct udev_device *hid_dev; /* The device's HID udev node. */ + struct udev_device *usb_dev; /* The device's USB udev node. */ + struct udev_device *intf_dev; /* The device's interface (in the USB sense). */ + unsigned short dev_vid; + unsigned short dev_pid; + char *serial_number_utf8 = NULL; + char *product_name_utf8 = NULL; + int bus_type; + int result; + + /* Get the filename of the /sys entry for the device + and create a udev_device object (dev) representing it */ + sysfs_path = hid_wrapper_udev_list_entry_get_name(dev_list_entry); + raw_dev = hid_wrapper_udev_device_new_from_syspath(udev, sysfs_path); + dev_path = hid_wrapper_udev_device_get_devnode(raw_dev); + + hid_dev = hid_wrapper_udev_device_get_parent_with_subsystem_devtype( + raw_dev, + "hid", + NULL); + + if (!hid_dev) { + /* Unable to find parent hid device. */ + goto next; + } + + result = parse_uevent_info( + hid_wrapper_udev_device_get_sysattr_value(hid_dev, "uevent"), + &bus_type, + &dev_vid, + &dev_pid, + &serial_number_utf8, + &product_name_utf8); + + if (!result) { + /* parse_uevent_info() failed for at least one field. */ + goto next; + } + + if (bus_type != BUS_USB && bus_type != BUS_BLUETOOTH) { + /* We only know how to handle USB and BT devices. */ + goto next; + } + + /* Check the VID/PID against the arguments */ + if ((vendor_id == 0x0 || vendor_id == dev_vid) && + (product_id == 0x0 || product_id == dev_pid)) { + struct hid_device_info *tmp; + + /* VID/PID match. Create the record. */ + tmp = malloc(sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + prev_dev = cur_dev; + cur_dev = tmp; + + /* Fill out the record */ + cur_dev->next = NULL; + cur_dev->path = dev_path? strdup(dev_path): NULL; + + /* VID/PID */ + cur_dev->vendor_id = dev_vid; + cur_dev->product_id = dev_pid; + + /* Serial Number */ + cur_dev->serial_number = utf8_to_wchar_t(serial_number_utf8); + + /* Release Number */ + cur_dev->release_number = 0x0; + + /* Interface Number */ + cur_dev->interface_number = -1; + + switch (bus_type) { + case BUS_USB: + /* The device pointed to by raw_dev contains information about + the hidraw device. In order to get information about the + USB device, get the parent device with the + subsystem/devtype pair of "usb"/"usb_device". This will + be several levels up the tree, but the function will find + it. */ + usb_dev = hid_wrapper_udev_device_get_parent_with_subsystem_devtype( + raw_dev, + "usb", + "usb_device"); + + if (!usb_dev) { + /* Free this device */ + free(cur_dev->serial_number); + free(cur_dev->path); + free(cur_dev); + + /* Take it off the device list. */ + if (prev_dev) { + prev_dev->next = NULL; + cur_dev = prev_dev; + } + else { + cur_dev = root = NULL; + } + + goto next; + } + + /* Manufacturer and Product strings */ + cur_dev->manufacturer_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_MANUFACTURER]); + cur_dev->product_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_PRODUCT]); + + /* Release Number */ + str = hid_wrapper_udev_device_get_sysattr_value(usb_dev, "bcdDevice"); + cur_dev->release_number = (str)? strtol(str, NULL, 16): 0x0; + + /* Get a handle to the interface's udev node. */ + intf_dev = hid_wrapper_udev_device_get_parent_with_subsystem_devtype( + raw_dev, + "usb", + "usb_interface"); + if (intf_dev) { + str = hid_wrapper_udev_device_get_sysattr_value(intf_dev, "bInterfaceNumber"); + cur_dev->interface_number = (str)? strtol(str, NULL, 16): -1; + } + + break; + + case BUS_BLUETOOTH: + /* Manufacturer and Product strings */ + cur_dev->manufacturer_string = wcsdup(L""); + cur_dev->product_string = utf8_to_wchar_t(product_name_utf8); + + break; + + default: + /* Unknown device type - this should never happen, as we + * check for USB and Bluetooth devices above */ + break; + } + } + + next: + free(serial_number_utf8); + free(product_name_utf8); + hid_wrapper_udev_device_unref(raw_dev); + /* hid_dev, usb_dev and intf_dev don't need to be (and can't be) + unref()d. It will cause a double-free() error. I'm not + sure why. */ + } + /* Free the enumerator and udev objects. */ + hid_wrapper_udev_enumerate_unref(enumerate); + hid_wrapper_udev_unref(udev); + + return root; +} + +void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) +{ + struct hid_device_info *d = devs; + while (d) { + struct hid_device_info *next = d->next; + free(d->path); + free(d->serial_number); + free(d->manufacturer_string); + free(d->product_string); + free(d); + d = next; + } +} + +hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) +{ + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + devs = hid_enumerate(vendor_id, product_id); + cur_dev = devs; + while (cur_dev) { + if (cur_dev->vendor_id == vendor_id && + cur_dev->product_id == product_id) { + if (serial_number) { + if (wcscmp(serial_number, cur_dev->serial_number) == 0) { + path_to_open = cur_dev->path; + break; + } + } + else { + path_to_open = cur_dev->path; + break; + } + } + cur_dev = cur_dev->next; + } + + if (path_to_open) { + /* Open the device */ + handle = hid_open_path(path_to_open); + } + + hid_free_enumeration(devs); + + return handle; +} + +hid_device * HID_API_EXPORT hid_open_path(const char *path) +{ + hid_device *dev = NULL; + + hid_init(); + + dev = new_hid_device(); + + /* OPEN HERE */ + dev->device_handle = open(path, O_RDWR); + + /* If we have a good handle, return it. */ + if (dev->device_handle > 0) { + + /* Get the report descriptor */ + int res, desc_size = 0; + struct hidraw_report_descriptor rpt_desc; + + memset(&rpt_desc, 0x0, sizeof(rpt_desc)); + + /* Get Report Descriptor Size */ + res = ioctl(dev->device_handle, HIDIOCGRDESCSIZE, &desc_size); + if (res < 0) + perror("HIDIOCGRDESCSIZE"); + + + /* Get Report Descriptor */ + rpt_desc.size = desc_size; + res = ioctl(dev->device_handle, HIDIOCGRDESC, &rpt_desc); + if (res < 0) { + perror("HIDIOCGRDESC"); + } else { + /* Determine if this device uses numbered reports. */ + dev->uses_numbered_reports = + uses_numbered_reports(rpt_desc.value, + rpt_desc.size); + } + + return dev; + } + else { + /* Unable to open any devices. */ + free(dev); + return NULL; + } +} + + +int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + int bytes_written; + + bytes_written = write(dev->device_handle, data, length); + + return bytes_written; +} + + +int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + int bytes_read; + + if (milliseconds >= 0) { + /* Milliseconds is either 0 (non-blocking) or > 0 (contains + a valid timeout). In both cases we want to call poll() + and wait for data to arrive. Don't rely on non-blocking + operation (O_NONBLOCK) since some kernels don't seem to + properly report device disconnection through read() when + in non-blocking mode. */ + int ret; + struct pollfd fds; + + fds.fd = dev->device_handle; + fds.events = POLLIN; + fds.revents = 0; + ret = poll(&fds, 1, milliseconds); + if (ret == -1 || ret == 0) { + /* Error or timeout */ + return ret; + } + else { + /* Check for errors on the file descriptor. This will + indicate a device disconnection. */ + if (fds.revents & (POLLERR | POLLHUP | POLLNVAL)) + return -1; + } + } + + bytes_read = read(dev->device_handle, data, length); + if (bytes_read < 0 && (errno == EAGAIN || errno == EINPROGRESS)) + bytes_read = 0; + + if (bytes_read >= 0 && + kernel_version != 0 && + kernel_version < KERNEL_VERSION(2,6,34) && + dev->uses_numbered_reports) { + /* Work around a kernel bug. Chop off the first byte. */ + memmove(data, data+1, bytes_read); + bytes_read--; + } + + return bytes_read; +} + +int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) +{ + return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); +} + +int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) +{ + /* Do all non-blocking in userspace using poll(), since it looks + like there's a bug in the kernel in some versions where + read() will not return -1 on disconnection of the USB device */ + + dev->blocking = !nonblock; + return 0; /* Success */ +} + + +int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + int res; + + res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data); + if (res < 0) + perror("ioctl (SFEATURE)"); + + return res; +} + +int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + int res; + + res = ioctl(dev->device_handle, HIDIOCGFEATURE(length), data); + if (res < 0) + perror("ioctl (GFEATURE)"); + + + return res; +} + + +void HID_API_EXPORT hid_close(hid_device *dev) +{ + if (!dev) + return; + close(dev->device_handle); + free(dev); +} + + +int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_device_string(dev, DEVICE_STRING_MANUFACTURER, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_device_string(dev, DEVICE_STRING_PRODUCT, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_device_string(dev, DEVICE_STRING_SERIAL, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + return -1; +} + + +HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) +{ + return NULL; +} diff --git a/src/hidapi/mac/hid.c b/src/hidapi/mac/hid.c new file mode 100644 index 000000000..ca10a9cca --- /dev/null +++ b/src/hidapi/mac/hid.c @@ -0,0 +1,1121 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 2010-07-03 + + Copyright 2010, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +/* See Apple Technical Note TN2187 for details on IOHidManager. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hidapi.h" + +/* Barrier implementation because Mac OSX doesn't have pthread_barrier. + It also doesn't have clock_gettime(). So much for POSIX and SUSv2. + This implementation came from Brent Priddy and was posted on + StackOverflow. It is used with his permission. */ +typedef int pthread_barrierattr_t; +typedef struct pthread_barrier { + pthread_mutex_t mutex; + pthread_cond_t cond; + int count; + int trip_count; +} pthread_barrier_t; + +static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) +{ + if(count == 0) { + errno = EINVAL; + return -1; + } + + if(pthread_mutex_init(&barrier->mutex, 0) < 0) { + return -1; + } + if(pthread_cond_init(&barrier->cond, 0) < 0) { + pthread_mutex_destroy(&barrier->mutex); + return -1; + } + barrier->trip_count = count; + barrier->count = 0; + + return 0; +} + +static int pthread_barrier_destroy(pthread_barrier_t *barrier) +{ + pthread_cond_destroy(&barrier->cond); + pthread_mutex_destroy(&barrier->mutex); + return 0; +} + +static int pthread_barrier_wait(pthread_barrier_t *barrier) +{ + pthread_mutex_lock(&barrier->mutex); + ++(barrier->count); + if(barrier->count >= barrier->trip_count) + { + barrier->count = 0; + pthread_cond_broadcast(&barrier->cond); + pthread_mutex_unlock(&barrier->mutex); + return 1; + } + else + { + pthread_cond_wait(&barrier->cond, &(barrier->mutex)); + pthread_mutex_unlock(&barrier->mutex); + return 0; + } +} + +static int return_data(hid_device *dev, unsigned char *data, size_t length); + +/* Linked List of input reports received from the device. */ +struct input_report { + uint8_t *data; + size_t len; + struct input_report *next; +}; + +struct hid_device_ { + IOHIDDeviceRef device_handle; + int blocking; + int uses_numbered_reports; + int disconnected; + CFStringRef run_loop_mode; + CFRunLoopRef run_loop; + CFRunLoopSourceRef source; + uint8_t *input_report_buf; + CFIndex max_input_report_len; + struct input_report *input_reports; + + pthread_t thread; + pthread_mutex_t mutex; /* Protects input_reports */ + pthread_cond_t condition; + pthread_barrier_t barrier; /* Ensures correct startup sequence */ + pthread_barrier_t shutdown_barrier; /* Ensures correct shutdown sequence */ + int shutdown_thread; +}; + +static hid_device *new_hid_device(void) +{ + hid_device *dev = calloc(1, sizeof(hid_device)); + dev->device_handle = NULL; + dev->blocking = 1; + dev->uses_numbered_reports = 0; + dev->disconnected = 0; + dev->run_loop_mode = NULL; + dev->run_loop = NULL; + dev->source = NULL; + dev->input_report_buf = NULL; + dev->input_reports = NULL; + dev->shutdown_thread = 0; + + /* Thread objects */ + pthread_mutex_init(&dev->mutex, NULL); + pthread_cond_init(&dev->condition, NULL); + pthread_barrier_init(&dev->barrier, NULL, 2); + pthread_barrier_init(&dev->shutdown_barrier, NULL, 2); + + return dev; +} + +static void free_hid_device(hid_device *dev) +{ + if (!dev) + return; + + /* Delete any input reports still left over. */ + struct input_report *rpt = dev->input_reports; + while (rpt) { + struct input_report *next = rpt->next; + free(rpt->data); + free(rpt); + rpt = next; + } + + /* Free the string and the report buffer. The check for NULL + is necessary here as CFRelease() doesn't handle NULL like + free() and others do. */ + if (dev->run_loop_mode) + CFRelease(dev->run_loop_mode); + if (dev->source) + CFRelease(dev->source); + free(dev->input_report_buf); + + /* Clean up the thread objects */ + pthread_barrier_destroy(&dev->shutdown_barrier); + pthread_barrier_destroy(&dev->barrier); + pthread_cond_destroy(&dev->condition); + pthread_mutex_destroy(&dev->mutex); + + /* Free the structure itself. */ + free(dev); +} + +static IOHIDManagerRef hid_mgr = 0x0; + + +#if 0 +static void register_error(hid_device *dev, const char *op) +{ + +} +#endif + + +static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key) +{ + CFTypeRef ref; + int32_t value; + + ref = IOHIDDeviceGetProperty(device, key); + if (ref) { + if (CFGetTypeID(ref) == CFNumberGetTypeID()) { + CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &value); + return value; + } + } + return 0; +} + +static unsigned short get_vendor_id(IOHIDDeviceRef device) +{ + return get_int_property(device, CFSTR(kIOHIDVendorIDKey)); +} + +static unsigned short get_product_id(IOHIDDeviceRef device) +{ + return get_int_property(device, CFSTR(kIOHIDProductIDKey)); +} + +static int32_t get_max_report_length(IOHIDDeviceRef device) +{ + return get_int_property(device, CFSTR(kIOHIDMaxInputReportSizeKey)); +} + +static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, wchar_t *buf, size_t len) +{ + CFStringRef str; + + if (!len) + return 0; + + str = IOHIDDeviceGetProperty(device, prop); + + buf[0] = 0; + + if (str) { + CFIndex str_len = CFStringGetLength(str); + CFRange range; + CFIndex used_buf_len; + CFIndex chars_copied; + + len --; + + range.location = 0; + range.length = ((size_t)str_len > len)? len: (size_t)str_len; + chars_copied = CFStringGetBytes(str, + range, + kCFStringEncodingUTF32LE, + (char)'?', + FALSE, + (UInt8*)buf, + len * sizeof(wchar_t), + &used_buf_len); + + if (chars_copied == len) + buf[len] = 0; /* len is decremented above */ + else + buf[chars_copied] = 0; + + return 0; + } + else + return -1; + +} + +static int get_serial_number(IOHIDDeviceRef device, wchar_t *buf, size_t len) +{ + return get_string_property(device, CFSTR(kIOHIDSerialNumberKey), buf, len); +} + +static int get_manufacturer_string(IOHIDDeviceRef device, wchar_t *buf, size_t len) +{ + return get_string_property(device, CFSTR(kIOHIDManufacturerKey), buf, len); +} + +static int get_product_string(IOHIDDeviceRef device, wchar_t *buf, size_t len) +{ + return get_string_property(device, CFSTR(kIOHIDProductKey), buf, len); +} + + +/* Implementation of wcsdup() for Mac. */ +static wchar_t *dup_wcs(const wchar_t *s) +{ + size_t len = wcslen(s); + wchar_t *ret = malloc((len+1)*sizeof(wchar_t)); + wcscpy(ret, s); + + return ret; +} + +/* hidapi_IOHIDDeviceGetService() + * + * Return the io_service_t corresponding to a given IOHIDDeviceRef, either by: + * - on OS X 10.6 and above, calling IOHIDDeviceGetService() + * - on OS X 10.5, extract it from the IOHIDDevice struct + */ +static io_service_t hidapi_IOHIDDeviceGetService(IOHIDDeviceRef device) +{ + static void *iokit_framework = NULL; + static io_service_t (*dynamic_IOHIDDeviceGetService)(IOHIDDeviceRef device) = NULL; + + /* Use dlopen()/dlsym() to get a pointer to IOHIDDeviceGetService() if it exists. + * If any of these steps fail, dynamic_IOHIDDeviceGetService will be left NULL + * and the fallback method will be used. + */ + if (iokit_framework == NULL) { + iokit_framework = dlopen("/System/Library/IOKit.framework/IOKit", RTLD_LAZY); + + if (iokit_framework != NULL) + dynamic_IOHIDDeviceGetService = dlsym(iokit_framework, "IOHIDDeviceGetService"); + } + + if (dynamic_IOHIDDeviceGetService != NULL) { + /* Running on OS X 10.6 and above: IOHIDDeviceGetService() exists */ + return dynamic_IOHIDDeviceGetService(device); + } + else + { + /* Running on OS X 10.5: IOHIDDeviceGetService() doesn't exist. + * + * Be naughty and pull the service out of the IOHIDDevice. + * IOHIDDevice is an opaque struct not exposed to applications, but its + * layout is stable through all available versions of OS X. + * Tested and working on OS X 10.5.8 i386, x86_64, and ppc. + */ + struct IOHIDDevice_internal { + /* The first field of the IOHIDDevice struct is a + * CFRuntimeBase (which is a private CF struct). + * + * a, b, and c are the 3 fields that make up a CFRuntimeBase. + * See http://opensource.apple.com/source/CF/CF-476.18/CFRuntime.h + * + * The second field of the IOHIDDevice is the io_service_t we're looking for. + */ + uintptr_t a; + uint8_t b[4]; +#if __LP64__ + uint32_t c; +#endif + io_service_t service; + }; + struct IOHIDDevice_internal *tmp = (struct IOHIDDevice_internal *)device; + + return tmp->service; + } +} + +/* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */ +static int init_hid_manager(void) +{ + /* Initialize all the HID Manager Objects */ + hid_mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + if (hid_mgr) { + IOHIDManagerSetDeviceMatching(hid_mgr, NULL); + IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + return 0; + } + + return -1; +} + +/* Initialize the IOHIDManager if necessary. This is the public function, and + it is safe to call this function repeatedly. Return 0 for success and -1 + for failure. */ +int HID_API_EXPORT hid_init(void) +{ + if (!hid_mgr) { + return init_hid_manager(); + } + + /* Already initialized. */ + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ + if (hid_mgr) { + /* Close the HID manager. */ + IOHIDManagerClose(hid_mgr, kIOHIDOptionsTypeNone); + CFRelease(hid_mgr); + hid_mgr = NULL; + } + + return 0; +} + +static void process_pending_events(void) { + SInt32 res; + do { + res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, FALSE); + } while(res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut); +} + +struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + struct hid_device_info *root = NULL; /* return object */ + struct hid_device_info *cur_dev = NULL; + CFIndex num_devices; + int i; + + /* Set up the HID Manager if it hasn't been done */ + if (hid_init() < 0) + return NULL; + + /* give the IOHIDManager a chance to update itself */ + process_pending_events(); + + /* Get a list of the Devices */ + IOHIDManagerSetDeviceMatching(hid_mgr, NULL); + CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr); + + /* Convert the list into a C array so we can iterate easily. */ + num_devices = CFSetGetCount(device_set); + IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); + CFSetGetValues(device_set, (const void **) device_array); + + /* Iterate over each device, making an entry for it. */ + for (i = 0; i < num_devices; i++) { + unsigned short dev_vid; + unsigned short dev_pid; + #define BUF_LEN 256 + wchar_t buf[BUF_LEN]; + + IOHIDDeviceRef dev = device_array[i]; + + if (!dev) { + continue; + } + dev_vid = get_vendor_id(dev); + dev_pid = get_product_id(dev); + + /* Check the VID/PID against the arguments */ + if ((vendor_id == 0x0 || vendor_id == dev_vid) && + (product_id == 0x0 || product_id == dev_pid)) { + struct hid_device_info *tmp; + bool is_usb_hid; /* Is this an actual HID usb device */ + io_object_t iokit_dev; + kern_return_t res; + io_string_t path; + + /* VID/PID match. Create the record. */ + tmp = malloc(sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + cur_dev = tmp; + + is_usb_hid = get_int_property(dev, CFSTR(kUSBInterfaceClass)) == kUSBHIDClass; + + /* Get the Usage Page and Usage for this device. */ + cur_dev->usage_page = get_int_property(dev, CFSTR(kIOHIDPrimaryUsagePageKey)); + cur_dev->usage = get_int_property(dev, CFSTR(kIOHIDPrimaryUsageKey)); + + /* Fill out the record */ + cur_dev->next = NULL; + + /* Fill in the path (IOService plane) */ + iokit_dev = hidapi_IOHIDDeviceGetService(dev); + res = IORegistryEntryGetPath(iokit_dev, kIOServicePlane, path); + if (res == KERN_SUCCESS) + cur_dev->path = strdup(path); + else + cur_dev->path = strdup(""); + + /* Serial Number */ + get_serial_number(dev, buf, BUF_LEN); + cur_dev->serial_number = dup_wcs(buf); + + /* Manufacturer and Product strings */ + get_manufacturer_string(dev, buf, BUF_LEN); + cur_dev->manufacturer_string = dup_wcs(buf); + get_product_string(dev, buf, BUF_LEN); + cur_dev->product_string = dup_wcs(buf); + + /* VID/PID */ + cur_dev->vendor_id = dev_vid; + cur_dev->product_id = dev_pid; + + /* Release Number */ + cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey)); + + /* We can only retrieve the interface number for USB HID devices. + * IOKit always seems to return 0 when querying a standard USB device + * for its interface. */ + if (is_usb_hid) { + /* Get the interface number */ + cur_dev->interface_number = get_int_property(dev, CFSTR(kUSBInterfaceNumber)); + } else { + cur_dev->interface_number = -1; + } + } + } + + free(device_array); + CFRelease(device_set); + + return root; +} + +void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) +{ + /* This function is identical to the Linux version. Platform independent. */ + struct hid_device_info *d = devs; + while (d) { + struct hid_device_info *next = d->next; + free(d->path); + free(d->serial_number); + free(d->manufacturer_string); + free(d->product_string); + free(d); + d = next; + } +} + +hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) +{ + /* This function is identical to the Linux version. Platform independent. */ + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device * handle = NULL; + + devs = hid_enumerate(vendor_id, product_id); + cur_dev = devs; + while (cur_dev) { + if (cur_dev->vendor_id == vendor_id && + cur_dev->product_id == product_id) { + if (serial_number) { + if (wcscmp(serial_number, cur_dev->serial_number) == 0) { + path_to_open = cur_dev->path; + break; + } + } + else { + path_to_open = cur_dev->path; + break; + } + } + cur_dev = cur_dev->next; + } + + if (path_to_open) { + /* Open the device */ + handle = hid_open_path(path_to_open); + } + + hid_free_enumeration(devs); + + return handle; +} + +static void hid_device_removal_callback(void *context, IOReturn result, + void *sender) +{ + /* Stop the Run Loop for this device. */ + hid_device *d = context; + + d->disconnected = 1; + CFRunLoopStop(d->run_loop); +} + +/* The Run Loop calls this function for each input report received. + This function puts the data into a linked list to be picked up by + hid_read(). */ +static void hid_report_callback(void *context, IOReturn result, void *sender, + IOHIDReportType report_type, uint32_t report_id, + uint8_t *report, CFIndex report_length) +{ + struct input_report *rpt; + hid_device *dev = context; + + /* Make a new Input Report object */ + rpt = calloc(1, sizeof(struct input_report)); + rpt->data = calloc(1, report_length); + memcpy(rpt->data, report, report_length); + rpt->len = report_length; + rpt->next = NULL; + + /* Lock this section */ + pthread_mutex_lock(&dev->mutex); + + /* Attach the new report object to the end of the list. */ + if (dev->input_reports == NULL) { + /* The list is empty. Put it at the root. */ + dev->input_reports = rpt; + } + else { + /* Find the end of the list and attach. */ + struct input_report *cur = dev->input_reports; + int num_queued = 0; + while (cur->next != NULL) { + cur = cur->next; + num_queued++; + } + cur->next = rpt; + + /* Pop one off if we've reached 30 in the queue. This + way we don't grow forever if the user never reads + anything from the device. */ + if (num_queued > 30) { + return_data(dev, NULL, 0); + } + } + + /* Signal a waiting thread that there is data. */ + pthread_cond_signal(&dev->condition); + + /* Unlock */ + pthread_mutex_unlock(&dev->mutex); + +} + +/* This gets called when the read_thread's run loop gets signaled by + hid_close(), and serves to stop the read_thread's run loop. */ +static void perform_signal_callback(void *context) +{ + hid_device *dev = context; + CFRunLoopStop(dev->run_loop); /*TODO: CFRunLoopGetCurrent()*/ +} + +static void *read_thread(void *param) +{ + hid_device *dev = param; + SInt32 code; + + /* Move the device's run loop to this thread. */ + IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetCurrent(), dev->run_loop_mode); + + /* Create the RunLoopSource which is used to signal the + event loop to stop when hid_close() is called. */ + CFRunLoopSourceContext ctx; + memset(&ctx, 0, sizeof(ctx)); + ctx.version = 0; + ctx.info = dev; + ctx.perform = &perform_signal_callback; + dev->source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0/*order*/, &ctx); + CFRunLoopAddSource(CFRunLoopGetCurrent(), dev->source, dev->run_loop_mode); + + /* Store off the Run Loop so it can be stopped from hid_close() + and on device disconnection. */ + dev->run_loop = CFRunLoopGetCurrent(); + + /* Notify the main thread that the read thread is up and running. */ + pthread_barrier_wait(&dev->barrier); + + /* Run the Event Loop. CFRunLoopRunInMode() will dispatch HID input + reports into the hid_report_callback(). */ + while (!dev->shutdown_thread && !dev->disconnected) { + code = CFRunLoopRunInMode(dev->run_loop_mode, 1000/*sec*/, FALSE); + /* Return if the device has been disconnected */ + if (code == kCFRunLoopRunFinished) { + dev->disconnected = 1; + break; + } + + + /* Break if The Run Loop returns Finished or Stopped. */ + if (code != kCFRunLoopRunTimedOut && + code != kCFRunLoopRunHandledSource) { + /* There was some kind of error. Setting + shutdown seems to make sense, but + there may be something else more appropriate */ + dev->shutdown_thread = 1; + break; + } + } + + /* Now that the read thread is stopping, Wake any threads which are + waiting on data (in hid_read_timeout()). Do this under a mutex to + make sure that a thread which is about to go to sleep waiting on + the condition actually will go to sleep before the condition is + signaled. */ + pthread_mutex_lock(&dev->mutex); + pthread_cond_broadcast(&dev->condition); + pthread_mutex_unlock(&dev->mutex); + + /* Wait here until hid_close() is called and makes it past + the call to CFRunLoopWakeUp(). This thread still needs to + be valid when that function is called on the other thread. */ + pthread_barrier_wait(&dev->shutdown_barrier); + + return NULL; +} + +/* hid_open_path() + * + * path must be a valid path to an IOHIDDevice in the IOService plane + * Example: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver" + */ +hid_device * HID_API_EXPORT hid_open_path(const char *path) +{ + hid_device *dev = NULL; + io_registry_entry_t entry = MACH_PORT_NULL; + + dev = new_hid_device(); + + /* Set up the HID Manager if it hasn't been done */ + if (hid_init() < 0) + return NULL; + + /* Get the IORegistry entry for the given path */ + entry = IORegistryEntryFromPath(kIOMasterPortDefault, path); + if (entry == MACH_PORT_NULL) { + /* Path wasn't valid (maybe device was removed?) */ + goto return_error; + } + + /* Create an IOHIDDevice for the entry */ + dev->device_handle = IOHIDDeviceCreate(kCFAllocatorDefault, entry); + if (dev->device_handle == NULL) { + /* Error creating the HID device */ + goto return_error; + } + + /* Open the IOHIDDevice */ + IOReturn ret = IOHIDDeviceOpen(dev->device_handle, kIOHIDOptionsTypeSeizeDevice); + if (ret == kIOReturnSuccess) { + char str[32]; + + /* Create the buffers for receiving data */ + dev->max_input_report_len = (CFIndex) get_max_report_length(dev->device_handle); + dev->input_report_buf = calloc(dev->max_input_report_len, sizeof(uint8_t)); + + /* Create the Run Loop Mode for this device. + printing the reference seems to work. */ + sprintf(str, "HIDAPI_%p", dev->device_handle); + dev->run_loop_mode = + CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII); + + /* Attach the device to a Run Loop */ + IOHIDDeviceRegisterInputReportCallback( + dev->device_handle, dev->input_report_buf, dev->max_input_report_len, + &hid_report_callback, dev); + IOHIDDeviceRegisterRemovalCallback(dev->device_handle, hid_device_removal_callback, dev); + + /* Start the read thread */ + pthread_create(&dev->thread, NULL, read_thread, dev); + + /* Wait here for the read thread to be initialized. */ + pthread_barrier_wait(&dev->barrier); + + IOObjectRelease(entry); + return dev; + } + else { + goto return_error; + } + +return_error: + if (dev->device_handle != NULL) + CFRelease(dev->device_handle); + + if (entry != MACH_PORT_NULL) + IOObjectRelease(entry); + + free_hid_device(dev); + return NULL; +} + +static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char *data, size_t length) +{ + const unsigned char *data_to_send; + size_t length_to_send; + IOReturn res; + + /* Return if the device has been disconnected. */ + if (dev->disconnected) + return -1; + + if (data[0] == 0x0) { + /* Not using numbered Reports. + Don't send the report number. */ + data_to_send = data+1; + length_to_send = length-1; + } + else { + /* Using numbered Reports. + Send the Report Number */ + data_to_send = data; + length_to_send = length; + } + + if (!dev->disconnected) { + res = IOHIDDeviceSetReport(dev->device_handle, + type, + data[0], /* Report ID*/ + data_to_send, length_to_send); + + if (res == kIOReturnSuccess) { + return length; + } + else + return -1; + } + + return -1; +} + +int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + return set_report(dev, kIOHIDReportTypeOutput, data, length); +} + +/* Helper function, so that this isn't duplicated in hid_read(). */ +static int return_data(hid_device *dev, unsigned char *data, size_t length) +{ + /* Copy the data out of the linked list item (rpt) into the + return buffer (data), and delete the liked list item. */ + struct input_report *rpt = dev->input_reports; + size_t len = (length < rpt->len)? length: rpt->len; + memcpy(data, rpt->data, len); + dev->input_reports = rpt->next; + free(rpt->data); + free(rpt); + return len; +} + +static int cond_wait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + while (!dev->input_reports) { + int res = pthread_cond_wait(cond, mutex); + if (res != 0) + return res; + + /* A res of 0 means we may have been signaled or it may + be a spurious wakeup. Check to see that there's acutally + data in the queue before returning, and if not, go back + to sleep. See the pthread_cond_timedwait() man page for + details. */ + + if (dev->shutdown_thread || dev->disconnected) + return -1; + } + + return 0; +} + +static int cond_timedwait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) +{ + while (!dev->input_reports) { + int res = pthread_cond_timedwait(cond, mutex, abstime); + if (res != 0) + return res; + + /* A res of 0 means we may have been signaled or it may + be a spurious wakeup. Check to see that there's acutally + data in the queue before returning, and if not, go back + to sleep. See the pthread_cond_timedwait() man page for + details. */ + + if (dev->shutdown_thread || dev->disconnected) + return -1; + } + + return 0; + +} + +int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + int bytes_read = -1; + + /* Lock the access to the report list. */ + pthread_mutex_lock(&dev->mutex); + + /* There's an input report queued up. Return it. */ + if (dev->input_reports) { + /* Return the first one */ + bytes_read = return_data(dev, data, length); + goto ret; + } + + /* Return if the device has been disconnected. */ + if (dev->disconnected) { + bytes_read = -1; + goto ret; + } + + if (dev->shutdown_thread) { + /* This means the device has been closed (or there + has been an error. An error code of -1 should + be returned. */ + bytes_read = -1; + goto ret; + } + + /* There is no data. Go to sleep and wait for data. */ + + if (milliseconds == -1) { + /* Blocking */ + int res; + res = cond_wait(dev, &dev->condition, &dev->mutex); + if (res == 0) + bytes_read = return_data(dev, data, length); + else { + /* There was an error, or a device disconnection. */ + bytes_read = -1; + } + } + else if (milliseconds > 0) { + /* Non-blocking, but called with timeout. */ + int res; + struct timespec ts; + struct timeval tv; + gettimeofday(&tv, NULL); + TIMEVAL_TO_TIMESPEC(&tv, &ts); + ts.tv_sec += milliseconds / 1000; + ts.tv_nsec += (milliseconds % 1000) * 1000000; + if (ts.tv_nsec >= 1000000000L) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000L; + } + + res = cond_timedwait(dev, &dev->condition, &dev->mutex, &ts); + if (res == 0) + bytes_read = return_data(dev, data, length); + else if (res == ETIMEDOUT) + bytes_read = 0; + else + bytes_read = -1; + } + else { + /* Purely non-blocking */ + bytes_read = 0; + } + +ret: + /* Unlock */ + pthread_mutex_unlock(&dev->mutex); + return bytes_read; +} + +int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) +{ + return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); +} + +int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) +{ + /* All Nonblocking operation is handled by the library. */ + dev->blocking = !nonblock; + + return 0; +} + +int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + return set_report(dev, kIOHIDReportTypeFeature, data, length); +} + +int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + CFIndex len = length - 1; + IOReturn res; + + /* Return if the device has been unplugged. */ + if (dev->disconnected) + return -1; + + res = IOHIDDeviceGetReport(dev->device_handle, + kIOHIDReportTypeFeature, + data[0], /* Report ID */ + data + 1, &len); + if (res == kIOReturnSuccess) + return len + 1; + else + return -1; +} + + +void HID_API_EXPORT hid_close(hid_device *dev) +{ + if (!dev) + return; + + /* Disconnect the report callback before close. */ + if (!dev->disconnected) { + IOHIDDeviceRegisterInputReportCallback( + dev->device_handle, dev->input_report_buf, dev->max_input_report_len, + NULL, dev); + IOHIDDeviceRegisterRemovalCallback(dev->device_handle, NULL, dev); + IOHIDDeviceUnscheduleFromRunLoop(dev->device_handle, dev->run_loop, dev->run_loop_mode); + IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode); + } + + /* Cause read_thread() to stop. */ + dev->shutdown_thread = 1; + + /* Wake up the run thread's event loop so that the thread can exit. */ + CFRunLoopSourceSignal(dev->source); + CFRunLoopWakeUp(dev->run_loop); + + /* Notify the read thread that it can shut down now. */ + pthread_barrier_wait(&dev->shutdown_barrier); + + /* Wait for read_thread() to end. */ + pthread_join(dev->thread, NULL); + + /* Close the OS handle to the device, but only if it's not + been unplugged. If it's been unplugged, then calling + IOHIDDeviceClose() will crash. */ + if (!dev->disconnected) { + IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeSeizeDevice); + } + + /* Clear out the queue of received reports. */ + pthread_mutex_lock(&dev->mutex); + while (dev->input_reports) { + return_data(dev, NULL, 0); + } + pthread_mutex_unlock(&dev->mutex); + CFRelease(dev->device_handle); + + free_hid_device(dev); +} + +int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_manufacturer_string(dev->device_handle, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_product_string(dev->device_handle, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_serial_number(dev->device_handle, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + /* TODO: */ + + return 0; +} + + +HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) +{ + /* TODO: */ + + return NULL; +} + + + + + + + +#if 0 +static int32_t get_location_id(IOHIDDeviceRef device) +{ + return get_int_property(device, CFSTR(kIOHIDLocationIDKey)); +} + +static int32_t get_usage(IOHIDDeviceRef device) +{ + int32_t res; + res = get_int_property(device, CFSTR(kIOHIDDeviceUsageKey)); + if (!res) + res = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey)); + return res; +} + +static int32_t get_usage_page(IOHIDDeviceRef device) +{ + int32_t res; + res = get_int_property(device, CFSTR(kIOHIDDeviceUsagePageKey)); + if (!res) + res = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey)); + return res; +} + +static int get_transport(IOHIDDeviceRef device, wchar_t *buf, size_t len) +{ + return get_string_property(device, CFSTR(kIOHIDTransportKey), buf, len); +} + + +int main(void) +{ + IOHIDManagerRef mgr; + int i; + + mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + IOHIDManagerSetDeviceMatching(mgr, NULL); + IOHIDManagerOpen(mgr, kIOHIDOptionsTypeNone); + + CFSetRef device_set = IOHIDManagerCopyDevices(mgr); + + CFIndex num_devices = CFSetGetCount(device_set); + IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); + CFSetGetValues(device_set, (const void **) device_array); + + for (i = 0; i < num_devices; i++) { + IOHIDDeviceRef dev = device_array[i]; + printf("Device: %p\n", dev); + printf(" %04hx %04hx\n", get_vendor_id(dev), get_product_id(dev)); + + wchar_t serial[256], buf[256]; + char cbuf[256]; + get_serial_number(dev, serial, 256); + + + printf(" Serial: %ls\n", serial); + printf(" Loc: %ld\n", get_location_id(dev)); + get_transport(dev, buf, 256); + printf(" Trans: %ls\n", buf); + make_path(dev, cbuf, 256); + printf(" Path: %s\n", cbuf); + + } + + return 0; +} +#endif diff --git a/src/hidapi/win/hid.c b/src/hidapi/win/hid.c new file mode 100644 index 000000000..4a71e2552 --- /dev/null +++ b/src/hidapi/win/hid.c @@ -0,0 +1,956 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +#include + +#ifndef _NTDEF_ +typedef LONG NTSTATUS; +#endif + +#ifdef __MINGW32__ +#include +#include +#endif + +#ifdef __CYGWIN__ +#include +#define _wcsdup wcsdup +#endif + +/* The maximum number of characters that can be passed into the + HidD_Get*String() functions without it failing.*/ +#define MAX_STRING_WCHARS 0xFFF + +/*#define HIDAPI_USE_DDK*/ + +#ifdef __cplusplus +extern "C" { +#endif + #include + #include + #ifdef HIDAPI_USE_DDK + #include + #endif + + /* Copied from inc/ddk/hidclass.h, part of the Windows DDK. */ + #define HID_OUT_CTL_CODE(id) \ + CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS) + #define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#include +#include + + +#include "hidapi.h" + +#undef MIN +#define MIN(x,y) ((x) < (y)? (x): (y)) + +#ifdef _MSC_VER + /* Thanks Microsoft, but I know how to use strncpy(). */ + #pragma warning(disable:4996) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef HIDAPI_USE_DDK + /* Since we're not building with the DDK, and the HID header + files aren't part of the SDK, we have to define all this + stuff here. In lookup_functions(), the function pointers + defined below are set. */ + typedef struct _HIDD_ATTRIBUTES{ + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; + } HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; + + typedef USHORT USAGE; + typedef struct _HIDP_CAPS { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + USHORT fields_not_used_by_hidapi[10]; + } HIDP_CAPS, *PHIDP_CAPS; + typedef void* PHIDP_PREPARSED_DATA; + #define HIDP_STATUS_SUCCESS 0x110000 + + typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib); + typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length); + typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length); + typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data); + typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data); + typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, HIDP_CAPS *caps); + typedef BOOLEAN (__stdcall *HidD_SetNumInputBuffers_)(HANDLE handle, ULONG number_buffers); + + static HidD_GetAttributes_ HidD_GetAttributes; + static HidD_GetSerialNumberString_ HidD_GetSerialNumberString; + static HidD_GetManufacturerString_ HidD_GetManufacturerString; + static HidD_GetProductString_ HidD_GetProductString; + static HidD_SetFeature_ HidD_SetFeature; + static HidD_GetFeature_ HidD_GetFeature; + static HidD_GetIndexedString_ HidD_GetIndexedString; + static HidD_GetPreparsedData_ HidD_GetPreparsedData; + static HidD_FreePreparsedData_ HidD_FreePreparsedData; + static HidP_GetCaps_ HidP_GetCaps; + static HidD_SetNumInputBuffers_ HidD_SetNumInputBuffers; + + static HMODULE lib_handle = NULL; + static BOOLEAN initialized = FALSE; +#endif /* HIDAPI_USE_DDK */ + +struct hid_device_ { + HANDLE device_handle; + BOOL blocking; + USHORT output_report_length; + size_t input_report_length; + void *last_error_str; + DWORD last_error_num; + BOOL read_pending; + char *read_buf; + OVERLAPPED ol; +}; + +static hid_device *new_hid_device() +{ + hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); + dev->device_handle = INVALID_HANDLE_VALUE; + dev->blocking = TRUE; + dev->output_report_length = 0; + dev->input_report_length = 0; + dev->last_error_str = NULL; + dev->last_error_num = 0; + dev->read_pending = FALSE; + dev->read_buf = NULL; + memset(&dev->ol, 0, sizeof(dev->ol)); + dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL); + + return dev; +} + +static void free_hid_device(hid_device *dev) +{ + CloseHandle(dev->ol.hEvent); + CloseHandle(dev->device_handle); + LocalFree(dev->last_error_str); + free(dev->read_buf); + free(dev); +} + +static void register_error(hid_device *dev, const char *op) +{ + WCHAR *ptr, *msg; + + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPVOID)&msg, 0/*sz*/, + NULL); + + /* Get rid of the CR and LF that FormatMessage() sticks at the + end of the message. Thanks Microsoft! */ + ptr = msg; + while (*ptr) { + if (*ptr == '\r') { + *ptr = 0x0000; + break; + } + ptr++; + } + + /* Store the message off in the Device entry so that + the hid_error() function can pick it up. */ + LocalFree(dev->last_error_str); + dev->last_error_str = msg; +} + +#ifndef HIDAPI_USE_DDK +static int lookup_functions() +{ + lib_handle = LoadLibraryA("hid.dll"); + if (lib_handle) { +#define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1; + RESOLVE(HidD_GetAttributes); + RESOLVE(HidD_GetSerialNumberString); + RESOLVE(HidD_GetManufacturerString); + RESOLVE(HidD_GetProductString); + RESOLVE(HidD_SetFeature); + RESOLVE(HidD_GetFeature); + RESOLVE(HidD_GetIndexedString); + RESOLVE(HidD_GetPreparsedData); + RESOLVE(HidD_FreePreparsedData); + RESOLVE(HidP_GetCaps); + RESOLVE(HidD_SetNumInputBuffers); +#undef RESOLVE + } + else + return -1; + + return 0; +} +#endif + +static HANDLE open_device(const char *path, BOOL open_rw) +{ + HANDLE handle; + DWORD desired_access = (open_rw)? (GENERIC_WRITE | GENERIC_READ): 0; + DWORD share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE; + + handle = CreateFileA(path, + desired_access, + share_mode, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/ + 0); + + return handle; +} + +int HID_API_EXPORT hid_init(void) +{ +#ifndef HIDAPI_USE_DDK + if (!initialized) { + if (lookup_functions() < 0) { + hid_exit(); + return -1; + } + initialized = TRUE; + } +#endif + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ +#ifndef HIDAPI_USE_DDK + if (lib_handle) + FreeLibrary(lib_handle); + lib_handle = NULL; + initialized = FALSE; +#endif + return 0; +} + +struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + BOOL res; + struct hid_device_info *root = NULL; /* return object */ + struct hid_device_info *cur_dev = NULL; + + /* Windows objects for interacting with the driver. */ + GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} }; + SP_DEVINFO_DATA devinfo_data; + SP_DEVICE_INTERFACE_DATA device_interface_data; + SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL; + HDEVINFO device_info_set = INVALID_HANDLE_VALUE; + int device_index = 0; + int i; + + if (hid_init() < 0) + return NULL; + + /* Initialize the Windows objects. */ + memset(&devinfo_data, 0x0, sizeof(devinfo_data)); + devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA); + device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + /* Get information for all the devices belonging to the HID class. */ + device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + /* Iterate over each device in the HID class, looking for the right one. */ + + for (;;) { + HANDLE write_handle = INVALID_HANDLE_VALUE; + DWORD required_size = 0; + HIDD_ATTRIBUTES attrib; + + res = SetupDiEnumDeviceInterfaces(device_info_set, + NULL, + &InterfaceClassGuid, + device_index, + &device_interface_data); + + if (!res) { + /* A return of FALSE from this function means that + there are no more devices. */ + break; + } + + /* Call with 0-sized detail size, and let the function + tell us how long the detail struct needs to be. The + size is put in &required_size. */ + res = SetupDiGetDeviceInterfaceDetailA(device_info_set, + &device_interface_data, + NULL, + 0, + &required_size, + NULL); + + /* Allocate a long enough structure for device_interface_detail_data. */ + device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size); + device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); + + /* Get the detailed data for this device. The detail data gives us + the device path for this device, which is then passed into + CreateFile() to get a handle to the device. */ + res = SetupDiGetDeviceInterfaceDetailA(device_info_set, + &device_interface_data, + device_interface_detail_data, + required_size, + NULL, + NULL); + + if (!res) { + /* register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail"); + Continue to the next device. */ + goto cont; + } + + /* Make sure this device is of Setup Class "HIDClass" and has a + driver bound to it. */ + for (i = 0; ; i++) { + char driver_name[256]; + + /* Populate devinfo_data. This function will return failure + when there are no more interfaces left. */ + res = SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data); + if (!res) + goto cont; + + res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, + SPDRP_CLASS, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); + if (!res) + goto cont; + + if ((strcmp(driver_name, "HIDClass") == 0) || + (strcmp(driver_name, "Mouse") == 0) || + (strcmp(driver_name, "Keyboard") == 0)) { + /* See if there's a driver bound. */ + res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, + SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); + if (res) + break; + } + } + + //wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath); + + /* Open a handle to the device */ + write_handle = open_device(device_interface_detail_data->DevicePath, FALSE); + + /* Check validity of write_handle. */ + if (write_handle == INVALID_HANDLE_VALUE) { + /* Unable to open the device. */ + //register_error(dev, "CreateFile"); + goto cont_close; + } + + + /* Get the Vendor ID and Product ID for this device. */ + attrib.Size = sizeof(HIDD_ATTRIBUTES); + HidD_GetAttributes(write_handle, &attrib); + //wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID); + + /* Check the VID/PID to see if we should add this + device to the enumeration list. */ + if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) && + (product_id == 0x0 || attrib.ProductID == product_id)) { + + #define WSTR_LEN 512 + const char *str; + struct hid_device_info *tmp; + PHIDP_PREPARSED_DATA pp_data = NULL; + HIDP_CAPS caps; + BOOLEAN res; + NTSTATUS nt_res; + wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */ + size_t len; + + /* VID/PID match. Create the record. */ + tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + cur_dev = tmp; + + /* Get the Usage Page and Usage for this device. */ + res = HidD_GetPreparsedData(write_handle, &pp_data); + if (res) { + nt_res = HidP_GetCaps(pp_data, &caps); + if (nt_res == HIDP_STATUS_SUCCESS) { + cur_dev->usage_page = caps.UsagePage; + cur_dev->usage = caps.Usage; + } + + HidD_FreePreparsedData(pp_data); + } + + /* Fill out the record */ + cur_dev->next = NULL; + str = device_interface_detail_data->DevicePath; + if (str) { + len = strlen(str); + cur_dev->path = (char*) calloc(len+1, sizeof(char)); + strncpy(cur_dev->path, str, len+1); + cur_dev->path[len] = '\0'; + } + else + cur_dev->path = NULL; + + /* Serial Number */ + res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->serial_number = _wcsdup(wstr); + } + + /* Manufacturer String */ + res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->manufacturer_string = _wcsdup(wstr); + } + + /* Product String */ + res = HidD_GetProductString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->product_string = _wcsdup(wstr); + } + + /* VID/PID */ + cur_dev->vendor_id = attrib.VendorID; + cur_dev->product_id = attrib.ProductID; + + /* Release Number */ + cur_dev->release_number = attrib.VersionNumber; + + /* Interface Number. It can sometimes be parsed out of the path + on Windows if a device has multiple interfaces. See + http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or + search for "Hardware IDs for HID Devices" at MSDN. If it's not + in the path, it's set to -1. */ + cur_dev->interface_number = -1; + if (cur_dev->path) { + char *interface_component = strstr(cur_dev->path, "&mi_"); + if (interface_component) { + char *hex_str = interface_component + 4; + char *endptr = NULL; + cur_dev->interface_number = strtol(hex_str, &endptr, 16); + if (endptr == hex_str) { + /* The parsing failed. Set interface_number to -1. */ + cur_dev->interface_number = -1; + } + } + } + } + +cont_close: + CloseHandle(write_handle); +cont: + /* We no longer need the detail data. It can be freed */ + free(device_interface_detail_data); + + device_index++; + + } + + /* Close the device information handle. */ + SetupDiDestroyDeviceInfoList(device_info_set); + + return root; + +} + +void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs) +{ + /* TODO: Merge this with the Linux version. This function is platform-independent. */ + struct hid_device_info *d = devs; + while (d) { + struct hid_device_info *next = d->next; + free(d->path); + free(d->serial_number); + free(d->manufacturer_string); + free(d->product_string); + free(d); + d = next; + } +} + + +HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) +{ + /* TODO: Merge this functions with the Linux version. This function should be platform independent. */ + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + devs = hid_enumerate(vendor_id, product_id); + cur_dev = devs; + while (cur_dev) { + if (cur_dev->vendor_id == vendor_id && + cur_dev->product_id == product_id) { + if (serial_number) { + if (wcscmp(serial_number, cur_dev->serial_number) == 0) { + path_to_open = cur_dev->path; + break; + } + } + else { + path_to_open = cur_dev->path; + break; + } + } + cur_dev = cur_dev->next; + } + + if (path_to_open) { + /* Open the device */ + handle = hid_open_path(path_to_open); + } + + hid_free_enumeration(devs); + + return handle; +} + +HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) +{ + hid_device *dev; + HIDP_CAPS caps; + PHIDP_PREPARSED_DATA pp_data = NULL; + BOOLEAN res; + NTSTATUS nt_res; + + if (hid_init() < 0) { + return NULL; + } + + dev = new_hid_device(); + + /* Open a handle to the device */ + dev->device_handle = open_device(path, TRUE); + + /* Check validity of write_handle. */ + if (dev->device_handle == INVALID_HANDLE_VALUE) { + /* System devices, such as keyboards and mice, cannot be opened in + read-write mode, because the system takes exclusive control over + them. This is to prevent keyloggers. However, feature reports + can still be sent and received. Retry opening the device, but + without read/write access. */ + dev->device_handle = open_device(path, FALSE); + + /* Check the validity of the limited device_handle. */ + if (dev->device_handle == INVALID_HANDLE_VALUE) { + /* Unable to open the device, even without read-write mode. */ + register_error(dev, "CreateFile"); + goto err; + } + } + + /* Set the Input Report buffer size to 64 reports. */ + res = HidD_SetNumInputBuffers(dev->device_handle, 64); + if (!res) { + register_error(dev, "HidD_SetNumInputBuffers"); + goto err; + } + + /* Get the Input Report length for the device. */ + res = HidD_GetPreparsedData(dev->device_handle, &pp_data); + if (!res) { + register_error(dev, "HidD_GetPreparsedData"); + goto err; + } + nt_res = HidP_GetCaps(pp_data, &caps); + if (nt_res != HIDP_STATUS_SUCCESS) { + register_error(dev, "HidP_GetCaps"); + goto err_pp_data; + } + dev->output_report_length = caps.OutputReportByteLength; + dev->input_report_length = caps.InputReportByteLength; + HidD_FreePreparsedData(pp_data); + + dev->read_buf = (char*) malloc(dev->input_report_length); + + return dev; + +err_pp_data: + HidD_FreePreparsedData(pp_data); +err: + free_hid_device(dev); + return NULL; +} + +int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + DWORD bytes_written; + BOOL res; + + OVERLAPPED ol; + unsigned char *buf; + memset(&ol, 0, sizeof(ol)); + + /* Make sure the right number of bytes are passed to WriteFile. Windows + expects the number of bytes which are in the _longest_ report (plus + one for the report number) bytes even if the data is a report + which is shorter than that. Windows gives us this value in + caps.OutputReportByteLength. If a user passes in fewer bytes than this, + create a temporary buffer which is the proper size. */ + if (length >= dev->output_report_length) { + /* The user passed the right number of bytes. Use the buffer as-is. */ + buf = (unsigned char *) data; + } else { + /* Create a temporary buffer and copy the user's data + into it, padding the rest with zeros. */ + buf = (unsigned char *) malloc(dev->output_report_length); + memcpy(buf, data, length); + memset(buf + length, 0, dev->output_report_length - length); + length = dev->output_report_length; + } + + res = WriteFile(dev->device_handle, buf, length, NULL, &ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + /* WriteFile() failed. Return error. */ + register_error(dev, "WriteFile"); + bytes_written = -1; + goto end_of_function; + } + } + + /* Wait here until the write is done. This makes + hid_write() synchronous. */ + res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/); + if (!res) { + /* The Write operation failed. */ + register_error(dev, "WriteFile"); + bytes_written = -1; + goto end_of_function; + } + +end_of_function: + if (buf != data) + free(buf); + + return bytes_written; +} + + +int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + DWORD bytes_read = 0; + size_t copy_len = 0; + BOOL res; + + /* Copy the handle for convenience. */ + HANDLE ev = dev->ol.hEvent; + + if (!dev->read_pending) { + /* Start an Overlapped I/O read. */ + dev->read_pending = TRUE; + memset(dev->read_buf, 0, dev->input_report_length); + ResetEvent(ev); + res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + /* ReadFile() has failed. + Clean up and return error. */ + CancelIo(dev->device_handle); + dev->read_pending = FALSE; + goto end_of_function; + } + } + } + + if (milliseconds >= 0) { + /* See if there is any data yet. */ + res = WaitForSingleObject(ev, milliseconds); + if (res != WAIT_OBJECT_0) { + /* There was no data this time. Return zero bytes available, + but leave the Overlapped I/O running. */ + return 0; + } + } + + /* Either WaitForSingleObject() told us that ReadFile has completed, or + we are in non-blocking mode. Get the number of bytes read. The actual + data has been copied to the data[] array which was passed to ReadFile(). */ + res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/); + + /* Set pending back to false, even if GetOverlappedResult() returned error. */ + dev->read_pending = FALSE; + + if (res && bytes_read > 0) { + if (dev->read_buf[0] == 0x0) { + /* If report numbers aren't being used, but Windows sticks a report + number (0x0) on the beginning of the report anyway. To make this + work like the other platforms, and to make it work more like the + HID spec, we'll skip over this byte. */ + bytes_read--; + copy_len = length > bytes_read ? bytes_read : length; + memcpy(data, dev->read_buf+1, copy_len); + } + else { + /* Copy the whole buffer, report number and all. */ + copy_len = length > bytes_read ? bytes_read : length; + memcpy(data, dev->read_buf, copy_len); + } + } + +end_of_function: + if (!res) { + register_error(dev, "GetOverlappedResult"); + return -1; + } + + return copy_len; +} + +int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length) +{ + return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); +} + +int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock) +{ + dev->blocking = !nonblock; + return 0; /* Success */ +} + +int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, length); + if (!res) { + register_error(dev, "HidD_SetFeature"); + return -1; + } + + return length; +} + + +int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + BOOL res; +#if 0 + res = HidD_GetFeature(dev->device_handle, data, length); + if (!res) { + register_error(dev, "HidD_GetFeature"); + return -1; + } + return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */ +#else + DWORD bytes_returned; + + OVERLAPPED ol; + memset(&ol, 0, sizeof(ol)); + + res = DeviceIoControl(dev->device_handle, + IOCTL_HID_GET_FEATURE, + data, length, + data, length, + &bytes_returned, &ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + /* DeviceIoControl() failed. Return error. */ + register_error(dev, "Send Feature Report DeviceIoControl"); + return -1; + } + } + + /* Wait here until the write is done. This makes + hid_get_feature_report() synchronous. */ + res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/); + if (!res) { + /* The operation failed. */ + register_error(dev, "Send Feature Report GetOverLappedResult"); + return -1; + } + + /* bytes_returned does not include the first byte which contains the + report ID. The data buffer actually contains one more byte than + bytes_returned. */ + bytes_returned++; + + return bytes_returned; +#endif +} + +void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev) +{ + if (!dev) + return; + CancelIo(dev->device_handle); + free_hid_device(dev); +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); + if (!res) { + register_error(dev, "HidD_GetManufacturerString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); + if (!res) { + register_error(dev, "HidD_GetProductString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); + if (!res) { + register_error(dev, "HidD_GetSerialNumberString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); + if (!res) { + register_error(dev, "HidD_GetIndexedString"); + return -1; + } + + return 0; +} + + +HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) +{ + return (wchar_t*)dev->last_error_str; +} + + +/*#define PICPGM*/ +/*#define S11*/ +#define P32 +#ifdef S11 + unsigned short VendorID = 0xa0a0; + unsigned short ProductID = 0x0001; +#endif + +#ifdef P32 + unsigned short VendorID = 0x04d8; + unsigned short ProductID = 0x3f; +#endif + + +#ifdef PICPGM + unsigned short VendorID = 0x04d8; + unsigned short ProductID = 0x0033; +#endif + + +#if 0 +int __cdecl main(int argc, char* argv[]) +{ + int res; + unsigned char buf[65]; + + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + + /* Set up the command buffer. */ + memset(buf,0x00,sizeof(buf)); + buf[0] = 0; + buf[1] = 0x81; + + + /* Open the device. */ + int handle = open(VendorID, ProductID, L"12345"); + if (handle < 0) + printf("unable to open device\n"); + + + /* Toggle LED (cmd 0x80) */ + buf[1] = 0x80; + res = write(handle, buf, 65); + if (res < 0) + printf("Unable to write()\n"); + + /* Request state (cmd 0x81) */ + buf[1] = 0x81; + write(handle, buf, 65); + if (res < 0) + printf("Unable to write() (2)\n"); + + /* Read requested state */ + read(handle, buf, 65); + if (res < 0) + printf("Unable to read()\n"); + + /* Print out the returned buffer. */ + for (int i = 0; i < 4; i++) + printf("buf[%d]: %d\n", i, buf[i]); + + return 0; +} +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/src/libnest2d/CMakeLists.txt b/src/libnest2d/CMakeLists.txt index 587b814b2..6484da3d0 100644 --- a/src/libnest2d/CMakeLists.txt +++ b/src/libnest2d/CMakeLists.txt @@ -1,134 +1,31 @@ -cmake_minimum_required(VERSION 3.0) - -project(Libnest2D) - -if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) - # Update if necessary - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long ") -endif() - -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED) - -# Add our own cmake module path. -list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules/) - -option(LIBNEST2D_UNITTESTS "If enabled, googletest framework will be downloaded - and the provided unit tests will be included in the build." OFF) - -option(LIBNEST2D_BUILD_EXAMPLES "If enabled, examples will be built." OFF) - -option(LIBNEST2D_HEADER_ONLY "If enabled static library will not be built." ON) - -set(GEOMETRY_BACKENDS clipper boost eigen) -set(LIBNEST2D_GEOMETRIES clipper CACHE STRING "Geometry backend") -set_property(CACHE LIBNEST2D_GEOMETRIES PROPERTY STRINGS ${GEOMETRY_BACKENDS}) -list(FIND GEOMETRY_BACKENDS ${LIBNEST2D_GEOMETRIES} GEOMETRY_TYPE) -if(${GEOMETRY_TYPE} EQUAL -1) - message(FATAL_ERROR "Option ${LIBNEST2D_GEOMETRIES} not supported, valid entries are ${GEOMETRY_BACKENDS}") -endif() - -set(OPTIMIZERS nlopt optimlib) -set(LIBNEST2D_OPTIMIZER nlopt CACHE STRING "Optimization backend") -set_property(CACHE LIBNEST2D_OPTIMIZER PROPERTY STRINGS ${OPTIMIZERS}) -list(FIND OPTIMIZERS ${LIBNEST2D_OPTIMIZER} OPTIMIZER_TYPE) -if(${OPTIMIZER_TYPE} EQUAL -1) - message(FATAL_ERROR "Option ${LIBNEST2D_OPTIMIZER} not supported, valid entries are ${OPTIMIZERS}") -endif() - -add_library(libnest2d INTERFACE) - -set(SRC_DIR ${PROJECT_SOURCE_DIR}/include) - set(LIBNEST2D_SRCFILES - ${SRC_DIR}/libnest2d/libnest2d.hpp # Templates only - ${SRC_DIR}/libnest2d/geometry_traits.hpp - ${SRC_DIR}/libnest2d/geometry_traits_nfp.hpp - ${SRC_DIR}/libnest2d/common.hpp - ${SRC_DIR}/libnest2d/optimizer.hpp - ${SRC_DIR}/libnest2d/utils/metaloop.hpp - ${SRC_DIR}/libnest2d/utils/rotfinder.hpp - ${SRC_DIR}/libnest2d/utils/rotcalipers.hpp - ${SRC_DIR}/libnest2d/utils/bigint.hpp - ${SRC_DIR}/libnest2d/utils/rational.hpp - ${SRC_DIR}/libnest2d/placers/placer_boilerplate.hpp - ${SRC_DIR}/libnest2d/placers/bottomleftplacer.hpp - ${SRC_DIR}/libnest2d/placers/nfpplacer.hpp - ${SRC_DIR}/libnest2d/selections/selection_boilerplate.hpp - ${SRC_DIR}/libnest2d/selections/filler.hpp - ${SRC_DIR}/libnest2d/selections/firstfit.hpp - ${SRC_DIR}/libnest2d/selections/djd_heuristic.hpp + include/libnest2d/libnest2d.hpp + include/libnest2d/nester.hpp + include/libnest2d/geometry_traits.hpp + include/libnest2d/geometry_traits_nfp.hpp + include/libnest2d/common.hpp + include/libnest2d/optimizer.hpp + include/libnest2d/utils/metaloop.hpp + include/libnest2d/utils/rotfinder.hpp + include/libnest2d/utils/rotcalipers.hpp + include/libnest2d/placers/placer_boilerplate.hpp + include/libnest2d/placers/bottomleftplacer.hpp + include/libnest2d/placers/nfpplacer.hpp + include/libnest2d/selections/selection_boilerplate.hpp + #include/libnest2d/selections/filler.hpp + include/libnest2d/selections/firstfit.hpp + #include/libnest2d/selections/djd_heuristic.hpp + include/libnest2d/backends/clipper/geometries.hpp + include/libnest2d/backends/clipper/clipper_polygon.hpp + include/libnest2d/optimizers/nlopt/nlopt_boilerplate.hpp + include/libnest2d/optimizers/nlopt/simplex.hpp + include/libnest2d/optimizers/nlopt/subplex.hpp + include/libnest2d/optimizers/nlopt/genetic.hpp + src/libnest2d.cpp ) -set(TBB_STATIC ON) -find_package(TBB QUIET) -if(TBB_FOUND) - message(STATUS "Parallelization with Intel TBB") - target_include_directories(libnest2d INTERFACE ${TBB_INCLUDE_DIRS}) - target_compile_definitions(libnest2d INTERFACE ${TBB_DEFINITIONS} -DUSE_TBB) - if(MSVC) - # Suppress implicit linking of the TBB libraries by the Visual Studio compiler. - target_compile_definitions(libnest2d INTERFACE -D__TBB_NO_IMPLICIT_LINKAGE) - endif() - # The Intel TBB library will use the std::exception_ptr feature of C++11. - target_compile_definitions(libnest2d INTERFACE -DTBB_USE_CAPTURED_EXCEPTION=0) +add_library(libnest2d ${LIBNEST2D_SRCFILES}) - find_package(Threads REQUIRED) - target_link_libraries(libnest2d INTERFACE - tbb # VS debug mode needs linking this way: - # ${TBB_LIBRARIES} - ${CMAKE_DL_LIBS} - Threads::Threads - ) -else() - find_package(OpenMP QUIET) - - if(OpenMP_CXX_FOUND) - message(STATUS "Parallelization with OpenMP") - target_include_directories(libnest2d INTERFACE OpenMP::OpenMP_CXX) - target_link_libraries(libnest2d INTERFACE OpenMP::OpenMP_CXX) - else() - message("Parallelization with C++11 threads") - find_package(Threads REQUIRED) - target_link_libraries(libnest2d INTERFACE Threads::Threads) - endif() -endif() - -add_subdirectory(${SRC_DIR}/libnest2d/backends/${LIBNEST2D_GEOMETRIES}) -target_link_libraries(libnest2d INTERFACE ${LIBNEST2D_GEOMETRIES}Backend) - -add_subdirectory(${SRC_DIR}/libnest2d/optimizers/${LIBNEST2D_OPTIMIZER}) -target_link_libraries(libnest2d INTERFACE ${LIBNEST2D_OPTIMIZER}Optimizer) - -# target_sources(libnest2d INTERFACE ${LIBNEST2D_SRCFILES}) -target_include_directories(libnest2d INTERFACE ${SRC_DIR}) - -if(NOT LIBNEST2D_HEADER_ONLY) - set(LIBNAME libnest2d_${LIBNEST2D_GEOMETRIES}_${LIBNEST2D_OPTIMIZER}) - add_library(${LIBNAME} ${PROJECT_SOURCE_DIR}/src/libnest2d.cpp) - target_link_libraries(${LIBNAME} PUBLIC libnest2d) - target_compile_definitions(${LIBNAME} PUBLIC LIBNEST2D_STATIC) -endif() - -if(LIBNEST2D_BUILD_EXAMPLES) - - add_executable(example examples/main.cpp - # tools/libnfpglue.hpp - # tools/libnfpglue.cpp - tools/nfp_svgnest.hpp - tools/nfp_svgnest_glue.hpp - tools/svgtools.hpp - tests/printer_parts.cpp - tests/printer_parts.h - ) - - if(NOT LIBNEST2D_HEADER_ONLY) - target_link_libraries(example ${LIBNAME}) - else() - target_link_libraries(example libnest2d) - endif() -endif() - -if(LIBNEST2D_UNITTESTS) - add_subdirectory(${PROJECT_SOURCE_DIR}/tests) -endif() +target_include_directories(libnest2d PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) +target_link_libraries(libnest2d PUBLIC clipper NLopt::nlopt TBB::tbb Boost::boost) +target_compile_definitions(libnest2d PUBLIC USE_TBB LIBNEST2D_STATIC LIBNEST2D_OPTIMIZER_nlopt LIBNEST2D_GEOMETRIES_clipper) diff --git a/src/libnest2d/cmake_modules/DownloadNLopt.cmake b/src/libnest2d/cmake_modules/DownloadNLopt.cmake deleted file mode 100644 index 62b2b4c1a..000000000 --- a/src/libnest2d/cmake_modules/DownloadNLopt.cmake +++ /dev/null @@ -1,35 +0,0 @@ -include(DownloadProject) - -if (CMAKE_VERSION VERSION_LESS 3.2) - set(UPDATE_DISCONNECTED_IF_AVAILABLE "") -else() - set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1") -endif() - -set(URL_NLOPT "https://github.com/stevengj/nlopt.git" - CACHE STRING "Location of the nlopt git repository") - -# set(NLopt_DIR ${CMAKE_BINARY_DIR}/nlopt) -include(DownloadProject) -download_project( PROJ nlopt - GIT_REPOSITORY ${URL_NLOPT} - GIT_TAG v2.5.0 - # CMAKE_CACHE_ARGS -DBUILD_SHARED_LIBS:BOOL=OFF -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${NLopt_DIR} - ${UPDATE_DISCONNECTED_IF_AVAILABLE} -) - -set(SHARED_LIBS_STATE BUILD_SHARED_LIBS) -set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) -set(NLOPT_PYTHON OFF CACHE BOOL "" FORCE) -set(NLOPT_OCTAVE OFF CACHE BOOL "" FORCE) -set(NLOPT_MATLAB OFF CACHE BOOL "" FORCE) -set(NLOPT_GUILE OFF CACHE BOOL "" FORCE) -set(NLOPT_SWIG OFF CACHE BOOL "" FORCE) -set(NLOPT_LINK_PYTHON OFF CACHE BOOL "" FORCE) - -add_subdirectory(${nlopt_SOURCE_DIR} ${nlopt_BINARY_DIR}) - -set(NLopt_LIBS nlopt) -set(NLopt_INCLUDE_DIR ${nlopt_BINARY_DIR} - ${nlopt_BINARY_DIR}/src/api) -set(SHARED_LIBS_STATE ${SHARED_STATE}) \ No newline at end of file diff --git a/src/libnest2d/cmake_modules/DownloadProject.CMakeLists.cmake.in b/src/libnest2d/cmake_modules/DownloadProject.CMakeLists.cmake.in deleted file mode 100644 index d5cf3c1d9..000000000 --- a/src/libnest2d/cmake_modules/DownloadProject.CMakeLists.cmake.in +++ /dev/null @@ -1,17 +0,0 @@ -# Distributed under the OSI-approved MIT License. See accompanying -# file LICENSE or https://github.com/Crascit/DownloadProject for details. - -cmake_minimum_required(VERSION 2.8.2) - -project(${DL_ARGS_PROJ}-download NONE) - -include(ExternalProject) -ExternalProject_Add(${DL_ARGS_PROJ}-download - ${DL_ARGS_UNPARSED_ARGUMENTS} - SOURCE_DIR "${DL_ARGS_SOURCE_DIR}" - BINARY_DIR "${DL_ARGS_BINARY_DIR}" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" -) \ No newline at end of file diff --git a/src/libnest2d/cmake_modules/DownloadProject.cmake b/src/libnest2d/cmake_modules/DownloadProject.cmake deleted file mode 100644 index 1709e09ad..000000000 --- a/src/libnest2d/cmake_modules/DownloadProject.cmake +++ /dev/null @@ -1,182 +0,0 @@ -# Distributed under the OSI-approved MIT License. See accompanying -# file LICENSE or https://github.com/Crascit/DownloadProject for details. -# -# MODULE: DownloadProject -# -# PROVIDES: -# download_project( PROJ projectName -# [PREFIX prefixDir] -# [DOWNLOAD_DIR downloadDir] -# [SOURCE_DIR srcDir] -# [BINARY_DIR binDir] -# [QUIET] -# ... -# ) -# -# Provides the ability to download and unpack a tarball, zip file, git repository, -# etc. at configure time (i.e. when the cmake command is run). How the downloaded -# and unpacked contents are used is up to the caller, but the motivating case is -# to download source code which can then be included directly in the build with -# add_subdirectory() after the call to download_project(). Source and build -# directories are set up with this in mind. -# -# The PROJ argument is required. The projectName value will be used to construct -# the following variables upon exit (obviously replace projectName with its actual -# value): -# -# projectName_SOURCE_DIR -# projectName_BINARY_DIR -# -# The SOURCE_DIR and BINARY_DIR arguments are optional and would not typically -# need to be provided. They can be specified if you want the downloaded source -# and build directories to be located in a specific place. The contents of -# projectName_SOURCE_DIR and projectName_BINARY_DIR will be populated with the -# locations used whether you provide SOURCE_DIR/BINARY_DIR or not. -# -# The DOWNLOAD_DIR argument does not normally need to be set. It controls the -# location of the temporary CMake build used to perform the download. -# -# The PREFIX argument can be provided to change the base location of the default -# values of DOWNLOAD_DIR, SOURCE_DIR and BINARY_DIR. If all of those three arguments -# are provided, then PREFIX will have no effect. The default value for PREFIX is -# CMAKE_BINARY_DIR. -# -# The QUIET option can be given if you do not want to show the output associated -# with downloading the specified project. -# -# In addition to the above, any other options are passed through unmodified to -# ExternalProject_Add() to perform the actual download, patch and update steps. -# The following ExternalProject_Add() options are explicitly prohibited (they -# are reserved for use by the download_project() command): -# -# CONFIGURE_COMMAND -# BUILD_COMMAND -# INSTALL_COMMAND -# TEST_COMMAND -# -# Only those ExternalProject_Add() arguments which relate to downloading, patching -# and updating of the project sources are intended to be used. Also note that at -# least one set of download-related arguments are required. -# -# If using CMake 3.2 or later, the UPDATE_DISCONNECTED option can be used to -# prevent a check at the remote end for changes every time CMake is run -# after the first successful download. See the documentation of the ExternalProject -# module for more information. It is likely you will want to use this option if it -# is available to you. Note, however, that the ExternalProject implementation contains -# bugs which result in incorrect handling of the UPDATE_DISCONNECTED option when -# using the URL download method or when specifying a SOURCE_DIR with no download -# method. Fixes for these have been created, the last of which is scheduled for -# inclusion in CMake 3.8.0. Details can be found here: -# -# https://gitlab.kitware.com/cmake/cmake/commit/bdca68388bd57f8302d3c1d83d691034b7ffa70c -# https://gitlab.kitware.com/cmake/cmake/issues/16428 -# -# If you experience build errors related to the update step, consider avoiding -# the use of UPDATE_DISCONNECTED. -# -# EXAMPLE USAGE: -# -# include(DownloadProject) -# download_project(PROJ googletest -# GIT_REPOSITORY https://github.com/google/googletest.git -# GIT_TAG master -# UPDATE_DISCONNECTED 1 -# QUIET -# ) -# -# add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) -# -#======================================================================================== - - -set(_DownloadProjectDir "${CMAKE_CURRENT_LIST_DIR}") - -include(CMakeParseArguments) - -function(download_project) - - set(options QUIET) - set(oneValueArgs - PROJ - PREFIX - DOWNLOAD_DIR - SOURCE_DIR - BINARY_DIR - # Prevent the following from being passed through - CONFIGURE_COMMAND - BUILD_COMMAND - INSTALL_COMMAND - TEST_COMMAND - ) - set(multiValueArgs "") - - cmake_parse_arguments(DL_ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Hide output if requested - if (DL_ARGS_QUIET) - set(OUTPUT_QUIET "OUTPUT_QUIET") - else() - unset(OUTPUT_QUIET) - message(STATUS "Downloading/updating ${DL_ARGS_PROJ}") - endif() - - # Set up where we will put our temporary CMakeLists.txt file and also - # the base point below which the default source and binary dirs will be. - # The prefix must always be an absolute path. - if (NOT DL_ARGS_PREFIX) - set(DL_ARGS_PREFIX "${CMAKE_BINARY_DIR}") - else() - get_filename_component(DL_ARGS_PREFIX "${DL_ARGS_PREFIX}" ABSOLUTE - BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}") - endif() - if (NOT DL_ARGS_DOWNLOAD_DIR) - set(DL_ARGS_DOWNLOAD_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-download") - endif() - - # Ensure the caller can know where to find the source and build directories - if (NOT DL_ARGS_SOURCE_DIR) - set(DL_ARGS_SOURCE_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-src") - endif() - if (NOT DL_ARGS_BINARY_DIR) - set(DL_ARGS_BINARY_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-build") - endif() - set(${DL_ARGS_PROJ}_SOURCE_DIR "${DL_ARGS_SOURCE_DIR}" PARENT_SCOPE) - set(${DL_ARGS_PROJ}_BINARY_DIR "${DL_ARGS_BINARY_DIR}" PARENT_SCOPE) - - # The way that CLion manages multiple configurations, it causes a copy of - # the CMakeCache.txt to be copied across due to it not expecting there to - # be a project within a project. This causes the hard-coded paths in the - # cache to be copied and builds to fail. To mitigate this, we simply - # remove the cache if it exists before we configure the new project. It - # is safe to do so because it will be re-generated. Since this is only - # executed at the configure step, it should not cause additional builds or - # downloads. - file(REMOVE "${DL_ARGS_DOWNLOAD_DIR}/CMakeCache.txt") - - # Create and build a separate CMake project to carry out the download. - # If we've already previously done these steps, they will not cause - # anything to be updated, so extra rebuilds of the project won't occur. - # Make sure to pass through CMAKE_MAKE_PROGRAM in case the main project - # has this set to something not findable on the PATH. - configure_file("${_DownloadProjectDir}/DownloadProject.CMakeLists.cmake.in" - "${DL_ARGS_DOWNLOAD_DIR}/CMakeLists.txt") - execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" - -D "CMAKE_MAKE_PROGRAM:FILE=${CMAKE_MAKE_PROGRAM}" - . - RESULT_VARIABLE result - ${OUTPUT_QUIET} - WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}" - ) - if(result) - message(FATAL_ERROR "CMake step for ${DL_ARGS_PROJ} failed: ${result}") - endif() - execute_process(COMMAND ${CMAKE_COMMAND} --build . - RESULT_VARIABLE result - ${OUTPUT_QUIET} - WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}" - ) - if(result) - message(FATAL_ERROR "Build step for ${DL_ARGS_PROJ} failed: ${result}") - endif() - -endfunction() \ No newline at end of file diff --git a/src/libnest2d/cmake_modules/FindClipper.cmake b/src/libnest2d/cmake_modules/FindClipper.cmake deleted file mode 100644 index 01b6b99d5..000000000 --- a/src/libnest2d/cmake_modules/FindClipper.cmake +++ /dev/null @@ -1,58 +0,0 @@ -# Find Clipper library (http://www.angusj.com/delphi/clipper.php). -# The following variables are set -# -# CLIPPER_FOUND -# CLIPPER_INCLUDE_DIRS -# CLIPPER_LIBRARIES -# -# It searches the environment variable $CLIPPER_PATH automatically. - -FIND_PATH(CLIPPER_INCLUDE_DIRS clipper.hpp - $ENV{CLIPPER_PATH} - $ENV{CLIPPER_PATH}/cpp/ - $ENV{CLIPPER_PATH}/include/ - $ENV{CLIPPER_PATH}/include/polyclipping/ - ${PROJECT_SOURCE_DIR}/python/pymesh/third_party/include/ - ${PROJECT_SOURCE_DIR}/python/pymesh/third_party/include/polyclipping/ - ${CMAKE_PREFIX_PATH}/include/polyclipping - ${CMAKE_PREFIX_PATH}/include/ - /opt/local/include/ - /opt/local/include/polyclipping/ - /usr/local/include/ - /usr/local/include/polyclipping/ - /usr/include - /usr/include/polyclipping/) - -FIND_LIBRARY(CLIPPER_LIBRARIES polyclipping - $ENV{CLIPPER_PATH} - $ENV{CLIPPER_PATH}/cpp/ - $ENV{CLIPPER_PATH}/cpp/build/ - $ENV{CLIPPER_PATH}/lib/ - $ENV{CLIPPER_PATH}/lib/polyclipping/ - ${PROJECT_SOURCE_DIR}/python/pymesh/third_party/lib/ - ${PROJECT_SOURCE_DIR}/python/pymesh/third_party/lib/polyclipping/ - ${CMAKE_PREFIX_PATH}/lib/ - ${CMAKE_PREFIX_PATH}/lib/polyclipping/ - /opt/local/lib/ - /opt/local/lib/polyclipping/ - /usr/local/lib/ - /usr/local/lib/polyclipping/ - /usr/lib/polyclipping) - -include(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(Clipper - "Clipper library cannot be found. Consider set CLIPPER_PATH environment variable" - CLIPPER_INCLUDE_DIRS - CLIPPER_LIBRARIES) - -MARK_AS_ADVANCED( - CLIPPER_INCLUDE_DIRS - CLIPPER_LIBRARIES) - -if(CLIPPER_FOUND) - add_library(Clipper::Clipper INTERFACE IMPORTED) - set_target_properties(Clipper::Clipper PROPERTIES INTERFACE_LINK_LIBRARIES ${CLIPPER_LIBRARIES}) - set_target_properties(Clipper::Clipper PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CLIPPER_INCLUDE_DIRS}) - #target_link_libraries(Clipper::Clipper INTERFACE ${CLIPPER_LIBRARIES}) - #target_include_directories(Clipper::Clipper INTERFACE ${CLIPPER_INCLUDE_DIRS}) -endif() diff --git a/src/libnest2d/include/libnest2d.h b/src/libnest2d/include/libnest2d.h deleted file mode 100644 index 4661b4574..000000000 --- a/src/libnest2d/include/libnest2d.h +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef LIBNEST2D_H -#define LIBNEST2D_H - -// The type of backend should be set conditionally by the cmake configuriation -// for now we set it statically to clipper backend -#ifdef LIBNEST2D_BACKEND_CLIPPER -#include -#endif - -#ifdef LIBNEST2D_OPTIMIZER_NLOPT -// We include the stock optimizers for local and global optimization -#include // Local subplex for NfpPlacer -#include // Genetic for min. bounding box -#endif - -#include -#include -#include -#include -#include -#include - -namespace libnest2d { - -using Point = PointImpl; -using Coord = TCoord; -using Box = _Box; -using Segment = _Segment; -using Circle = _Circle; - -using Item = _Item; -using Rectangle = _Rectangle; -using PackGroup = _PackGroup; - -using FillerSelection = selections::_FillerSelection; -using FirstFitSelection = selections::_FirstFitSelection; -using DJDHeuristic = selections::_DJDHeuristic; - -template // Generic placer for arbitrary bin types -using _NfpPlacer = placers::_NofitPolyPlacer; - -// NfpPlacer is with Box bin -using NfpPlacer = _NfpPlacer; - -// This supports only box shaped bins -using BottomLeftPlacer = placers::_BottomLeftPlacer; - -#ifdef LIBNEST2D_STATIC - -extern template class Nester; -extern template class Nester; -extern template PackGroup Nester::execute( - std::vector::iterator, std::vector::iterator); -extern template PackGroup Nester::execute( - std::vector::iterator, std::vector::iterator); - -#endif - -template::iterator> -void nest(Iterator from, Iterator to, - const typename Placer::BinType& bin, - Coord dist = 0, - const typename Placer::Config& pconf = {}, - const typename Selector::Config& sconf = {}) -{ - _Nester nester(bin, dist, pconf, sconf); - nester.execute(from, to); -} - -template::iterator> -void nest(Iterator from, Iterator to, - const typename Placer::BinType& bin, - ProgressFunction prg, - StopCondition scond = []() { return false; }, - Coord dist = 0, - const typename Placer::Config& pconf = {}, - const typename Selector::Config& sconf = {}) -{ - _Nester nester(bin, dist, pconf, sconf); - if(prg) nester.progressIndicator(prg); - if(scond) nester.stopCondition(scond); - nester.execute(from, to); -} - -#ifdef LIBNEST2D_STATIC - -extern template class Nester; -extern template class Nester; - -extern template void nest(std::vector::iterator from, - std::vector::iterator to, - const Box& bin, - Coord dist = 0, - const NfpPlacer::Config& pconf, - const FirstFitSelection::Config& sconf); - -extern template void nest(std::vector::iterator from, - std::vector::iterator to, - const Box& bin, - ProgressFunction prg, - StopCondition scond, - Coord dist = 0, - const NfpPlacer::Config& pconf, - const FirstFitSelection::Config& sconf); - -#endif - -template> -void nest(Container&& cont, - const typename Placer::BinType& bin, - Coord dist = 0, - const typename Placer::Config& pconf = {}, - const typename Selector::Config& sconf = {}) -{ - nest(cont.begin(), cont.end(), bin, dist, pconf, sconf); -} - -template> -void nest(Container&& cont, - const typename Placer::BinType& bin, - ProgressFunction prg, - StopCondition scond = []() { return false; }, - Coord dist = 0, - const typename Placer::Config& pconf = {}, - const typename Selector::Config& sconf = {}) -{ - nest(cont.begin(), cont.end(), bin, prg, scond, dist, - pconf, sconf); -} - -} - -#endif // LIBNEST2D_H diff --git a/src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt b/src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt deleted file mode 100644 index 202089356..000000000 --- a/src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt +++ /dev/null @@ -1,73 +0,0 @@ -if(NOT TARGET clipper) # If there is a clipper target in the parent project we are good to go. - - find_package(Clipper 6.1) - - if(NOT CLIPPER_FOUND) - find_package(Subversion QUIET) - if(Subversion_FOUND) - - set(URL_CLIPPER "https://svn.code.sf.net/p/polyclipping/code/trunk/cpp" - CACHE STRING "Clipper source code repository location.") - - message(STATUS "Clipper not found so it will be downloaded.") - # Silently download and build the library in the build dir - - if (CMAKE_VERSION VERSION_LESS 3.2) - set(UPDATE_DISCONNECTED_IF_AVAILABLE "") - else() - set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1") - endif() - - include(DownloadProject) - download_project( PROJ clipper_library - SVN_REPOSITORY ${URL_CLIPPER} - SVN_REVISION -r540 - #SOURCE_SUBDIR cpp - INSTALL_COMMAND "" - CONFIGURE_COMMAND "" # Not working, I will just add the source files - ${UPDATE_DISCONNECTED_IF_AVAILABLE} - ) - - # This is not working and I dont have time to fix it - # add_subdirectory(${clipper_library_SOURCE_DIR}/cpp - # ${clipper_library_BINARY_DIR} - # ) - - add_library(clipperBackend STATIC - ${clipper_library_SOURCE_DIR}/clipper.cpp - ${clipper_library_SOURCE_DIR}/clipper.hpp) - - target_include_directories(clipperBackend INTERFACE ${clipper_library_SOURCE_DIR}) - else() - message(FATAL_ERROR "Can't find clipper library and no SVN client found to download. - You can download the clipper sources and define a clipper target in your project, that will work for libnest2d.") - endif() - else() - add_library(clipperBackend INTERFACE) - target_link_libraries(clipperBackend INTERFACE Clipper::Clipper) - endif() -else() - # set(CLIPPER_INCLUDE_DIRS "" PARENT_SCOPE) - # set(CLIPPER_LIBRARIES clipper PARENT_SCOPE) - add_library(clipperBackend INTERFACE) - target_link_libraries(clipperBackend INTERFACE clipper) -endif() - -# Clipper backend is not enough on its own, it still needs some functions -# from Boost geometry -if(NOT Boost_FOUND) - find_package(Boost 1.58 REQUIRED) - # TODO automatic download of boost geometry headers -endif() - -target_link_libraries(clipperBackend INTERFACE Boost::boost ) -#target_sources(ClipperBackend INTERFACE -# ${CMAKE_CURRENT_SOURCE_DIR}/geometries.hpp -# ${CMAKE_CURRENT_SOURCE_DIR}/clipper_polygon.hpp -# ${SRC_DIR}/libnest2d/utils/boost_alg.hpp ) - -target_compile_definitions(clipperBackend INTERFACE LIBNEST2D_BACKEND_CLIPPER) - -# And finally plug the clipperBackend into libnest2d -# target_link_libraries(libnest2d INTERFACE clipperBackend) - diff --git a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp index 56330e15e..57da6ec12 100644 --- a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp +++ b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp @@ -81,17 +81,16 @@ inline void offset(PolygonImpl& sh, TCoord distance, const PolygonTag using ClipperLib::etClosedPolygon; using ClipperLib::Paths; - // If the input is not at least a triangle, we can not do this algorithm - if(sh.Contour.size() <= 3 || - std::any_of(sh.Holes.begin(), sh.Holes.end(), - [](const PathImpl& p) { return p.size() <= 3; }) - ) throw GeometryException(GeomErr::OFFSET); - - ClipperOffset offs; Paths result; - offs.AddPath(sh.Contour, jtMiter, etClosedPolygon); - offs.AddPaths(sh.Holes, jtMiter, etClosedPolygon); - offs.Execute(result, static_cast(distance)); + + try { + ClipperOffset offs; + offs.AddPath(sh.Contour, jtMiter, etClosedPolygon); + offs.AddPaths(sh.Holes, jtMiter, etClosedPolygon); + offs.Execute(result, static_cast(distance)); + } catch (ClipperLib::clipperException &) { + throw GeometryException(GeomErr::OFFSET); + } // Offsetting reverts the orientation and also removes the last vertex // so boost will not have a closed polygon. diff --git a/src/libnest2d/include/libnest2d/geometry_traits.hpp b/src/libnest2d/include/libnest2d/geometry_traits.hpp index 827e2d8ba..72e239a70 100644 --- a/src/libnest2d/include/libnest2d/geometry_traits.hpp +++ b/src/libnest2d/include/libnest2d/geometry_traits.hpp @@ -1144,7 +1144,7 @@ inline bool isInside(const TBGuest& ibb, const TBHost& box, auto minY = getY(box.minCorner()); auto maxY = getY(box.maxCorner()); - return iminX > minX && imaxX < maxX && iminY > minY && imaxY < maxY; + return iminX >= minX && imaxX <= maxX && iminY >= minY && imaxY <= maxY; } template diff --git a/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp b/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp index 4a2c69bca..29a1ccd04 100644 --- a/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp +++ b/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp @@ -299,9 +299,456 @@ inline NfpResult nfpConvexOnly(const RawShape& sh, template NfpResult nfpSimpleSimple(const RawShape& cstationary, - const RawShape& cother) + const RawShape& cother) { - return {}; + + // Algorithms are from the original algorithm proposed in paper: + // https://eprints.soton.ac.uk/36850/1/CORMSIS-05-05.pdf + + // ///////////////////////////////////////////////////////////////////////// + // Algorithm 1: Obtaining the minkowski sum + // ///////////////////////////////////////////////////////////////////////// + + // I guess this is not a full minkowski sum of the two input polygons by + // definition. This yields a subset that is compatible with the next 2 + // algorithms. + + using Result = NfpResult; + using Vertex = TPoint; + using Coord = TCoord; + using Edge = _Segment; + namespace sl = shapelike; + using std::signbit; + using std::sort; + using std::vector; + using std::ref; + using std::reference_wrapper; + + // TODO The original algorithms expects the stationary polygon in + // counter clockwise and the orbiter in clockwise order. + // So for preventing any further complication, I will make the input + // the way it should be, than make my way around the orientations. + + // Reverse the stationary contour to counter clockwise + auto stcont = sl::contour(cstationary); + { + std::reverse(sl::begin(stcont), sl::end(stcont)); + stcont.pop_back(); + auto it = std::min_element(sl::begin(stcont), sl::end(stcont), + [](const Vertex& v1, const Vertex& v2) { + return getY(v1) < getY(v2); + }); + std::rotate(sl::begin(stcont), it, sl::end(stcont)); + sl::addVertex(stcont, sl::front(stcont)); + } + RawShape stationary; + sl::contour(stationary) = stcont; + + // Reverse the orbiter contour to counter clockwise + auto orbcont = sl::contour(cother); + { + std::reverse(orbcont.begin(), orbcont.end()); + + // Step 1: Make the orbiter reverse oriented + + orbcont.pop_back(); + auto it = std::min_element(orbcont.begin(), orbcont.end(), + [](const Vertex& v1, const Vertex& v2) { + return getY(v1) < getY(v2); + }); + + std::rotate(orbcont.begin(), it, orbcont.end()); + orbcont.emplace_back(orbcont.front()); + + for(auto &v : orbcont) v = -v; + + } + + // Copy the orbiter (contour only), we will have to work on it + RawShape orbiter; + sl::contour(orbiter) = orbcont; + + // An edge with additional data for marking it + struct MarkedEdge { + Edge e; Radians turn_angle = 0; bool is_turning_point = false; + MarkedEdge() = default; + MarkedEdge(const Edge& ed, Radians ta, bool tp): + e(ed), turn_angle(ta), is_turning_point(tp) {} + + // debug + std::string label; + }; + + // Container for marked edges + using EdgeList = vector; + + EdgeList A, B; + + // This is how an edge list is created from the polygons + auto fillEdgeList = [](EdgeList& L, const RawShape& ppoly, int dir) { + auto& poly = sl::contour(ppoly); + + L.reserve(sl::contourVertexCount(poly)); + + if(dir > 0) { + auto it = poly.begin(); + auto nextit = std::next(it); + + double turn_angle = 0; + bool is_turn_point = false; + + while(nextit != poly.end()) { + L.emplace_back(Edge(*it, *nextit), turn_angle, is_turn_point); + it++; nextit++; + } + } else { + auto it = sl::rbegin(poly); + auto nextit = std::next(it); + + double turn_angle = 0; + bool is_turn_point = false; + + while(nextit != sl::rend(poly)) { + L.emplace_back(Edge(*it, *nextit), turn_angle, is_turn_point); + it++; nextit++; + } + } + + auto getTurnAngle = [](const Edge& e1, const Edge& e2) { + auto phi = e1.angleToXaxis(); + auto phi_prev = e2.angleToXaxis(); + auto turn_angle = phi-phi_prev; + if(turn_angle > Pi) turn_angle -= TwoPi; + if(turn_angle < -Pi) turn_angle += TwoPi; + return turn_angle; + }; + + auto eit = L.begin(); + auto enext = std::next(eit); + + eit->turn_angle = getTurnAngle(L.front().e, L.back().e); + + while(enext != L.end()) { + enext->turn_angle = getTurnAngle( enext->e, eit->e); + eit->is_turning_point = + signbit(enext->turn_angle) != signbit(eit->turn_angle); + ++eit; ++enext; + } + + L.back().is_turning_point = signbit(L.back().turn_angle) != + signbit(L.front().turn_angle); + + }; + + // Step 2: Fill the edgelists + fillEdgeList(A, stationary, 1); + fillEdgeList(B, orbiter, 1); + + int i = 1; + for(MarkedEdge& me : A) { + std::cout << "a" << i << ":\n\t" + << getX(me.e.first()) << " " << getY(me.e.first()) << "\n\t" + << getX(me.e.second()) << " " << getY(me.e.second()) << "\n\t" + << "Turning point: " << (me.is_turning_point ? "yes" : "no") + << std::endl; + + me.label = "a"; me.label += std::to_string(i); + i++; + } + + i = 1; + for(MarkedEdge& me : B) { + std::cout << "b" << i << ":\n\t" + << getX(me.e.first()) << " " << getY(me.e.first()) << "\n\t" + << getX(me.e.second()) << " " << getY(me.e.second()) << "\n\t" + << "Turning point: " << (me.is_turning_point ? "yes" : "no") + << std::endl; + me.label = "b"; me.label += std::to_string(i); + i++; + } + + // A reference to a marked edge that also knows its container + struct MarkedEdgeRef { + reference_wrapper eref; + reference_wrapper> container; + Coord dir = 1; // Direction modifier + + inline Radians angleX() const { return eref.get().e.angleToXaxis(); } + inline const Edge& edge() const { return eref.get().e; } + inline Edge& edge() { return eref.get().e; } + inline bool isTurningPoint() const { + return eref.get().is_turning_point; + } + inline bool isFrom(const vector& cont ) { + return &(container.get()) == &cont; + } + inline bool eq(const MarkedEdgeRef& mr) { + return &(eref.get()) == &(mr.eref.get()); + } + + MarkedEdgeRef(reference_wrapper er, + reference_wrapper> ec): + eref(er), container(ec), dir(1) {} + + MarkedEdgeRef(reference_wrapper er, + reference_wrapper> ec, + Coord d): + eref(er), container(ec), dir(d) {} + }; + + using EdgeRefList = vector; + + // Comparing two marked edges + auto sortfn = [](const MarkedEdgeRef& e1, const MarkedEdgeRef& e2) { + return e1.angleX() < e2.angleX(); + }; + + EdgeRefList Aref, Bref; // We create containers for the references + Aref.reserve(A.size()); Bref.reserve(B.size()); + + // Fill reference container for the stationary polygon + std::for_each(A.begin(), A.end(), [&Aref](MarkedEdge& me) { + Aref.emplace_back( ref(me), ref(Aref) ); + }); + + // Fill reference container for the orbiting polygon + std::for_each(B.begin(), B.end(), [&Bref](MarkedEdge& me) { + Bref.emplace_back( ref(me), ref(Bref) ); + }); + + auto mink = [sortfn] // the Mink(Q, R, direction) sub-procedure + (const EdgeRefList& Q, const EdgeRefList& R, bool positive) + { + + // Step 1 "merge sort_list(Q) and sort_list(R) to form merge_list(Q,R)" + // Sort the containers of edge references and merge them. + // Q could be sorted only once and be reused here but we would still + // need to merge it with sorted(R). + + EdgeRefList merged; + EdgeRefList S, seq; + merged.reserve(Q.size() + R.size()); + + merged.insert(merged.end(), R.begin(), R.end()); + std::stable_sort(merged.begin(), merged.end(), sortfn); + merged.insert(merged.end(), Q.begin(), Q.end()); + std::stable_sort(merged.begin(), merged.end(), sortfn); + + // Step 2 "set i = 1, k = 1, direction = 1, s1 = q1" + // we don't use i, instead, q is an iterator into Q. k would be an index + // into the merged sequence but we use "it" as an iterator for that + + // here we obtain references for the containers for later comparisons + const auto& Rcont = R.begin()->container.get(); + const auto& Qcont = Q.begin()->container.get(); + + // Set the initial direction + Coord dir = 1; + + // roughly i = 1 (so q = Q.begin()) and s1 = q1 so S[0] = q; + if(positive) { + auto q = Q.begin(); + S.emplace_back(*q); + + // Roughly step 3 + + std::cout << "merged size: " << merged.size() << std::endl; + auto mit = merged.begin(); + for(bool finish = false; !finish && q != Q.end();) { + ++q; // "Set i = i + 1" + + while(!finish && mit != merged.end()) { + if(mit->isFrom(Rcont)) { + auto s = *mit; + s.dir = dir; + S.emplace_back(s); + } + + if(mit->eq(*q)) { + S.emplace_back(*q); + if(mit->isTurningPoint()) dir = -dir; + if(q == Q.begin()) finish = true; + break; + } + + mit += dir; + // __nfp::advance(mit, merged, dir > 0); + } + } + } else { + auto q = Q.rbegin(); + S.emplace_back(*q); + + // Roughly step 3 + + std::cout << "merged size: " << merged.size() << std::endl; + auto mit = merged.begin(); + for(bool finish = false; !finish && q != Q.rend();) { + ++q; // "Set i = i + 1" + + while(!finish && mit != merged.end()) { + if(mit->isFrom(Rcont)) { + auto s = *mit; + s.dir = dir; + S.emplace_back(s); + } + + if(mit->eq(*q)) { + S.emplace_back(*q); + S.back().dir = -1; + if(mit->isTurningPoint()) dir = -dir; + if(q == Q.rbegin()) finish = true; + break; + } + + mit += dir; + // __nfp::advance(mit, merged, dir > 0); + } + } + } + + + // Step 4: + + // "Let starting edge r1 be in position si in sequence" + // whaaat? I guess this means the following: + auto it = S.begin(); + while(!it->eq(*R.begin())) ++it; + + // "Set j = 1, next = 2, direction = 1, seq1 = si" + // we don't use j, seq is expanded dynamically. + dir = 1; + auto next = std::next(R.begin()); seq.emplace_back(*it); + + // Step 5: + // "If all si edges have been allocated to seqj" should mean that + // we loop until seq has equal size with S + auto send = it; //it == S.begin() ? it : std::prev(it); + while(it != S.end()) { + ++it; if(it == S.end()) it = S.begin(); + if(it == send) break; + + if(it->isFrom(Qcont)) { + seq.emplace_back(*it); // "If si is from Q, j = j + 1, seqj = si" + + // "If si is a turning point in Q, + // direction = - direction, next = next + direction" + if(it->isTurningPoint()) { + dir = -dir; + next += dir; +// __nfp::advance(next, R, dir > 0); + } + } + + if(it->eq(*next) /*&& dir == next->dir*/) { // "If si = direction.rnext" + // "j = j + 1, seqj = si, next = next + direction" + seq.emplace_back(*it); + next += dir; +// __nfp::advance(next, R, dir > 0); + } + } + + return seq; + }; + + std::vector seqlist; + seqlist.reserve(Bref.size()); + + EdgeRefList Bslope = Bref; // copy Bref, we will make a slope diagram + + // make the slope diagram of B + std::sort(Bslope.begin(), Bslope.end(), sortfn); + + auto slopeit = Bslope.begin(); // search for the first turning point + while(!slopeit->isTurningPoint() && slopeit != Bslope.end()) slopeit++; + + if(slopeit == Bslope.end()) { + // no turning point means convex polygon. + seqlist.emplace_back(mink(Aref, Bref, true)); + } else { + int dir = 1; + + auto firstturn = Bref.begin(); + while(!firstturn->eq(*slopeit)) ++firstturn; + + assert(firstturn != Bref.end()); + + EdgeRefList bgroup; bgroup.reserve(Bref.size()); + bgroup.emplace_back(*slopeit); + + auto b_it = std::next(firstturn); + while(b_it != firstturn) { + if(b_it == Bref.end()) b_it = Bref.begin(); + + while(!slopeit->eq(*b_it)) { + __nfp::advance(slopeit, Bslope, dir > 0); + } + + if(!slopeit->isTurningPoint()) { + bgroup.emplace_back(*slopeit); + } else { + if(!bgroup.empty()) { + if(dir > 0) bgroup.emplace_back(*slopeit); + for(auto& me : bgroup) { + std::cout << me.eref.get().label << ", "; + } + std::cout << std::endl; + seqlist.emplace_back(mink(Aref, bgroup, dir == 1 ? true : false)); + bgroup.clear(); + if(dir < 0) bgroup.emplace_back(*slopeit); + } else { + bgroup.emplace_back(*slopeit); + } + + dir *= -1; + } + ++b_it; + } + } + +// while(it != Bref.end()) // This is step 3 and step 4 in one loop +// if(it->isTurningPoint()) { +// R = {R.last, it++}; +// auto seq = mink(Q, R, orientation); + +// // TODO step 6 (should be 5 shouldn't it?): linking edges from A +// // I don't get this step + +// seqlist.insert(seqlist.end(), seq.begin(), seq.end()); +// orientation = !orientation; +// } else ++it; + +// if(seqlist.empty()) seqlist = mink(Q, {Bref.begin(), Bref.end()}, true); + + // ///////////////////////////////////////////////////////////////////////// + // Algorithm 2: breaking Minkowski sums into track line trips + // ///////////////////////////////////////////////////////////////////////// + + + // ///////////////////////////////////////////////////////////////////////// + // Algorithm 3: finding the boundary of the NFP from track line trips + // ///////////////////////////////////////////////////////////////////////// + + + for(auto& seq : seqlist) { + std::cout << "seqlist size: " << seq.size() << std::endl; + for(auto& s : seq) { + std::cout << (s.dir > 0 ? "" : "-") << s.eref.get().label << ", "; + } + std::cout << std::endl; + } + + auto& seq = seqlist.front(); + RawShape rsh; + Vertex top_nfp; + std::vector edgelist; edgelist.reserve(seq.size()); + for(auto& s : seq) { + edgelist.emplace_back(s.eref.get().e); + } + + __nfp::buildPolygon(edgelist, rsh, top_nfp); + + return Result(rsh, top_nfp); } // Specializable NFP implementation class. Specialize it if you have a faster diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp index 29d52c10f..b6d7fcdcf 100644 --- a/src/libnest2d/include/libnest2d/libnest2d.hpp +++ b/src/libnest2d/include/libnest2d/libnest2d.hpp @@ -1,862 +1,134 @@ #ifndef LIBNEST2D_HPP #define LIBNEST2D_HPP -#include -#include -#include -#include -#include -#include +// The type of backend should be set conditionally by the cmake configuriation +// for now we set it statically to clipper backend +#ifdef LIBNEST2D_GEOMETRIES_clipper +#include +#endif -#include +#ifdef LIBNEST2D_OPTIMIZER_nlopt +// We include the stock optimizers for local and global optimization +#include // Local subplex for NfpPlacer +#include // Genetic for min. bounding box +#endif + +#include +#include +#include +#include +#include +#include namespace libnest2d { -static const constexpr int BIN_ID_UNSET = -1; +using Point = PointImpl; +using Coord = TCoord; +using Box = _Box; +using Segment = _Segment; +using Circle = _Circle; -/** - * \brief An item to be placed on a bin. - * - * It holds a copy of the original shape object but supports move construction - * from the shape objects if its an rvalue reference. This way we can construct - * the items without the cost of copying a potentially large amount of input. - * - * The results of some calculations are cached for maintaining fast run times. - * For this reason, memory demands are much higher but this should pay off. - */ -template -class _Item { - using Coord = TCoord>; - using Vertex = TPoint; - using Box = _Box; +using Item = _Item; +using Rectangle = _Rectangle; +using PackGroup = _PackGroup; - using VertexConstIterator = typename TContour::const_iterator; +using FillerSelection = selections::_FillerSelection; +using FirstFitSelection = selections::_FirstFitSelection; +using DJDHeuristic = selections::_DJDHeuristic; - // The original shape that gets encapsulated. - RawShape sh_; +template // Generic placer for arbitrary bin types +using _NfpPlacer = placers::_NofitPolyPlacer; - // Transformation data - Vertex translation_{0, 0}; - Radians rotation_{0.0}; - Coord inflation_{0}; +// NfpPlacer is with Box bin +using NfpPlacer = _NfpPlacer; - // Info about whether the transformations will have to take place - // This is needed because if floating point is used, it is hard to say - // that a zero angle is not a rotation because of testing for equality. - bool has_rotation_ = false, has_translation_ = false, has_inflation_ = false; +// This supports only box shaped bins +using BottomLeftPlacer = placers::_BottomLeftPlacer; - // For caching the calculations as they can get pretty expensive. - mutable RawShape tr_cache_; - mutable bool tr_cache_valid_ = false; - mutable double area_cache_ = 0; - mutable bool area_cache_valid_ = false; - mutable RawShape inflate_cache_; - mutable bool inflate_cache_valid_ = false; +#ifdef LIBNEST2D_STATIC - enum class Convexity: char { - UNCHECKED, - C_TRUE, - C_FALSE - }; +extern template class _Nester; +extern template class _Nester; +extern template std::size_t _Nester::execute( + std::vector::iterator, std::vector::iterator); +extern template std::size_t _Nester::execute( + std::vector::iterator, std::vector::iterator); - mutable Convexity convexity_ = Convexity::UNCHECKED; - mutable VertexConstIterator rmt_; // rightmost top vertex - mutable VertexConstIterator lmb_; // leftmost bottom vertex - mutable bool rmt_valid_ = false, lmb_valid_ = false; - mutable struct BBCache { - Box bb; bool valid; - BBCache(): valid(false) {} - } bb_cache_; +#endif + +template +struct NestConfig { + typename Placer::Config placer_config; + typename Selector::Config selector_config; + using Placement = typename Placer::Config; + using Selection = typename Selector::Config; - int binid_{BIN_ID_UNSET}, priority_{0}; - bool fixed_{false}; - -public: - - /// The type of the shape which was handed over as the template argument. - using ShapeType = RawShape; - - /** - * \brief Iterator type for the outer vertices. - * - * Only const iterators can be used. The _Item type is not intended to - * modify the carried shapes from the outside. The main purpose of this type - * is to cache the calculation results from the various operators it - * supports. Giving out a non const iterator would make it impossible to - * perform correct cache invalidation. - */ - using Iterator = VertexConstIterator; - - /** - * @brief Get the orientation of the polygon. - * - * The orientation have to be specified as a specialization of the - * OrientationType struct which has a Value constant. - * - * @return The orientation type identifier for the _Item type. - */ - static BP2D_CONSTEXPR Orientation orientation() { - return OrientationType::Value; - } - - /** - * @brief Constructing an _Item form an existing raw shape. The shape will - * be copied into the _Item object. - * @param sh The original shape object. - */ - explicit inline _Item(const RawShape& sh): sh_(sh) {} - - /** - * @brief Construction of an item by moving the content of the raw shape, - * assuming that it supports move semantics. - * @param sh The original shape object. - */ - explicit inline _Item(RawShape&& sh): sh_(std::move(sh)) {} - - /** - * @brief Create an item from an initializer list. - * @param il The initializer list of vertices. - */ - inline _Item(const std::initializer_list< Vertex >& il): - sh_(sl::create(il)) {} - - inline _Item(const TContour& contour, - const THolesContainer& holes = {}): - sh_(sl::create(contour, holes)) {} - - inline _Item(TContour&& contour, - THolesContainer&& holes): - sh_(sl::create(std::move(contour), std::move(holes))) {} - - inline bool isFixed() const noexcept { return fixed_; } - inline void markAsFixed(bool fixed = true) { fixed_ = fixed; } - - inline void binId(int idx) { binid_ = idx; } - inline int binId() const noexcept { return binid_; } - - inline void priority(int p) { priority_ = p; } - inline int priority() const noexcept { return priority_; } - - /** - * @brief Convert the polygon to string representation. The format depends - * on the implementation of the polygon. - * @return - */ - inline std::string toString() const - { - return sl::toString(sh_); - } - - /// Iterator tho the first contour vertex in the polygon. - inline Iterator begin() const - { - return sl::cbegin(sh_); - } - - /// Alias to begin() - inline Iterator cbegin() const - { - return sl::cbegin(sh_); - } - - /// Iterator to the last contour vertex. - inline Iterator end() const - { - return sl::cend(sh_); - } - - /// Alias to end() - inline Iterator cend() const - { - return sl::cend(sh_); - } - - /** - * @brief Get a copy of an outer vertex within the carried shape. - * - * Note that the vertex considered here is taken from the original shape - * that this item is constructed from. This means that no transformation is - * applied to the shape in this call. - * - * @param idx The index of the requested vertex. - * @return A copy of the requested vertex. - */ - inline Vertex vertex(unsigned long idx) const - { - return sl::vertex(sh_, idx); - } - - /** - * @brief Modify a vertex. - * - * Note that this method will invalidate every cached calculation result - * including polygon offset and transformations. - * - * @param idx The index of the requested vertex. - * @param v The new vertex data. - */ - inline void setVertex(unsigned long idx, const Vertex& v ) - { - invalidateCache(); - sl::vertex(sh_, idx) = v; - } - - /** - * @brief Calculate the shape area. - * - * The method returns absolute value and does not reflect polygon - * orientation. The result is cached, subsequent calls will have very little - * cost. - * @return The shape area in floating point double precision. - */ - inline double area() const { - double ret ; - if(area_cache_valid_) ret = area_cache_; - else { - ret = sl::area(infaltedShape()); - area_cache_ = ret; - area_cache_valid_ = true; - } - return ret; - } - - inline bool isContourConvex() const { - bool ret = false; - - switch(convexity_) { - case Convexity::UNCHECKED: - ret = sl::isConvex(sl::contour(transformedShape())); - convexity_ = ret? Convexity::C_TRUE : Convexity::C_FALSE; - break; - case Convexity::C_TRUE: ret = true; break; - case Convexity::C_FALSE:; - } - - return ret; - } - - inline bool isHoleConvex(unsigned /*holeidx*/) const { - return false; - } - - inline bool areHolesConvex() const { - return false; - } - - /// The number of the outer ring vertices. - inline size_t vertexCount() const { - return sl::contourVertexCount(sh_); - } - - inline size_t holeCount() const { - return sl::holeCount(sh_); - } - - /** - * @brief isPointInside - * @param p - * @return - */ - inline bool isInside(const Vertex& p) const - { - return sl::isInside(p, transformedShape()); - } - - inline bool isInside(const _Item& sh) const - { - return sl::isInside(transformedShape(), sh.transformedShape()); - } - - inline bool isInside(const RawShape& sh) const - { - return sl::isInside(transformedShape(), sh); - } - - inline bool isInside(const _Box>& box) const; - inline bool isInside(const _Circle>& box) const; - - inline void translate(const Vertex& d) BP2D_NOEXCEPT - { - translation(translation() + d); - } - - inline void rotate(const Radians& rads) BP2D_NOEXCEPT - { - rotation(rotation() + rads); - } - - inline void inflation(Coord distance) BP2D_NOEXCEPT - { - inflation_ = distance; - has_inflation_ = true; - invalidateCache(); - } - - inline Coord inflation() const BP2D_NOEXCEPT { - return inflation_; - } - - inline void inflate(Coord distance) BP2D_NOEXCEPT - { - inflation(inflation() + distance); - } - - inline Radians rotation() const BP2D_NOEXCEPT - { - return rotation_; - } - - inline TPoint translation() const BP2D_NOEXCEPT - { - return translation_; - } - - inline void rotation(Radians rot) BP2D_NOEXCEPT - { - if(rotation_ != rot) { - rotation_ = rot; has_rotation_ = true; tr_cache_valid_ = false; - rmt_valid_ = false; lmb_valid_ = false; - bb_cache_.valid = false; - } - } - - inline void translation(const TPoint& tr) BP2D_NOEXCEPT - { - if(translation_ != tr) { - translation_ = tr; has_translation_ = true; tr_cache_valid_ = false; - //bb_cache_.valid = false; - } - } - - inline const RawShape& transformedShape() const - { - if(tr_cache_valid_) return tr_cache_; - - RawShape cpy = infaltedShape(); - if(has_rotation_) sl::rotate(cpy, rotation_); - if(has_translation_) sl::translate(cpy, translation_); - tr_cache_ = cpy; tr_cache_valid_ = true; - rmt_valid_ = false; lmb_valid_ = false; - - return tr_cache_; - } - - inline operator RawShape() const - { - return transformedShape(); - } - - inline const RawShape& rawShape() const BP2D_NOEXCEPT - { - return sh_; - } - - inline void resetTransformation() BP2D_NOEXCEPT - { - has_translation_ = false; has_rotation_ = false; has_inflation_ = false; - invalidateCache(); - } - - inline Box boundingBox() const { - if(!bb_cache_.valid) { - if(!has_rotation_) - bb_cache_.bb = sl::boundingBox(infaltedShape()); - else { - // TODO make sure this works - auto rotsh = infaltedShape(); - sl::rotate(rotsh, rotation_); - bb_cache_.bb = sl::boundingBox(rotsh); - } - bb_cache_.valid = true; - } - - auto &bb = bb_cache_.bb; auto &tr = translation_; - return {bb.minCorner() + tr, bb.maxCorner() + tr }; - } - - inline Vertex referenceVertex() const { - return rightmostTopVertex(); - } - - inline Vertex rightmostTopVertex() const { - if(!rmt_valid_ || !tr_cache_valid_) { // find max x and max y vertex - auto& tsh = transformedShape(); - rmt_ = std::max_element(sl::cbegin(tsh), sl::cend(tsh), vsort); - rmt_valid_ = true; - } - return *rmt_; - } - - inline Vertex leftmostBottomVertex() const { - if(!lmb_valid_ || !tr_cache_valid_) { // find min x and min y vertex - auto& tsh = transformedShape(); - lmb_ = std::min_element(sl::cbegin(tsh), sl::cend(tsh), vsort); - lmb_valid_ = true; - } - return *lmb_; - } - - //Static methods: - - inline static bool intersects(const _Item& sh1, const _Item& sh2) - { - return sl::intersects(sh1.transformedShape(), - sh2.transformedShape()); - } - - inline static bool touches(const _Item& sh1, const _Item& sh2) - { - return sl::touches(sh1.transformedShape(), - sh2.transformedShape()); - } - -private: - - inline const RawShape& infaltedShape() const { - if(has_inflation_ ) { - if(inflate_cache_valid_) return inflate_cache_; - - inflate_cache_ = sh_; - sl::offset(inflate_cache_, inflation_); - inflate_cache_valid_ = true; - return inflate_cache_; - } - return sh_; - } - - inline void invalidateCache() const BP2D_NOEXCEPT - { - tr_cache_valid_ = false; - lmb_valid_ = false; rmt_valid_ = false; - area_cache_valid_ = false; - inflate_cache_valid_ = false; - bb_cache_.valid = false; - convexity_ = Convexity::UNCHECKED; - } - - static inline bool vsort(const Vertex& v1, const Vertex& v2) - { - TCompute x1 = getX(v1), x2 = getX(v2); - TCompute y1 = getY(v1), y2 = getY(v2); - return y1 == y2 ? x1 < x2 : y1 < y2; - } + NestConfig() = default; + NestConfig(const typename Placer::Config &cfg) : placer_config{cfg} {} + NestConfig(const typename Selector::Config &cfg) : selector_config{cfg} {} + NestConfig(const typename Placer::Config & pcfg, + const typename Selector::Config &scfg) + : placer_config{pcfg}, selector_config{scfg} {} }; -/** - * \brief Subclass of _Item for regular rectangle items. - */ -template -class _Rectangle: public _Item { - using _Item::vertex; - using TO = Orientation; -public: - - using Unit = TCoord>; - - template::Value> - inline _Rectangle(Unit width, Unit height, - // disable this ctor if o != CLOCKWISE - enable_if_t< o == TO::CLOCKWISE, int> = 0 ): - _Item( sl::create( { - {0, 0}, - {0, height}, - {width, height}, - {width, 0}, - {0, 0} - } )) - { - } - - template::Value> - inline _Rectangle(Unit width, Unit height, - // disable this ctor if o != COUNTER_CLOCKWISE - enable_if_t< o == TO::COUNTER_CLOCKWISE, int> = 0 ): - _Item( sl::create( { - {0, 0}, - {width, 0}, - {width, height}, - {0, height}, - {0, 0} - } )) - { - } - - inline Unit width() const BP2D_NOEXCEPT { - return getX(vertex(2)); - } - - inline Unit height() const BP2D_NOEXCEPT { - return getY(vertex(2)); - } +struct NestControl { + ProgressFunction progressfn; + StopCondition stopcond = []{ return false; }; + + NestControl() = default; + NestControl(ProgressFunction pr) : progressfn{std::move(pr)} {} + NestControl(StopCondition sc) : stopcond{std::move(sc)} {} + NestControl(ProgressFunction pr, StopCondition sc) + : progressfn{std::move(pr)}, stopcond{std::move(sc)} + {} }; -template -inline bool _Item::isInside(const _Box>& box) const { - return sl::isInside(boundingBox(), box); +template::iterator> +std::size_t nest(Iterator from, Iterator to, + const typename Placer::BinType & bin, + Coord dist = 0, + const NestConfig &cfg = {}, + NestControl ctl = {}) +{ + _Nester nester{bin, dist, cfg.placer_config, cfg.selector_config}; + if(ctl.progressfn) nester.progressIndicator(ctl.progressfn); + if(ctl.stopcond) nester.stopCondition(ctl.stopcond); + return nester.execute(from, to); } -template inline bool -_Item::isInside(const _Circle>& circ) const { - return sl::isInside(transformedShape(), circ); +#ifdef LIBNEST2D_STATIC + +extern template class _Nester; +extern template class _Nester; +extern template std::size_t nest(std::vector::iterator from, + std::vector::iterator to, + const Box & bin, + Coord dist, + const NestConfig &cfg, + NestControl ctl); +extern template std::size_t nest(std::vector::iterator from, + std::vector::iterator to, + const Box & bin, + Coord dist, + const NestConfig &cfg, + NestControl ctl); + +#endif + +template> +std::size_t nest(Container&& cont, + const typename Placer::BinType & bin, + Coord dist = 0, + const NestConfig &cfg = {}, + NestControl ctl = {}) +{ + return nest(cont.begin(), cont.end(), bin, dist, cfg, ctl); } -template using _ItemRef = std::reference_wrapper<_Item>; -template using _ItemGroup = std::vector<_ItemRef>; - -/** - * \brief A list of packed item vectors. Each vector represents a bin. - */ -template -using _PackGroup = std::vector>>; - -template -struct ConstItemRange { - Iterator from; - Iterator to; - bool valid = false; - - ConstItemRange() = default; - ConstItemRange(Iterator f, Iterator t): from(f), to(t), valid(true) {} -}; - -template -inline ConstItemRange -rem(typename Container::const_iterator it, const Container& cont) { - return {std::next(it), cont.end()}; -} - -/** - * \brief A wrapper interface (trait) class for any placement strategy provider. - * - * If a client wants to use its own placement algorithm, all it has to do is to - * specialize this class template and define all the ten methods it has. It can - * use the strategies::PlacerBoilerplace class for creating a new placement - * strategy where only the constructor and the trypack method has to be provided - * and it will work out of the box. - */ -template -class PlacementStrategyLike { - PlacementStrategy impl_; -public: - - using RawShape = typename PlacementStrategy::ShapeType; - - /// The item type that the placer works with. - using Item = _Item; - - /// The placer's config type. Should be a simple struct but can be anything. - using Config = typename PlacementStrategy::Config; - - /** - * \brief The type of the bin that the placer works with. - * - * Can be a box or an arbitrary shape or just a width or height without a - * second dimension if an infinite bin is considered. - */ - using BinType = typename PlacementStrategy::BinType; - - /** - * \brief Pack result that can be used to accept or discard it. See trypack - * method. - */ - using PackResult = typename PlacementStrategy::PackResult; - - using ItemGroup = _ItemGroup; - using DefaultIterator = typename ItemGroup::const_iterator; - - /** - * @brief Constructor taking the bin and an optional configuration. - * @param bin The bin object whose type is defined by the placement strategy. - * @param config The configuration for the particular placer. - */ - explicit PlacementStrategyLike(const BinType& bin, - const Config& config = Config()): - impl_(bin) - { - configure(config); - } - - /** - * @brief Provide a different configuration for the placer. - * - * Note that it depends on the particular placer implementation how it - * reacts to config changes in the middle of a calculation. - * - * @param config The configuration object defined by the placement strategy. - */ - inline void configure(const Config& config) { impl_.configure(config); } - - /** - * Try to pack an item with a result object that contains the packing - * information for later accepting it. - * - * \param item_store A container of items that are intended to be packed - * later. Can be used by the placer to switch tactics. When it's knows that - * many items will come a greedy strategy may not be the best. - * \param from The iterator to the item from which the packing should start, - * including the pointed item - * \param count How many items should be packed. If the value is 1, than - * just the item pointed to by "from" argument should be packed. - */ - template - inline PackResult trypack( - Item& item, - const ConstItemRange& remaining = ConstItemRange()) - { - return impl_.trypack(item, remaining); - } - - /** - * @brief A method to accept a previously tried item (or items). - * - * If the pack result is a failure the method should ignore it. - * @param r The result of a previous trypack call. - */ - inline void accept(PackResult& r) { impl_.accept(r); } - - /** - * @brief pack Try to pack and immediately accept it on success. - * - * A default implementation would be to call - * { auto&& r = trypack(...); accept(r); return r; } but we should let the - * implementor of the placement strategy to harvest any optimizations from - * the absence of an intermediate step. The above version can still be used - * in the implementation. - * - * @param item The item to pack. - * @return Returns true if the item was packed or false if it could not be - * packed. - */ - template> - inline bool pack( - Item& item, - const Range& remaining = Range()) - { - return impl_.pack(item, remaining); - } - - /** - * This method makes possible to "preload" some items into the placer. It - * will not move these items but will consider them as already packed. - */ - inline void preload(const ItemGroup& packeditems) - { - impl_.preload(packeditems); - } - - /// Unpack the last element (remove it from the list of packed items). - inline void unpackLast() { impl_.unpackLast(); } - - /// Get the bin object. - inline const BinType& bin() const { return impl_.bin(); } - - /// Set a new bin object. - inline void bin(const BinType& bin) { impl_.bin(bin); } - - /// Get the packed items. - inline ItemGroup getItems() { return impl_.getItems(); } - - /// Clear the packed items so a new session can be started. - inline void clearItems() { impl_.clearItems(); } - - inline double filledArea() const { return impl_.filledArea(); } - -}; - -// The progress function will be called with the number of placed items -using ProgressFunction = std::function; -using StopCondition = std::function; - -/** - * A wrapper interface (trait) class for any selections strategy provider. - */ -template -class SelectionStrategyLike { - SelectionStrategy impl_; -public: - using RawShape = typename SelectionStrategy::ShapeType; - using Item = _Item; - using PackGroup = _PackGroup; - using Config = typename SelectionStrategy::Config; - - - /** - * @brief Provide a different configuration for the selection strategy. - * - * Note that it depends on the particular placer implementation how it - * reacts to config changes in the middle of a calculation. - * - * @param config The configuration object defined by the selection strategy. - */ - inline void configure(const Config& config) { - impl_.configure(config); - } - - /** - * @brief A function callback which should be called whenever an item or - * a group of items where successfully packed. - * @param fn A function callback object taking one unsigned integer as the - * number of the remaining items to pack. - */ - void progressIndicator(ProgressFunction fn) { impl_.progressIndicator(fn); } - - void stopCondition(StopCondition cond) { impl_.stopCondition(cond); } - - /** - * \brief A method to start the calculation on the input sequence. - * - * \tparam TPlacer The only mandatory template parameter is the type of - * placer compatible with the PlacementStrategyLike interface. - * - * \param first, last The first and last iterator if the input sequence. It - * can be only an iterator of a type convertible to Item. - * \param bin. The shape of the bin. It has to be supported by the placement - * strategy. - * \param An optional config object for the placer. - */ - template::BinType, - class PConfig = typename PlacementStrategyLike::Config> - inline void packItems( - TIterator first, - TIterator last, - TBin&& bin, - PConfig&& config = PConfig() ) - { - impl_.template packItems(first, last, - std::forward(bin), - std::forward(config)); - } - - /** - * @brief Get the items for a particular bin. - * @param binIndex The index of the requested bin. - * @return Returns a list of all items packed into the requested bin. - */ - inline const PackGroup& getResult() const { - return impl_.getResult(); - } - - void clear() { impl_.clear(); } -}; - -/** - * The _Nester is the front-end class for the libnest2d library. It takes the - * input items and changes their transformations to be inside the provided bin. - */ -template -class _Nester { - using TSel = SelectionStrategyLike; - TSel selector_; -public: - using Item = typename PlacementStrategy::Item; - using ShapeType = typename Item::ShapeType; - using ItemRef = std::reference_wrapper; - using TPlacer = PlacementStrategyLike; - using BinType = typename TPlacer::BinType; - using PlacementConfig = typename TPlacer::Config; - using SelectionConfig = typename TSel::Config; - using Coord = TCoord>; - using PackGroup = _PackGroup; - using ResultType = PackGroup; - -private: - BinType bin_; - PlacementConfig pconfig_; - Coord min_obj_distance_; - - using SItem = typename SelectionStrategy::Item; - using TPItem = remove_cvref_t; - using TSItem = remove_cvref_t; - - StopCondition stopfn_; - - template using TVal = remove_ref_t; - - template - using ItemIteratorOnly = - enable_if_t&, TPItem&>::value, Out>; - -public: - - /** - * \brief Constructor taking the bin as the only mandatory parameter. - * - * \param bin The bin shape that will be used by the placers. The type - * of the bin should be one that is supported by the placer type. - */ - template - _Nester(TBinType&& bin, Coord min_obj_distance = 0, - const PConf& pconfig = PConf(), const SConf& sconfig = SConf()): - bin_(std::forward(bin)), - pconfig_(pconfig), - min_obj_distance_(min_obj_distance) - { - static_assert( std::is_same::value, - "Incompatible placement and selection strategy!"); - - selector_.configure(sconfig); - } - - void configure(const PlacementConfig& pconf) { pconfig_ = pconf; } - void configure(const SelectionConfig& sconf) { selector_.configure(sconf); } - void configure(const PlacementConfig& pconf, const SelectionConfig& sconf) - { - pconfig_ = pconf; - selector_.configure(sconf); - } - void configure(const SelectionConfig& sconf, const PlacementConfig& pconf) - { - pconfig_ = pconf; - selector_.configure(sconf); - } - - /** - * \brief Arrange an input sequence of _Item-s. - * - * To get the result, call the translation(), rotation() and binId() - * methods of each item. If only the transformed polygon is needed, call - * transformedShape() to get the properly transformed shapes. - * - * The number of groups in the pack group is the number of bins opened by - * the selection algorithm. - */ - template - inline ItemIteratorOnly execute(It from, It to) - { - auto infl = static_cast(std::ceil(min_obj_distance_/2.0)); - if(infl > 0) std::for_each(from, to, [this, infl](Item& item) { - item.inflate(infl); - }); - - selector_.template packItems( - from, to, bin_, pconfig_); - - if(min_obj_distance_ > 0) std::for_each(from, to, [infl](Item& item) { - item.inflate(-infl); - }); - } - - /// Set a progress indicator function object for the selector. - inline _Nester& progressIndicator(ProgressFunction func) - { - selector_.progressIndicator(func); return *this; - } - - /// Set a predicate to tell when to abort nesting. - inline _Nester& stopCondition(StopCondition fn) - { - stopfn_ = fn; selector_.stopCondition(fn); return *this; - } - - inline const PackGroup& lastResult() const - { - return selector_.getResult(); - } -}; - } #endif // LIBNEST2D_HPP diff --git a/src/libnest2d/include/libnest2d/nester.hpp b/src/libnest2d/include/libnest2d/nester.hpp new file mode 100644 index 000000000..2f207d526 --- /dev/null +++ b/src/libnest2d/include/libnest2d/nester.hpp @@ -0,0 +1,869 @@ +#ifndef NESTER_HPP +#define NESTER_HPP + +#include +#include +#include +#include +#include +#include + +#include + +namespace libnest2d { + +static const constexpr int BIN_ID_UNSET = -1; + +/** + * \brief An item to be placed on a bin. + * + * It holds a copy of the original shape object but supports move construction + * from the shape objects if its an rvalue reference. This way we can construct + * the items without the cost of copying a potentially large amount of input. + * + * The results of some calculations are cached for maintaining fast run times. + * For this reason, memory demands are much higher but this should pay off. + */ +template +class _Item { + using Coord = TCoord>; + using Vertex = TPoint; + using Box = _Box; + + using VertexConstIterator = typename TContour::const_iterator; + + // The original shape that gets encapsulated. + RawShape sh_; + + // Transformation data + Vertex translation_{0, 0}; + Radians rotation_{0.0}; + Coord inflation_{0}; + + // Info about whether the transformations will have to take place + // This is needed because if floating point is used, it is hard to say + // that a zero angle is not a rotation because of testing for equality. + bool has_rotation_ = false, has_translation_ = false, has_inflation_ = false; + + // For caching the calculations as they can get pretty expensive. + mutable RawShape tr_cache_; + mutable bool tr_cache_valid_ = false; + mutable double area_cache_ = 0; + mutable bool area_cache_valid_ = false; + mutable RawShape inflate_cache_; + mutable bool inflate_cache_valid_ = false; + + enum class Convexity: char { + UNCHECKED, + C_TRUE, + C_FALSE + }; + + mutable Convexity convexity_ = Convexity::UNCHECKED; + mutable VertexConstIterator rmt_; // rightmost top vertex + mutable VertexConstIterator lmb_; // leftmost bottom vertex + mutable bool rmt_valid_ = false, lmb_valid_ = false; + mutable struct BBCache { + Box bb; bool valid; + BBCache(): valid(false) {} + } bb_cache_; + + int binid_{BIN_ID_UNSET}, priority_{0}; + bool fixed_{false}; + +public: + + /// The type of the shape which was handed over as the template argument. + using ShapeType = RawShape; + + /** + * \brief Iterator type for the outer vertices. + * + * Only const iterators can be used. The _Item type is not intended to + * modify the carried shapes from the outside. The main purpose of this type + * is to cache the calculation results from the various operators it + * supports. Giving out a non const iterator would make it impossible to + * perform correct cache invalidation. + */ + using Iterator = VertexConstIterator; + + /** + * @brief Get the orientation of the polygon. + * + * The orientation have to be specified as a specialization of the + * OrientationType struct which has a Value constant. + * + * @return The orientation type identifier for the _Item type. + */ + static BP2D_CONSTEXPR Orientation orientation() { + return OrientationType::Value; + } + + /** + * @brief Constructing an _Item form an existing raw shape. The shape will + * be copied into the _Item object. + * @param sh The original shape object. + */ + explicit inline _Item(const RawShape& sh): sh_(sh) {} + + /** + * @brief Construction of an item by moving the content of the raw shape, + * assuming that it supports move semantics. + * @param sh The original shape object. + */ + explicit inline _Item(RawShape&& sh): sh_(std::move(sh)) {} + + /** + * @brief Create an item from an initializer list. + * @param il The initializer list of vertices. + */ + inline _Item(const std::initializer_list< Vertex >& il): + sh_(sl::create(il)) {} + + inline _Item(const TContour& contour, + const THolesContainer& holes = {}): + sh_(sl::create(contour, holes)) {} + + inline _Item(TContour&& contour, + THolesContainer&& holes): + sh_(sl::create(std::move(contour), std::move(holes))) {} + + inline bool isFixed() const noexcept { return fixed_; } + inline void markAsFixedInBin(int binid) + { + fixed_ = binid >= 0; + binid_ = binid; + } + + inline void binId(int idx) { binid_ = idx; } + inline int binId() const noexcept { return binid_; } + + inline void priority(int p) { priority_ = p; } + inline int priority() const noexcept { return priority_; } + + /** + * @brief Convert the polygon to string representation. The format depends + * on the implementation of the polygon. + * @return + */ + inline std::string toString() const + { + return sl::toString(sh_); + } + + /// Iterator tho the first contour vertex in the polygon. + inline Iterator begin() const + { + return sl::cbegin(sh_); + } + + /// Alias to begin() + inline Iterator cbegin() const + { + return sl::cbegin(sh_); + } + + /// Iterator to the last contour vertex. + inline Iterator end() const + { + return sl::cend(sh_); + } + + /// Alias to end() + inline Iterator cend() const + { + return sl::cend(sh_); + } + + /** + * @brief Get a copy of an outer vertex within the carried shape. + * + * Note that the vertex considered here is taken from the original shape + * that this item is constructed from. This means that no transformation is + * applied to the shape in this call. + * + * @param idx The index of the requested vertex. + * @return A copy of the requested vertex. + */ + inline Vertex vertex(unsigned long idx) const + { + return sl::vertex(sh_, idx); + } + + /** + * @brief Modify a vertex. + * + * Note that this method will invalidate every cached calculation result + * including polygon offset and transformations. + * + * @param idx The index of the requested vertex. + * @param v The new vertex data. + */ + inline void setVertex(unsigned long idx, const Vertex& v ) + { + invalidateCache(); + sl::vertex(sh_, idx) = v; + } + + /** + * @brief Calculate the shape area. + * + * The method returns absolute value and does not reflect polygon + * orientation. The result is cached, subsequent calls will have very little + * cost. + * @return The shape area in floating point double precision. + */ + inline double area() const { + double ret ; + if(area_cache_valid_) ret = area_cache_; + else { + ret = sl::area(infaltedShape()); + area_cache_ = ret; + area_cache_valid_ = true; + } + return ret; + } + + inline bool isContourConvex() const { + bool ret = false; + + switch(convexity_) { + case Convexity::UNCHECKED: + ret = sl::isConvex(sl::contour(transformedShape())); + convexity_ = ret? Convexity::C_TRUE : Convexity::C_FALSE; + break; + case Convexity::C_TRUE: ret = true; break; + case Convexity::C_FALSE:; + } + + return ret; + } + + inline bool isHoleConvex(unsigned /*holeidx*/) const { + return false; + } + + inline bool areHolesConvex() const { + return false; + } + + /// The number of the outer ring vertices. + inline size_t vertexCount() const { + return sl::contourVertexCount(sh_); + } + + inline size_t holeCount() const { + return sl::holeCount(sh_); + } + + /** + * @brief isPointInside + * @param p + * @return + */ + inline bool isInside(const Vertex& p) const + { + return sl::isInside(p, transformedShape()); + } + + inline bool isInside(const _Item& sh) const + { + return sl::isInside(transformedShape(), sh.transformedShape()); + } + + inline bool isInside(const RawShape& sh) const + { + return sl::isInside(transformedShape(), sh); + } + + inline bool isInside(const _Box>& box) const; + inline bool isInside(const _Circle>& box) const; + + inline void translate(const Vertex& d) BP2D_NOEXCEPT + { + translation(translation() + d); + } + + inline void rotate(const Radians& rads) BP2D_NOEXCEPT + { + rotation(rotation() + rads); + } + + inline void inflation(Coord distance) BP2D_NOEXCEPT + { + inflation_ = distance; + has_inflation_ = true; + invalidateCache(); + } + + inline Coord inflation() const BP2D_NOEXCEPT { + return inflation_; + } + + inline void inflate(Coord distance) BP2D_NOEXCEPT + { + inflation(inflation() + distance); + } + + inline Radians rotation() const BP2D_NOEXCEPT + { + return rotation_; + } + + inline TPoint translation() const BP2D_NOEXCEPT + { + return translation_; + } + + inline void rotation(Radians rot) BP2D_NOEXCEPT + { + if(rotation_ != rot) { + rotation_ = rot; has_rotation_ = true; tr_cache_valid_ = false; + rmt_valid_ = false; lmb_valid_ = false; + bb_cache_.valid = false; + } + } + + inline void translation(const TPoint& tr) BP2D_NOEXCEPT + { + if(translation_ != tr) { + translation_ = tr; has_translation_ = true; tr_cache_valid_ = false; + //bb_cache_.valid = false; + } + } + + inline const RawShape& transformedShape() const + { + if(tr_cache_valid_) return tr_cache_; + + RawShape cpy = infaltedShape(); + if(has_rotation_) sl::rotate(cpy, rotation_); + if(has_translation_) sl::translate(cpy, translation_); + tr_cache_ = cpy; tr_cache_valid_ = true; + rmt_valid_ = false; lmb_valid_ = false; + + return tr_cache_; + } + + inline operator RawShape() const + { + return transformedShape(); + } + + inline const RawShape& rawShape() const BP2D_NOEXCEPT + { + return sh_; + } + + inline void resetTransformation() BP2D_NOEXCEPT + { + has_translation_ = false; has_rotation_ = false; has_inflation_ = false; + invalidateCache(); + } + + inline Box boundingBox() const { + if(!bb_cache_.valid) { + if(!has_rotation_) + bb_cache_.bb = sl::boundingBox(infaltedShape()); + else { + // TODO make sure this works + auto rotsh = infaltedShape(); + sl::rotate(rotsh, rotation_); + bb_cache_.bb = sl::boundingBox(rotsh); + } + bb_cache_.valid = true; + } + + auto &bb = bb_cache_.bb; auto &tr = translation_; + return {bb.minCorner() + tr, bb.maxCorner() + tr }; + } + + inline Vertex referenceVertex() const { + return rightmostTopVertex(); + } + + inline Vertex rightmostTopVertex() const { + if(!rmt_valid_ || !tr_cache_valid_) { // find max x and max y vertex + auto& tsh = transformedShape(); + rmt_ = std::max_element(sl::cbegin(tsh), sl::cend(tsh), vsort); + rmt_valid_ = true; + } + return *rmt_; + } + + inline Vertex leftmostBottomVertex() const { + if(!lmb_valid_ || !tr_cache_valid_) { // find min x and min y vertex + auto& tsh = transformedShape(); + lmb_ = std::min_element(sl::cbegin(tsh), sl::cend(tsh), vsort); + lmb_valid_ = true; + } + return *lmb_; + } + + //Static methods: + + inline static bool intersects(const _Item& sh1, const _Item& sh2) + { + return sl::intersects(sh1.transformedShape(), + sh2.transformedShape()); + } + + inline static bool touches(const _Item& sh1, const _Item& sh2) + { + return sl::touches(sh1.transformedShape(), + sh2.transformedShape()); + } + +private: + + inline const RawShape& infaltedShape() const { + if(has_inflation_ ) { + if(inflate_cache_valid_) return inflate_cache_; + + inflate_cache_ = sh_; + sl::offset(inflate_cache_, inflation_); + inflate_cache_valid_ = true; + return inflate_cache_; + } + return sh_; + } + + inline void invalidateCache() const BP2D_NOEXCEPT + { + tr_cache_valid_ = false; + lmb_valid_ = false; rmt_valid_ = false; + area_cache_valid_ = false; + inflate_cache_valid_ = false; + bb_cache_.valid = false; + convexity_ = Convexity::UNCHECKED; + } + + static inline bool vsort(const Vertex& v1, const Vertex& v2) + { + TCompute x1 = getX(v1), x2 = getX(v2); + TCompute y1 = getY(v1), y2 = getY(v2); + return y1 == y2 ? x1 < x2 : y1 < y2; + } +}; + +/** + * \brief Subclass of _Item for regular rectangle items. + */ +template +class _Rectangle: public _Item { + using _Item::vertex; + using TO = Orientation; +public: + + using Unit = TCoord>; + + template::Value> + inline _Rectangle(Unit width, Unit height, + // disable this ctor if o != CLOCKWISE + enable_if_t< o == TO::CLOCKWISE, int> = 0 ): + _Item( sl::create( { + {0, 0}, + {0, height}, + {width, height}, + {width, 0}, + {0, 0} + } )) + { + } + + template::Value> + inline _Rectangle(Unit width, Unit height, + // disable this ctor if o != COUNTER_CLOCKWISE + enable_if_t< o == TO::COUNTER_CLOCKWISE, int> = 0 ): + _Item( sl::create( { + {0, 0}, + {width, 0}, + {width, height}, + {0, height}, + {0, 0} + } )) + { + } + + inline Unit width() const BP2D_NOEXCEPT { + return getX(vertex(2)); + } + + inline Unit height() const BP2D_NOEXCEPT { + return getY(vertex(2)); + } +}; + +template +inline bool _Item::isInside(const _Box>& box) const { + return sl::isInside(boundingBox(), box); +} + +template inline bool +_Item::isInside(const _Circle>& circ) const { + return sl::isInside(transformedShape(), circ); +} + +template using _ItemRef = std::reference_wrapper<_Item>; +template using _ItemGroup = std::vector<_ItemRef>; + +/** + * \brief A list of packed item vectors. Each vector represents a bin. + */ +template +using _PackGroup = std::vector>>; + +template +struct ConstItemRange { + Iterator from; + Iterator to; + bool valid = false; + + ConstItemRange() = default; + ConstItemRange(Iterator f, Iterator t): from(f), to(t), valid(true) {} +}; + +template +inline ConstItemRange +rem(typename Container::const_iterator it, const Container& cont) { + return {std::next(it), cont.end()}; +} + +/** + * \brief A wrapper interface (trait) class for any placement strategy provider. + * + * If a client wants to use its own placement algorithm, all it has to do is to + * specialize this class template and define all the ten methods it has. It can + * use the strategies::PlacerBoilerplace class for creating a new placement + * strategy where only the constructor and the trypack method has to be provided + * and it will work out of the box. + */ +template +class PlacementStrategyLike { + PlacementStrategy impl_; +public: + + using RawShape = typename PlacementStrategy::ShapeType; + + /// The item type that the placer works with. + using Item = _Item; + + /// The placer's config type. Should be a simple struct but can be anything. + using Config = typename PlacementStrategy::Config; + + /** + * \brief The type of the bin that the placer works with. + * + * Can be a box or an arbitrary shape or just a width or height without a + * second dimension if an infinite bin is considered. + */ + using BinType = typename PlacementStrategy::BinType; + + /** + * \brief Pack result that can be used to accept or discard it. See trypack + * method. + */ + using PackResult = typename PlacementStrategy::PackResult; + + using ItemGroup = _ItemGroup; + using DefaultIterator = typename ItemGroup::const_iterator; + + /** + * @brief Constructor taking the bin and an optional configuration. + * @param bin The bin object whose type is defined by the placement strategy. + * @param config The configuration for the particular placer. + */ + explicit PlacementStrategyLike(const BinType& bin, + const Config& config = Config()): + impl_(bin) + { + configure(config); + } + + /** + * @brief Provide a different configuration for the placer. + * + * Note that it depends on the particular placer implementation how it + * reacts to config changes in the middle of a calculation. + * + * @param config The configuration object defined by the placement strategy. + */ + inline void configure(const Config& config) { impl_.configure(config); } + + /** + * Try to pack an item with a result object that contains the packing + * information for later accepting it. + * + * \param item_store A container of items that are intended to be packed + * later. Can be used by the placer to switch tactics. When it's knows that + * many items will come a greedy strategy may not be the best. + * \param from The iterator to the item from which the packing should start, + * including the pointed item + * \param count How many items should be packed. If the value is 1, than + * just the item pointed to by "from" argument should be packed. + */ + template + inline PackResult trypack( + Item& item, + const ConstItemRange& remaining = ConstItemRange()) + { + return impl_.trypack(item, remaining); + } + + /** + * @brief A method to accept a previously tried item (or items). + * + * If the pack result is a failure the method should ignore it. + * @param r The result of a previous trypack call. + */ + inline void accept(PackResult& r) { impl_.accept(r); } + + /** + * @brief pack Try to pack and immediately accept it on success. + * + * A default implementation would be to call + * { auto&& r = trypack(...); accept(r); return r; } but we should let the + * implementor of the placement strategy to harvest any optimizations from + * the absence of an intermediate step. The above version can still be used + * in the implementation. + * + * @param item The item to pack. + * @return Returns true if the item was packed or false if it could not be + * packed. + */ + template> + inline bool pack( + Item& item, + const Range& remaining = Range()) + { + return impl_.pack(item, remaining); + } + + /** + * This method makes possible to "preload" some items into the placer. It + * will not move these items but will consider them as already packed. + */ + inline void preload(const ItemGroup& packeditems) + { + impl_.preload(packeditems); + } + + /// Unpack the last element (remove it from the list of packed items). + inline void unpackLast() { impl_.unpackLast(); } + + /// Get the bin object. + inline const BinType& bin() const { return impl_.bin(); } + + /// Set a new bin object. + inline void bin(const BinType& bin) { impl_.bin(bin); } + + /// Get the packed items. + inline ItemGroup getItems() { return impl_.getItems(); } + + /// Clear the packed items so a new session can be started. + inline void clearItems() { impl_.clearItems(); } + + inline double filledArea() const { return impl_.filledArea(); } + +}; + +// The progress function will be called with the number of placed items +using ProgressFunction = std::function; +using StopCondition = std::function; + +/** + * A wrapper interface (trait) class for any selections strategy provider. + */ +template +class SelectionStrategyLike { + SelectionStrategy impl_; +public: + using RawShape = typename SelectionStrategy::ShapeType; + using Item = _Item; + using PackGroup = _PackGroup; + using Config = typename SelectionStrategy::Config; + + + /** + * @brief Provide a different configuration for the selection strategy. + * + * Note that it depends on the particular placer implementation how it + * reacts to config changes in the middle of a calculation. + * + * @param config The configuration object defined by the selection strategy. + */ + inline void configure(const Config& config) { + impl_.configure(config); + } + + /** + * @brief A function callback which should be called whenever an item or + * a group of items where successfully packed. + * @param fn A function callback object taking one unsigned integer as the + * number of the remaining items to pack. + */ + void progressIndicator(ProgressFunction fn) { impl_.progressIndicator(fn); } + + void stopCondition(StopCondition cond) { impl_.stopCondition(cond); } + + /** + * \brief A method to start the calculation on the input sequence. + * + * \tparam TPlacer The only mandatory template parameter is the type of + * placer compatible with the PlacementStrategyLike interface. + * + * \param first, last The first and last iterator if the input sequence. It + * can be only an iterator of a type convertible to Item. + * \param bin. The shape of the bin. It has to be supported by the placement + * strategy. + * \param An optional config object for the placer. + */ + template::BinType, + class PConfig = typename PlacementStrategyLike::Config> + inline void packItems( + TIterator first, + TIterator last, + TBin&& bin, + PConfig&& config = PConfig() ) + { + impl_.template packItems(first, last, + std::forward(bin), + std::forward(config)); + } + + /** + * @brief Get the items for a particular bin. + * @param binIndex The index of the requested bin. + * @return Returns a list of all items packed into the requested bin. + */ + inline const PackGroup& getResult() const { + return impl_.getResult(); + } + + void clear() { impl_.clear(); } +}; + +/** + * The _Nester is the front-end class for the libnest2d library. It takes the + * input items and changes their transformations to be inside the provided bin. + */ +template +class _Nester { + using TSel = SelectionStrategyLike; + TSel selector_; + +public: + using Item = typename PlacementStrategy::Item; + using ShapeType = typename Item::ShapeType; + using ItemRef = std::reference_wrapper; + using TPlacer = PlacementStrategyLike; + using BinType = typename TPlacer::BinType; + using PlacementConfig = typename TPlacer::Config; + using SelectionConfig = typename TSel::Config; + using Coord = TCoord>; + using PackGroup = _PackGroup; + using ResultType = PackGroup; + +private: + BinType bin_; + PlacementConfig pconfig_; + Coord min_obj_distance_; + + using SItem = typename SelectionStrategy::Item; + using TPItem = remove_cvref_t; + using TSItem = remove_cvref_t; + + StopCondition stopfn_; + + template using TVal = remove_ref_t; + + template + using ItemIteratorOnly = + enable_if_t&, TPItem&>::value, Out>; + +public: + + /** + * \brief Constructor taking the bin as the only mandatory parameter. + * + * \param bin The bin shape that will be used by the placers. The type + * of the bin should be one that is supported by the placer type. + */ + template + _Nester(TBinType&& bin, Coord min_obj_distance = 0, + const PConf& pconfig = PConf(), const SConf& sconfig = SConf()): + bin_(std::forward(bin)), + pconfig_(pconfig), + min_obj_distance_(min_obj_distance) + { + static_assert( std::is_same::value, + "Incompatible placement and selection strategy!"); + + selector_.configure(sconfig); + } + + void configure(const PlacementConfig& pconf) { pconfig_ = pconf; } + void configure(const SelectionConfig& sconf) { selector_.configure(sconf); } + void configure(const PlacementConfig& pconf, const SelectionConfig& sconf) + { + pconfig_ = pconf; + selector_.configure(sconf); + } + void configure(const SelectionConfig& sconf, const PlacementConfig& pconf) + { + pconfig_ = pconf; + selector_.configure(sconf); + } + + /** + * \brief Arrange an input sequence of _Item-s. + * + * To get the result, call the translation(), rotation() and binId() + * methods of each item. If only the transformed polygon is needed, call + * transformedShape() to get the properly transformed shapes. + * + * The number of groups in the pack group is the number of bins opened by + * the selection algorithm. + */ + template + inline ItemIteratorOnly execute(It from, It to) + { + auto infl = static_cast(std::ceil(min_obj_distance_/2.0)); + if(infl > 0) std::for_each(from, to, [this, infl](Item& item) { + item.inflate(infl); + }); + + selector_.template packItems( + from, to, bin_, pconfig_); + + if(min_obj_distance_ > 0) std::for_each(from, to, [infl](Item& item) { + item.inflate(-infl); + }); + + return selector_.getResult().size(); + } + + /// Set a progress indicator function object for the selector. + inline _Nester& progressIndicator(ProgressFunction func) + { + selector_.progressIndicator(func); return *this; + } + + /// Set a predicate to tell when to abort nesting. + inline _Nester& stopCondition(StopCondition fn) + { + stopfn_ = fn; selector_.stopCondition(fn); return *this; + } + + inline const PackGroup& lastResult() const + { + return selector_.getResult(); + } +}; + +} + +#endif // NESTER_HPP diff --git a/src/libnest2d/include/libnest2d/optimizers/nlopt/CMakeLists.txt b/src/libnest2d/include/libnest2d/optimizers/nlopt/CMakeLists.txt deleted file mode 100644 index 6f51718d8..000000000 --- a/src/libnest2d/include/libnest2d/optimizers/nlopt/CMakeLists.txt +++ /dev/null @@ -1,61 +0,0 @@ -find_package(NLopt 1.4) - -if(NOT NLopt_FOUND) - message(STATUS "NLopt not found so downloading " - "and automatic build is performed...") - - include(DownloadProject) - - if (CMAKE_VERSION VERSION_LESS 3.2) - set(UPDATE_DISCONNECTED_IF_AVAILABLE "") - else() - set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1") - endif() - - set(URL_NLOPT "https://github.com/stevengj/nlopt.git" - CACHE STRING "Location of the nlopt git repository") - - # set(NLopt_DIR ${CMAKE_BINARY_DIR}/nlopt) - include(DownloadProject) - download_project( PROJ nlopt - GIT_REPOSITORY ${URL_NLOPT} - GIT_TAG v2.5.0 - # CMAKE_CACHE_ARGS -DBUILD_SHARED_LIBS:BOOL=OFF -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${NLopt_DIR} - ${UPDATE_DISCONNECTED_IF_AVAILABLE} - ) - - set(SHARED_LIBS_STATE BUILD_SHARED_LIBS) - set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) - set(NLOPT_PYTHON OFF CACHE BOOL "" FORCE) - set(NLOPT_OCTAVE OFF CACHE BOOL "" FORCE) - set(NLOPT_MATLAB OFF CACHE BOOL "" FORCE) - set(NLOPT_GUILE OFF CACHE BOOL "" FORCE) - set(NLOPT_SWIG OFF CACHE BOOL "" FORCE) - set(NLOPT_LINK_PYTHON OFF CACHE BOOL "" FORCE) - - add_subdirectory(${nlopt_SOURCE_DIR} ${nlopt_BINARY_DIR}) - - set(NLopt_LIBS nlopt) - set(NLopt_INCLUDE_DIR ${nlopt_BINARY_DIR} ${nlopt_BINARY_DIR}/src/api) - set(SHARED_LIBS_STATE ${SHARED_STATE}) - - add_library(nloptOptimizer INTERFACE) - target_link_libraries(nloptOptimizer INTERFACE nlopt) - target_include_directories(nloptOptimizer INTERFACE ${NLopt_INCLUDE_DIR}) - -else() - add_library(nloptOptimizer INTERFACE) - target_link_libraries(nloptOptimizer INTERFACE Nlopt::Nlopt) -endif() - -#target_sources( nloptOptimizer INTERFACE -#${CMAKE_CURRENT_SOURCE_DIR}/simplex.hpp -#${CMAKE_CURRENT_SOURCE_DIR}/subplex.hpp -#${CMAKE_CURRENT_SOURCE_DIR}/genetic.hpp -#${CMAKE_CURRENT_SOURCE_DIR}/nlopt_boilerplate.hpp -#) - -target_compile_definitions(nloptOptimizer INTERFACE LIBNEST2D_OPTIMIZER_NLOPT) - -# And finally plug the nloptOptimizer into libnest2d -#target_link_libraries(libnest2d INTERFACE nloptOptimizer) diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index 1a341d691..003028758 100644 --- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -3,9 +3,6 @@ #include -// For caching nfps -#include - // For parallel for #include #include @@ -76,55 +73,6 @@ inline void enumerate( } -namespace __itemhash { - -using Key = size_t; - -template -Key hash(const _Item& item) { - using Point = TPoint; - using Segment = _Segment; - - static const int N = 26; - static const int M = N*N - 1; - - std::string ret; - auto& rhs = item.rawShape(); - auto& ctr = sl::contour(rhs); - auto it = ctr.begin(); - auto nx = std::next(it); - - double circ = 0; - while(nx != ctr.end()) { - Segment seg(*it++, *nx++); - Radians a = seg.angleToXaxis(); - double deg = Degrees(a); - int ms = 'A', ls = 'A'; - while(deg > N) { ms++; deg -= N; } - ls += int(deg); - ret.push_back(char(ms)); ret.push_back(char(ls)); - circ += std::sqrt(seg.template sqlength()); - } - - it = ctr.begin(); nx = std::next(it); - - while(nx != ctr.end()) { - Segment seg(*it++, *nx++); - auto l = int(M * std::sqrt(seg.template sqlength()) / circ); - int ms = 'A', ls = 'A'; - while(l > N) { ms++; l -= N; } - ls += l; - ret.push_back(char(ms)); ret.push_back(char(ls)); - } - - return std::hash()(ret); -} - -template -using Hash = std::unordered_map>; - -} - namespace placers { template @@ -529,17 +477,9 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer; - using ItemKeys = std::vector<__itemhash::Key>; - // Norming factor for the optimization function const double norm_; - // Caching calculated nfps - __itemhash::Hash nfpcache_; - - // Storing item hash keys - ItemKeys item_keys_; - public: using Pile = nfp::Shapes; @@ -636,15 +576,12 @@ public: private: using Shapes = TMultiShape; - using ItemRef = std::reference_wrapper; - using ItemWithHash = const std::pair; - Shapes calcnfp(const ItemWithHash itsh, Lvl) + Shapes calcnfp(const Item &trsh, Lvl) { using namespace nfp; Shapes nfps(items_.size()); - const Item& trsh = itsh.first; // ///////////////////////////////////////////////////////////////////// // TODO: this is a workaround and should be solved in Item with mutexes @@ -678,12 +615,11 @@ private: template - Shapes calcnfp( const ItemWithHash itsh, Level) + Shapes calcnfp(const Item &trsh, Level) { // Function for arbitrary level of nfp implementation using namespace nfp; Shapes nfps; - const Item& trsh = itsh.first; auto& orb = trsh.transformedShape(); bool orbconvex = trsh.isContourConvex(); @@ -849,8 +785,6 @@ private: remlist.insert(remlist.end(), remaining.from, remaining.to); } - size_t itemhash = __itemhash::hash(item); - if(items_.empty()) { setInitialPosition(item); best_overfit = overfit(item.transformedShape(), bin_); @@ -875,7 +809,7 @@ private: // it is disjunct from the current merged pile placeOutsideOfBin(item); - nfps = calcnfp({item, itemhash}, Lvl()); + nfps = calcnfp(item, Lvl()); auto iv = item.referenceVertex(); @@ -1112,7 +1046,6 @@ private: if(can_pack) { ret = PackResult(item); - item_keys_.emplace_back(itemhash); } else { ret = PackResult(best_overfit); } @@ -1189,8 +1122,6 @@ private: sl::rotate(sh, item.rotation()); Box bb = sl::boundingBox(sh); - bb.minCorner() += item.translation(); - bb.maxCorner() += item.translation(); Vertex ci, cb; auto bbin = sl::boundingBox(bin_); diff --git a/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp b/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp index 26681aeec..dc1bbd4f1 100644 --- a/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp +++ b/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp @@ -1,7 +1,7 @@ #ifndef PLACER_BOILERPLATE_HPP #define PLACER_BOILERPLATE_HPP -#include +#include namespace libnest2d { namespace placers { diff --git a/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp b/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp index 2df9a26c3..8e65bafe9 100644 --- a/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp +++ b/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp @@ -2,7 +2,7 @@ #define SELECTION_BOILERPLATE_HPP #include -#include +#include namespace libnest2d { namespace selections { @@ -25,7 +25,7 @@ public: inline void clear() { packed_bins_.clear(); } protected: - + template void remove_unpackable_items(Container &c, const Bin &bin, const PCfg& pcfg) { @@ -33,17 +33,17 @@ protected: // then it should be removed from the list auto it = c.begin(); while (it != c.end() && !stopcond_()) { - + // WARNING: The copy of itm needs to be created before Placer. // Placer is working with references and its destructor still // manipulates the item this is why the order of stack creation - // matters here. + // matters here. const Item& itm = *it; Item cpy{itm}; - + Placer p{bin}; p.configure(pcfg); - if (!p.pack(cpy)) it = c.erase(it); + if (itm.area() <= 0 || !p.pack(cpy)) it = c.erase(it); else it++; } } diff --git a/src/libnest2d/src/libnest2d.cpp b/src/libnest2d/src/libnest2d.cpp index 021458787..5a881541e 100644 --- a/src/libnest2d/src/libnest2d.cpp +++ b/src/libnest2d/src/libnest2d.cpp @@ -1,23 +1,26 @@ -#include +#include namespace libnest2d { -template class Nester; -template class Nester; +template class _Nester; +template class _Nester; -template PackGroup nest(std::vector::iterator from, - std::vector::iterator to, - const Box& bin, - Coord dist = 0, - const NfpPlacer::Config& pconf, - const FirstFitSelection::Config& sconf); +template std::size_t _Nester::execute( + std::vector::iterator, std::vector::iterator); +template std::size_t _Nester::execute( + std::vector::iterator, std::vector::iterator); -template PackGroup nest(std::vector::iterator from, - std::vector::iterator to, - const Box& bin, - ProgressFunction prg, - StopCondition scond, - Coord dist = 0, - const NfpPlacer::Config& pconf, - const FirstFitSelection::Config& sconf); +template std::size_t nest(std::vector::iterator from, + std::vector::iterator to, + const Box & bin, + Coord dist, + const NestConfig &cfg, + NestControl ctl); + +template std::size_t nest(std::vector::iterator from, + std::vector::iterator to, + const Box & bin, + Coord dist, + const NestConfig &cfg, + NestControl ctl); } diff --git a/src/libnest2d/tests/CMakeLists.txt b/src/libnest2d/tests/CMakeLists.txt deleted file mode 100644 index 1b7d8e3ae..000000000 --- a/src/libnest2d/tests/CMakeLists.txt +++ /dev/null @@ -1,60 +0,0 @@ - -# Try to find existing GTest installation -find_package(GTest 1.7) - -if(NOT GTEST_FOUND) - set(URL_GTEST "https://github.com/google/googletest.git" - CACHE STRING "Google test source code repository location.") - - message(STATUS "GTest not found so downloading from ${URL_GTEST}") - # Go and download google test framework, integrate it with the build - set(GTEST_LIBS_TO_LINK gtest gtest_main) - - if (CMAKE_VERSION VERSION_LESS 3.2) - set(UPDATE_DISCONNECTED_IF_AVAILABLE "") - else() - set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1") - endif() - - include(DownloadProject) - download_project(PROJ googletest - GIT_REPOSITORY ${URL_GTEST} - GIT_TAG release-1.7.0 - ${UPDATE_DISCONNECTED_IF_AVAILABLE} - ) - - # Prevent GoogleTest from overriding our compiler/linker options - # when building with Visual Studio - set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) - - add_subdirectory(${googletest_SOURCE_DIR} - ${googletest_BINARY_DIR} - ) - - set(GTEST_INCLUDE_DIRS ${googletest_SOURCE_DIR}/include) - -else() - find_package(Threads REQUIRED) - set(GTEST_LIBS_TO_LINK ${GTEST_BOTH_LIBRARIES} Threads::Threads) -endif() - -add_executable(tests_clipper_nlopt - test.cpp - ../tools/svgtools.hpp -# ../tools/libnfpglue.hpp -# ../tools/libnfpglue.cpp - printer_parts.h - printer_parts.cpp -) - -target_link_libraries(tests_clipper_nlopt libnest2d ${GTEST_LIBS_TO_LINK} ) - -target_include_directories(tests_clipper_nlopt PRIVATE BEFORE ${GTEST_INCLUDE_DIRS}) - -if(NOT LIBNEST2D_HEADER_ONLY) - target_link_libraries(tests_clipper_nlopt ${LIBNAME}) -else() - target_link_libraries(tests_clipper_nlopt libnest2d) -endif() - -add_test(libnest2d_tests tests_clipper_nlopt) diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 52168c929..3fa7e1841 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -375,7 +375,7 @@ public: for(unsigned idx = 0; idx < fixeditems.size(); ++idx) { Item& itm = fixeditems[idx]; - itm.markAsFixed(); + itm.markAsFixedInBin(itm.binId()); } m_pck.configure(m_pconf); @@ -618,19 +618,21 @@ void arrange(ArrangePolygons & arrangables, items.reserve(arrangables.size()); // Create Item from Arrangeable - auto process_arrangeable = - [](const ArrangePolygon &arrpoly, std::vector &outp) + auto process_arrangeable = [](const ArrangePolygon &arrpoly, + std::vector & outp) { - Polygon p = arrpoly.poly.contour; - const Vec2crd & offs = arrpoly.translation; - double rotation = arrpoly.rotation; + Polygon p = arrpoly.poly.contour; + const Vec2crd &offs = arrpoly.translation; + double rotation = arrpoly.rotation; if (p.is_counter_clockwise()) p.reverse(); clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p)); - - auto firstp = clpath.Contour.front(); - clpath.Contour.emplace_back(firstp); + + if (!clpath.Contour.empty()) { + auto firstp = clpath.Contour.front(); + clpath.Contour.emplace_back(firstp); + } outp.emplace_back(std::move(clpath)); outp.back().rotation(rotation); diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp index b1ebdcfbc..4fbe72163 100644 --- a/src/libslic3r/BoundingBox.hpp +++ b/src/libslic3r/BoundingBox.hpp @@ -15,7 +15,7 @@ public: PointClass max; bool defined; - BoundingBoxBase() : defined(false), min(PointClass::Zero()), max(PointClass::Zero()) {} + BoundingBoxBase() : min(PointClass::Zero()), max(PointClass::Zero()), defined(false) {} BoundingBoxBase(const PointClass &pmin, const PointClass &pmax) : min(pmin), max(pmax), defined(pmin(0) < pmax(0) && pmin(1) < pmax(1)) {} BoundingBoxBase(const std::vector& points) : min(PointClass::Zero()), max(PointClass::Zero()) @@ -59,7 +59,7 @@ template class BoundingBox3Base : public BoundingBoxBase { public: - BoundingBox3Base() : BoundingBoxBase() {}; + BoundingBox3Base() : BoundingBoxBase() {} BoundingBox3Base(const PointClass &pmin, const PointClass &pmax) : BoundingBoxBase(pmin, pmax) { if (pmin(2) >= pmax(2)) BoundingBoxBase::defined = false; } @@ -100,6 +100,33 @@ public: } }; +// Will prevent warnings caused by non existing definition of template in hpp +extern template void BoundingBoxBase::scale(double factor); +extern template void BoundingBoxBase::scale(double factor); +extern template void BoundingBoxBase::scale(double factor); +extern template void BoundingBoxBase::offset(coordf_t delta); +extern template void BoundingBoxBase::offset(coordf_t delta); +extern template void BoundingBoxBase::merge(const Point &point); +extern template void BoundingBoxBase::merge(const Vec2d &point); +extern template void BoundingBoxBase::merge(const Points &points); +extern template void BoundingBoxBase::merge(const Pointfs &points); +extern template void BoundingBoxBase::merge(const BoundingBoxBase &bb); +extern template void BoundingBoxBase::merge(const BoundingBoxBase &bb); +extern template Point BoundingBoxBase::size() const; +extern template Vec2d BoundingBoxBase::size() const; +extern template double BoundingBoxBase::radius() const; +extern template double BoundingBoxBase::radius() const; +extern template Point BoundingBoxBase::center() const; +extern template Vec2d BoundingBoxBase::center() const; +extern template void BoundingBox3Base::merge(const Vec3d &point); +extern template void BoundingBox3Base::merge(const Pointf3s &points); +extern template void BoundingBox3Base::merge(const BoundingBox3Base &bb); +extern template Vec3d BoundingBox3Base::size() const; +extern template double BoundingBox3Base::radius() const; +extern template void BoundingBox3Base::offset(coordf_t delta); +extern template Vec3d BoundingBox3Base::center() const; +extern template coordf_t BoundingBox3Base::max_size() const; + class BoundingBox : public BoundingBoxBase { public: @@ -113,9 +140,9 @@ public: // to encompass the original bounding box. void align_to_grid(const coord_t cell_size); - BoundingBox() : BoundingBoxBase() {}; - BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase(pmin, pmax) {}; - BoundingBox(const Points &points) : BoundingBoxBase(points) {}; + BoundingBox() : BoundingBoxBase() {} + BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase(pmin, pmax) {} + BoundingBox(const Points &points) : BoundingBoxBase(points) {} BoundingBox(const Lines &lines); friend BoundingBox get_extents_rotated(const Points &points, double angle); @@ -124,25 +151,25 @@ public: class BoundingBox3 : public BoundingBox3Base { public: - BoundingBox3() : BoundingBox3Base() {}; - BoundingBox3(const Vec3crd &pmin, const Vec3crd &pmax) : BoundingBox3Base(pmin, pmax) {}; - BoundingBox3(const Points3& points) : BoundingBox3Base(points) {}; + BoundingBox3() : BoundingBox3Base() {} + BoundingBox3(const Vec3crd &pmin, const Vec3crd &pmax) : BoundingBox3Base(pmin, pmax) {} + BoundingBox3(const Points3& points) : BoundingBox3Base(points) {} }; class BoundingBoxf : public BoundingBoxBase { public: - BoundingBoxf() : BoundingBoxBase() {}; - BoundingBoxf(const Vec2d &pmin, const Vec2d &pmax) : BoundingBoxBase(pmin, pmax) {}; - BoundingBoxf(const std::vector &points) : BoundingBoxBase(points) {}; + BoundingBoxf() : BoundingBoxBase() {} + BoundingBoxf(const Vec2d &pmin, const Vec2d &pmax) : BoundingBoxBase(pmin, pmax) {} + BoundingBoxf(const std::vector &points) : BoundingBoxBase(points) {} }; class BoundingBoxf3 : public BoundingBox3Base { public: - BoundingBoxf3() : BoundingBox3Base() {}; - BoundingBoxf3(const Vec3d &pmin, const Vec3d &pmax) : BoundingBox3Base(pmin, pmax) {}; - BoundingBoxf3(const std::vector &points) : BoundingBox3Base(points) {}; + BoundingBoxf3() : BoundingBox3Base() {} + BoundingBoxf3(const Vec3d &pmin, const Vec3d &pmax) : BoundingBox3Base(pmin, pmax) {} + BoundingBoxf3(const std::vector &points) : BoundingBox3Base(points) {} BoundingBoxf3 transformed(const Transform3d& matrix) const; }; diff --git a/src/libslic3r/BridgeDetector.cpp b/src/libslic3r/BridgeDetector.cpp index 596b61a31..4dc1b9963 100644 --- a/src/libslic3r/BridgeDetector.cpp +++ b/src/libslic3r/BridgeDetector.cpp @@ -6,9 +6,9 @@ namespace Slic3r { BridgeDetector::BridgeDetector( - ExPolygon _expolygon, - const ExPolygonCollection &_lower_slices, - coord_t _spacing) : + ExPolygon _expolygon, + const ExPolygons &_lower_slices, + coord_t _spacing) : // The original infill polygon, not inflated. expolygons(expolygons_owned), // All surfaces of the object supporting this region. @@ -20,9 +20,9 @@ BridgeDetector::BridgeDetector( } BridgeDetector::BridgeDetector( - const ExPolygons &_expolygons, - const ExPolygonCollection &_lower_slices, - coord_t _spacing) : + const ExPolygons &_expolygons, + const ExPolygons &_lower_slices, + coord_t _spacing) : // The original infill polygon, not inflated. expolygons(_expolygons), // All surfaces of the object supporting this region. @@ -40,13 +40,17 @@ void BridgeDetector::initialize() this->angle = -1.; // Outset our bridge by an arbitrary amout; we'll use this outer margin for detecting anchors. - Polygons grown = offset(to_polygons(this->expolygons), this->spacing); + Polygons grown = offset(to_polygons(this->expolygons), float(this->spacing)); // Detect possible anchoring edges of this bridging region. // Detect what edges lie on lower slices by turning bridge contour and holes // into polylines and then clipping them with each lower slice's contour. // Currently _edges are only used to set a candidate direction of the bridge (see bridge_direction_candidates()). - this->_edges = intersection_pl(to_polylines(grown), this->lower_slices.contours()); + Polygons contours; + contours.reserve(this->lower_slices.size()); + for (const ExPolygon &expoly : this->lower_slices) + contours.push_back(expoly.contour); + this->_edges = intersection_pl(to_polylines(grown), contours); #ifdef SLIC3R_DEBUG printf(" bridge has " PRINTF_ZU " support(s)\n", this->_edges.size()); @@ -54,7 +58,7 @@ void BridgeDetector::initialize() // detect anchors as intersection between our bridge expolygon and the lower slices // safety offset required to avoid Clipper from detecting empty intersection while Boost actually found some edges - this->_anchor_regions = intersection_ex(grown, to_polygons(this->lower_slices.expolygons), true); + this->_anchor_regions = intersection_ex(grown, to_polygons(this->lower_slices), true); /* if (0) { @@ -281,29 +285,28 @@ Polygons BridgeDetector::coverage(double angle, bool precise) const { } } - // Unite the trapezoids before rotation, as the rotation creates tiny gaps and intersections between the trapezoids - // instead of exact overlaps. - covered = union_(covered); - // Intersect trapezoids with actual bridge area to remove extra margins and append it to result. - polygons_rotate(covered, -(PI/2.0 - angle)); - //covered = intersection(covered, to_polygons(this->expolygons)); + // Unite the trapezoids before rotation, as the rotation creates tiny gaps and intersections between the trapezoids + // instead of exact overlaps. + covered = union_(covered); + // Intersect trapezoids with actual bridge area to remove extra margins and append it to result. + polygons_rotate(covered, -(PI/2.0 - angle)); + //covered = intersection(covered, to_polygons(this->expolygons)); #if 0 - { - my @lines = map @{$_->lines}, @$trapezoids; - $_->rotate(-(PI/2 - $angle), [0,0]) for @lines; - - require "Slic3r/SVG.pm"; - Slic3r::SVG::output( - "coverage_" . rad2deg($angle) . ".svg", - expolygons => [$self->expolygon], - green_expolygons => $self->_anchor_regions, - red_expolygons => $coverage, - lines => \@lines, - ); + { + my @lines = map @{$_->lines}, @$trapezoids; + $_->rotate(-(PI/2 - $angle), [0,0]) for @lines; + + require "Slic3r/SVG.pm"; + Slic3r::SVG::output( + "coverage_" . rad2deg($angle) . ".svg", + expolygons => [$self->expolygon], + green_expolygons => $self->_anchor_regions, + red_expolygons => $coverage, + lines => \@lines, + ); } #endif } - return covered; } @@ -316,7 +319,7 @@ BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const if (angle == -1) angle = this->angle; if (angle == -1) return; - Polygons grown_lower = offset(this->lower_slices.expolygons, this->spacing); + Polygons grown_lower = offset(this->lower_slices, float(this->spacing)); for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly) { // get unsupported bridge edges (both contour and holes) diff --git a/src/libslic3r/BridgeDetector.hpp b/src/libslic3r/BridgeDetector.hpp index e79a619b6..74f926e0b 100644 --- a/src/libslic3r/BridgeDetector.hpp +++ b/src/libslic3r/BridgeDetector.hpp @@ -3,7 +3,6 @@ #include "libslic3r.h" #include "ExPolygon.hpp" -#include "ExPolygonCollection.hpp" #include namespace Slic3r { @@ -21,7 +20,7 @@ public: // In case the caller gaves us the input polygons by a value, make a copy. ExPolygons expolygons_owned; // Lower slices, all regions. - const ExPolygonCollection &lower_slices; + const ExPolygons &lower_slices; // Scaled extrusion width of the infill. coord_t spacing; // Angle resolution for the brute force search of the best bridging angle. @@ -29,8 +28,8 @@ public: // The final optimal angle. double angle; - BridgeDetector(ExPolygon _expolygon, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width); - BridgeDetector(const ExPolygons &_expolygons, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width); + BridgeDetector(ExPolygon _expolygon, const ExPolygons &_lower_slices, coord_t _extrusion_width); + BridgeDetector(const ExPolygons &_expolygons, const ExPolygons &_lower_slices, coord_t _extrusion_width); // If bridge_direction_override != 0, then the angle is used instead of auto-detect. bool detect_angle(double bridge_direction_override = 0.); Polygons coverage(double angle = -1, bool precise = true) const; diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 73abe1f16..65e5aa0ef 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -22,6 +22,8 @@ add_library(libslic3r STATIC Config.hpp EdgeGrid.cpp EdgeGrid.hpp + ElephantFootCompensation.cpp + ElephantFootCompensation.hpp ExPolygon.cpp ExPolygon.hpp ExPolygonCollection.cpp @@ -73,6 +75,8 @@ add_library(libslic3r STATIC Format/STL.hpp GCode/Analyzer.cpp GCode/Analyzer.hpp + GCode/ThumbnailData.cpp + GCode/ThumbnailData.hpp GCode/CoolingBuffer.cpp GCode/CoolingBuffer.hpp GCode/PostProcessor.cpp @@ -102,7 +106,7 @@ add_library(libslic3r STATIC Geometry.cpp Geometry.hpp Int128.hpp -# KdTree.hpp + KDTreeIndirect.hpp Layer.cpp Layer.hpp LayerRegion.cpp @@ -135,8 +139,6 @@ add_library(libslic3r STATIC PolygonTrimmer.hpp Polyline.cpp Polyline.hpp - PolylineCollection.cpp - PolylineCollection.hpp Print.cpp Print.hpp PrintBase.cpp @@ -146,6 +148,8 @@ add_library(libslic3r STATIC PrintObject.cpp PrintRegion.cpp Semver.cpp + ShortestPath.cpp + ShortestPath.hpp SLAPrint.cpp SLAPrint.hpp SLA/SLAAutoSupports.hpp @@ -180,8 +184,13 @@ add_library(libslic3r STATIC miniz_extension.cpp SLA/SLACommon.hpp SLA/SLABoilerPlate.hpp - SLA/SLABasePool.hpp - SLA/SLABasePool.cpp + 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 @@ -193,6 +202,8 @@ add_library(libslic3r STATIC SLA/SLARaster.cpp SLA/SLARasterWriter.hpp SLA/SLARasterWriter.cpp + SLA/ConcaveHull.hpp + SLA/ConcaveHull.cpp ) encoding_check(libslic3r) @@ -201,7 +212,7 @@ if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE) endif () -target_compile_definitions(libslic3r PUBLIC -DUSE_TBB) +target_compile_definitions(libslic3r PUBLIC -DUSE_TBB -DTBB_USE_CAPTURED_EXCEPTION=0) target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${LIBNEST2D_INCLUDES} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(libslic3r libnest2d @@ -218,7 +229,9 @@ target_link_libraries(libslic3r poly2tri qhull semver - tbb + TBB::tbb + # OpenVDB::openvdb + ${CMAKE_DL_LIBS} ) if(WIN32) diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 649d52c0d..8c3c6951c 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -1,5 +1,6 @@ #include "ClipperUtils.hpp" #include "Geometry.hpp" +#include "ShortestPath.hpp" // #define CLIPPER_UTILS_DEBUG @@ -106,8 +107,7 @@ void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, ExPolygons* ex } } -ExPolygons -PolyTreeToExPolygons(ClipperLib::PolyTree& polytree) +ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree& polytree) { ExPolygons retval; for (int i = 0; i < polytree.ChildCount(); ++i) @@ -150,8 +150,7 @@ Slic3r::Polylines ClipperPaths_to_Slic3rPolylines(const ClipperLib::Paths &input return retval; } -ExPolygons -ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input) +ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input) { // init Clipper ClipperLib::Clipper clipper; @@ -166,8 +165,7 @@ ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input) return PolyTreeToExPolygons(polytree); } -ClipperLib::Path -Slic3rMultiPoint_to_ClipperPath(const MultiPoint &input) +ClipperLib::Path Slic3rMultiPoint_to_ClipperPath(const MultiPoint &input) { ClipperLib::Path retval; for (Points::const_iterator pit = input.points.begin(); pit != input.points.end(); ++pit) @@ -175,8 +173,7 @@ Slic3rMultiPoint_to_ClipperPath(const MultiPoint &input) return retval; } -ClipperLib::Path -Slic3rMultiPoint_to_ClipperPath_reversed(const Slic3r::MultiPoint &input) +ClipperLib::Path Slic3rMultiPoint_to_ClipperPath_reversed(const Slic3r::MultiPoint &input) { ClipperLib::Path output; output.reserve(input.points.size()); @@ -193,6 +190,19 @@ ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polygons &input) return retval; } +ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const ExPolygons &input) +{ + ClipperLib::Paths retval; + for (auto &ep : input) { + retval.emplace_back(Slic3rMultiPoint_to_ClipperPath(ep.contour)); + + for (auto &h : ep.holes) + retval.emplace_back(Slic3rMultiPoint_to_ClipperPath(h)); + } + + return retval; +} + ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polylines &input) { ClipperLib::Paths retval; @@ -471,14 +481,16 @@ ExPolygons offset2_ex(const ExPolygons &expolygons, const double delta1, return union_ex(polys); } -template -T -_clipper_do(const ClipperLib::ClipType clipType, const Polygons &subject, - const Polygons &clip, const ClipperLib::PolyFillType fillType, const bool safety_offset_) +template +T _clipper_do(const ClipperLib::ClipType clipType, + TSubj && subject, + TClip && clip, + const ClipperLib::PolyFillType fillType, + const bool safety_offset_) { // read input - ClipperLib::Paths input_subject = Slic3rMultiPoints_to_ClipperPaths(subject); - ClipperLib::Paths input_clip = Slic3rMultiPoints_to_ClipperPaths(clip); + ClipperLib::Paths input_subject = Slic3rMultiPoints_to_ClipperPaths(std::forward(subject)); + ClipperLib::Paths input_clip = Slic3rMultiPoints_to_ClipperPaths(std::forward(clip)); // perform safety offset if (safety_offset_) { @@ -505,7 +517,7 @@ _clipper_do(const ClipperLib::ClipType clipType, const Polygons &subject, // Fix of #117: A large fractal pyramid takes ages to slice // The Clipper library has difficulties processing overlapping polygons. -// Namely, the function Clipper::JoinCommonEdges() has potentially a terrible time complexity if the output +// Namely, the function ClipperLib::JoinCommonEdges() has potentially a terrible time complexity if the output // of the operation is of the PolyTree type. // This function implmenets a following workaround: // 1) Peform the Clipper operation with the output to Paths. This method handles overlaps in a reasonable time. @@ -647,12 +659,26 @@ _clipper_ln(ClipperLib::ClipType clipType, const Lines &subject, const Polygons return retval; } -ClipperLib::PolyTree -union_pt(const Polygons &subject, bool safety_offset_) +ClipperLib::PolyTree union_pt(const Polygons &subject, bool safety_offset_) { return _clipper_do(ClipperLib::ctUnion, subject, Polygons(), ClipperLib::pftEvenOdd, safety_offset_); } +ClipperLib::PolyTree union_pt(const ExPolygons &subject, bool safety_offset_) +{ + return _clipper_do(ClipperLib::ctUnion, subject, Polygons(), ClipperLib::pftEvenOdd, safety_offset_); +} + +ClipperLib::PolyTree union_pt(Polygons &&subject, bool safety_offset_) +{ + return _clipper_do(ClipperLib::ctUnion, std::move(subject), Polygons(), ClipperLib::pftEvenOdd, safety_offset_); +} + +ClipperLib::PolyTree union_pt(ExPolygons &&subject, bool safety_offset_) +{ + return _clipper_do(ClipperLib::ctUnion, std::move(subject), Polygons(), ClipperLib::pftEvenOdd, safety_offset_); +} + Polygons union_pt_chained(const Polygons &subject, bool safety_offset_) { @@ -663,30 +689,123 @@ union_pt_chained(const Polygons &subject, bool safety_offset_) return retval; } -void traverse_pt(ClipperLib::PolyNodes &nodes, Polygons* retval) +static ClipperLib::PolyNodes order_nodes(const ClipperLib::PolyNodes &nodes) +{ + // collect ordering points + Points ordering_points; + ordering_points.reserve(nodes.size()); + for (const ClipperLib::PolyNode *node : nodes) + ordering_points.emplace_back(Point(node->Contour.front().X, node->Contour.front().Y)); + + // perform the ordering + ClipperLib::PolyNodes ordered_nodes = chain_clipper_polynodes(ordering_points, nodes); + + return ordered_nodes; +} + +enum class e_ordering { + ORDER_POLYNODES, + DONT_ORDER_POLYNODES +}; + +template +void foreach_node(const ClipperLib::PolyNodes &nodes, + std::function fn); + +template<> void foreach_node( + const ClipperLib::PolyNodes & nodes, + std::function fn) +{ + for (auto &n : nodes) fn(n); +} + +template<> void foreach_node( + const ClipperLib::PolyNodes & nodes, + std::function fn) +{ + auto ordered_nodes = order_nodes(nodes); + for (auto &n : ordered_nodes) fn(n); +} + +template +void _traverse_pt(const ClipperLib::PolyNodes &nodes, Polygons *retval) { /* use a nearest neighbor search to order these children TODO: supply start_near to chained_path() too? */ - // collect ordering points - Points ordering_points; - ordering_points.reserve(nodes.size()); - for (ClipperLib::PolyNodes::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { - Point p((*it)->Contour.front().X, (*it)->Contour.front().Y); - ordering_points.emplace_back(p); - } - - // perform the ordering - ClipperLib::PolyNodes ordered_nodes; - Slic3r::Geometry::chained_path_items(ordering_points, nodes, ordered_nodes); - // push results recursively - for (ClipperLib::PolyNodes::iterator it = ordered_nodes.begin(); it != ordered_nodes.end(); ++it) { + foreach_node(nodes, [&retval](const ClipperLib::PolyNode *node) { // traverse the next depth - traverse_pt((*it)->Childs, retval); - retval->emplace_back(ClipperPath_to_Slic3rPolygon((*it)->Contour)); - if ((*it)->IsHole()) retval->back().reverse(); // ccw - } + _traverse_pt(node->Childs, retval); + retval->emplace_back(ClipperPath_to_Slic3rPolygon(node->Contour)); + if (node->IsHole()) retval->back().reverse(); // ccw + }); +} + +template +void _traverse_pt(const ClipperLib::PolyNode *tree, ExPolygons *retval) +{ + if (!retval || !tree) return; + + ExPolygons &retv = *retval; + + std::function hole_fn; + + auto contour_fn = [&retv, &hole_fn](const ClipperLib::PolyNode *pptr) { + ExPolygon poly; + poly.contour.points = ClipperPath_to_Slic3rPolygon(pptr->Contour); + auto fn = std::bind(hole_fn, std::placeholders::_1, poly); + foreach_node(pptr->Childs, fn); + retv.push_back(poly); + }; + + hole_fn = [&contour_fn](const ClipperLib::PolyNode *pptr, ExPolygon& poly) + { + poly.holes.emplace_back(); + poly.holes.back().points = ClipperPath_to_Slic3rPolygon(pptr->Contour); + foreach_node(pptr->Childs, contour_fn); + }; + + contour_fn(tree); +} + +template +void _traverse_pt(const ClipperLib::PolyNodes &nodes, ExPolygons *retval) +{ + // Here is the actual traverse + foreach_node(nodes, [&retval](const ClipperLib::PolyNode *node) { + _traverse_pt(node, retval); + }); +} + +void traverse_pt(const ClipperLib::PolyNode *tree, ExPolygons *retval) +{ + _traverse_pt(tree, retval); +} + +void traverse_pt_unordered(const ClipperLib::PolyNode *tree, ExPolygons *retval) +{ + _traverse_pt(tree, retval); +} + +void traverse_pt(const ClipperLib::PolyNodes &nodes, Polygons *retval) +{ + _traverse_pt(nodes, retval); +} + +void traverse_pt(const ClipperLib::PolyNodes &nodes, ExPolygons *retval) +{ + _traverse_pt(nodes, retval); +} + +void traverse_pt_unordered(const ClipperLib::PolyNodes &nodes, Polygons *retval) +{ + _traverse_pt(nodes, retval); +} + +void traverse_pt_unordered(const ClipperLib::PolyNodes &nodes, ExPolygons *retval) +{ + _traverse_pt(nodes, retval); } Polygons simplify_polygons(const Polygons &subject, bool preserve_collinear) @@ -795,4 +914,330 @@ Polygons top_level_islands(const Slic3r::Polygons &polygons) return out; } +// Outer offset shall not split the input contour into multiples. It is expected, that the solution will be non empty and it will contain just a single polygon. +ClipperLib::Paths fix_after_outer_offset(const ClipperLib::Path &input, ClipperLib::PolyFillType filltype, bool reverse_result) +{ + ClipperLib::Paths solution; + if (! input.empty()) { + ClipperLib::Clipper clipper; + clipper.AddPath(input, ClipperLib::ptSubject, true); + clipper.ReverseSolution(reverse_result); + clipper.Execute(ClipperLib::ctUnion, solution, filltype, filltype); + } + return solution; +} + +// Inner offset may split the source contour into multiple contours, but one shall not be inside the other. +ClipperLib::Paths fix_after_inner_offset(const ClipperLib::Path &input, ClipperLib::PolyFillType filltype, bool reverse_result) +{ + ClipperLib::Paths solution; + if (! input.empty()) { + ClipperLib::Clipper clipper; + clipper.AddPath(input, ClipperLib::ptSubject, true); + ClipperLib::IntRect r = clipper.GetBounds(); + r.left -= 10; r.top -= 10; r.right += 10; r.bottom += 10; + if (filltype == ClipperLib::pftPositive) + clipper.AddPath({ ClipperLib::IntPoint(r.left, r.bottom), ClipperLib::IntPoint(r.left, r.top), ClipperLib::IntPoint(r.right, r.top), ClipperLib::IntPoint(r.right, r.bottom) }, ClipperLib::ptSubject, true); + else + clipper.AddPath({ ClipperLib::IntPoint(r.left, r.bottom), ClipperLib::IntPoint(r.right, r.bottom), ClipperLib::IntPoint(r.right, r.top), ClipperLib::IntPoint(r.left, r.top) }, ClipperLib::ptSubject, true); + clipper.ReverseSolution(reverse_result); + clipper.Execute(ClipperLib::ctUnion, solution, filltype, filltype); + if (! solution.empty()) + solution.erase(solution.begin()); + } + return solution; +} + +ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::vector &deltas, double miter_limit) +{ + assert(contour.size() == deltas.size()); + +#ifndef NDEBUG + // Verify that the deltas are either all positive, or all negative. + bool positive = false; + bool negative = false; + for (float delta : deltas) + if (delta < 0.f) + negative = true; + else if (delta > 0.f) + positive = true; + assert(! (negative && positive)); +#endif /* NDEBUG */ + + ClipperLib::Path out; + + if (deltas.size() > 2) + { + out.reserve(contour.size() * 2); + + // Clamp miter limit to 2. + miter_limit = (miter_limit > 2.) ? 2. / (miter_limit * miter_limit) : 0.5; + + // perpenduclar vector + auto perp = [](const Vec2d &v) -> Vec2d { return Vec2d(v.y(), - v.x()); }; + + // Add a new point to the output, scale by CLIPPER_OFFSET_SCALE and round to ClipperLib::cInt. + auto add_offset_point = [&out](Vec2d pt) { + pt *= double(CLIPPER_OFFSET_SCALE); + pt += Vec2d(0.5 - (pt.x() < 0), 0.5 - (pt.y() < 0)); + out.emplace_back(ClipperLib::cInt(pt.x()), ClipperLib::cInt(pt.y())); + }; + + // Minimum edge length, squared. + double lmin = *std::max_element(deltas.begin(), deltas.end()) * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR; + double l2min = lmin * lmin; + // Minimum angle to consider two edges to be parallel. + // Vojtech's estimate. +// const double sin_min_parallel = EPSILON + 1. / double(CLIPPER_OFFSET_SCALE); + // Implementation equal to Clipper. + const double sin_min_parallel = 1.; + + // Find the last point further from pt by l2min. + Vec2d pt = contour.front().cast(); + size_t iprev = contour.size() - 1; + Vec2d ptprev; + for (; iprev > 0; -- iprev) { + ptprev = contour[iprev].cast(); + if ((ptprev - pt).squaredNorm() > l2min) + break; + } + + if (iprev != 0) { + size_t ilast = iprev; + // Normal to the (pt - ptprev) segment. + Vec2d nprev = perp(pt - ptprev).normalized(); + for (size_t i = 0; ; ) { + // Find the next point further from pt by l2min. + size_t j = i + 1; + Vec2d ptnext; + for (; j <= ilast; ++ j) { + ptnext = contour[j].cast(); + double l2 = (ptnext - pt).squaredNorm(); + if (l2 > l2min) + break; + } + if (j > ilast) { + assert(i <= ilast); + // If the last edge is too short, merge it with the previous edge. + i = ilast; + ptnext = contour.front().cast(); + } + + // Normal to the (ptnext - pt) segment. + Vec2d nnext = perp(ptnext - pt).normalized(); + + double delta = deltas[i]; + double sin_a = clamp(-1., 1., cross2(nprev, nnext)); + double convex = sin_a * delta; + if (convex <= - sin_min_parallel) { + // Concave corner. + add_offset_point(pt + nprev * delta); + add_offset_point(pt); + add_offset_point(pt + nnext * delta); + } else { + double dot = nprev.dot(nnext); + if (convex < sin_min_parallel && dot > 0.) { + // Nearly parallel. + add_offset_point((nprev.dot(nnext) > 0.) ? (pt + nprev * delta) : pt); + } else { + // Convex corner, possibly extremely sharp if convex < sin_min_parallel. + double r = 1. + dot; + if (r >= miter_limit) + add_offset_point(pt + (nprev + nnext) * (delta / r)); + else { + double dx = std::tan(std::atan2(sin_a, dot) / 4.); + Vec2d newpt1 = pt + (nprev - perp(nprev) * dx) * delta; + Vec2d newpt2 = pt + (nnext + perp(nnext) * dx) * delta; +#ifndef NDEBUG + Vec2d vedge = 0.5 * (newpt1 + newpt2) - pt; + double dist_norm = vedge.norm(); + assert(std::abs(dist_norm - std::abs(delta)) < SCALED_EPSILON); +#endif /* NDEBUG */ + add_offset_point(newpt1); + add_offset_point(newpt2); + } + } + } + + if (i == ilast) + break; + + ptprev = pt; + nprev = nnext; + pt = ptnext; + i = j; + } + } + } + +#if 0 + { + ClipperLib::Path polytmp(out); + unscaleClipperPolygon(polytmp); + Slic3r::Polygon offsetted = ClipperPath_to_Slic3rPolygon(polytmp); + BoundingBox bbox = get_extents(contour); + bbox.merge(get_extents(offsetted)); + static int iRun = 0; + SVG svg(debug_out_path("mittered_offset_path_scaled-%d.svg", iRun ++).c_str(), bbox); + svg.draw_outline(Polygon(contour), "blue", scale_(0.01)); + svg.draw_outline(offsetted, "red", scale_(0.01)); + svg.draw(contour, "blue", scale_(0.03)); + svg.draw((Points)offsetted, "blue", scale_(0.03)); + } +#endif + + return out; +} + +Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit) +{ +#ifndef NDEBUG + // Verify that the deltas are all non positive. + for (const std::vector &ds : deltas) + for (float delta : ds) + assert(delta <= 0.); + assert(expoly.holes.size() + 1 == deltas.size()); +#endif /* NDEBUG */ + + // 1) Offset the outer contour. + ClipperLib::Paths contours = fix_after_inner_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftNegative, true); + + // 2) Offset the holes one by one, collect the results. + ClipperLib::Paths holes; + holes.reserve(expoly.holes.size()); + for (const Polygon& hole : expoly.holes) + append(holes, fix_after_outer_offset(mittered_offset_path_scaled(hole, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftPositive, false)); + + // 3) Subtract holes from the contours. + ClipperLib::Paths output; + if (holes.empty()) + output = std::move(contours); + else { + ClipperLib::Clipper clipper; + clipper.Clear(); + clipper.AddPaths(contours, ClipperLib::ptSubject, true); + clipper.AddPaths(holes, ClipperLib::ptClip, true); + clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); + } + + // 4) Unscale the output. + unscaleClipperPolygons(output); + return ClipperPaths_to_Slic3rPolygons(output); +} + +Polygons variable_offset_outer(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit) +{ +#ifndef NDEBUG + // Verify that the deltas are all non positive. +for (const std::vector& ds : deltas) + for (float delta : ds) + assert(delta >= 0.); + assert(expoly.holes.size() + 1 == deltas.size()); +#endif /* NDEBUG */ + + // 1) Offset the outer contour. + ClipperLib::Paths contours = fix_after_outer_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftPositive, false); + + // 2) Offset the holes one by one, collect the results. + ClipperLib::Paths holes; + holes.reserve(expoly.holes.size()); + for (const Polygon& hole : expoly.holes) + append(holes, fix_after_inner_offset(mittered_offset_path_scaled(hole, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftPositive, true)); + + // 3) Subtract holes from the contours. + ClipperLib::Paths output; + if (holes.empty()) + output = std::move(contours); + else { + ClipperLib::Clipper clipper; + clipper.Clear(); + clipper.AddPaths(contours, ClipperLib::ptSubject, true); + clipper.AddPaths(holes, ClipperLib::ptClip, true); + clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); + } + + // 4) Unscale the output. + unscaleClipperPolygons(output); + return ClipperPaths_to_Slic3rPolygons(output); +} + +ExPolygons variable_offset_outer_ex(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit) +{ +#ifndef NDEBUG + // Verify that the deltas are all non positive. +for (const std::vector& ds : deltas) + for (float delta : ds) + assert(delta >= 0.); + assert(expoly.holes.size() + 1 == deltas.size()); +#endif /* NDEBUG */ + + // 1) Offset the outer contour. + ClipperLib::Paths contours = fix_after_outer_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftPositive, false); + + // 2) Offset the holes one by one, collect the results. + ClipperLib::Paths holes; + holes.reserve(expoly.holes.size()); + for (const Polygon& hole : expoly.holes) + append(holes, fix_after_inner_offset(mittered_offset_path_scaled(hole, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftPositive, true)); + + // 3) Subtract holes from the contours. + unscaleClipperPolygons(contours); + ExPolygons output; + if (holes.empty()) { + output.reserve(contours.size()); + for (ClipperLib::Path &path : contours) + output.emplace_back(ClipperPath_to_Slic3rPolygon(path)); + } else { + ClipperLib::Clipper clipper; + unscaleClipperPolygons(holes); + clipper.AddPaths(contours, ClipperLib::ptSubject, true); + clipper.AddPaths(holes, ClipperLib::ptClip, true); + ClipperLib::PolyTree polytree; + clipper.Execute(ClipperLib::ctDifference, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero); + output = PolyTreeToExPolygons(polytree); + } + + return output; +} + + +ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit) +{ +#ifndef NDEBUG + // Verify that the deltas are all non positive. + for (const std::vector& ds : deltas) + for (float delta : ds) + assert(delta <= 0.); + assert(expoly.holes.size() + 1 == deltas.size()); +#endif /* NDEBUG */ + + // 1) Offset the outer contour. + ClipperLib::Paths contours = fix_after_inner_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftNegative, false); + + // 2) Offset the holes one by one, collect the results. + ClipperLib::Paths holes; + holes.reserve(expoly.holes.size()); + for (const Polygon& hole : expoly.holes) + append(holes, fix_after_outer_offset(mittered_offset_path_scaled(hole, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftNegative, true)); + + // 3) Subtract holes from the contours. + unscaleClipperPolygons(contours); + ExPolygons output; + if (holes.empty()) { + output.reserve(contours.size()); + for (ClipperLib::Path &path : contours) + output.emplace_back(ClipperPath_to_Slic3rPolygon(path)); + } else { + ClipperLib::Clipper clipper; + unscaleClipperPolygons(holes); + clipper.AddPaths(contours, ClipperLib::ptSubject, true); + clipper.AddPaths(holes, ClipperLib::ptClip, true); + ClipperLib::PolyTree polytree; + clipper.Execute(ClipperLib::ctDifference, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero); + output = PolyTreeToExPolygons(polytree); + } + + return output; +} + } diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index 4c8742279..39fe8156a 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -34,6 +34,7 @@ Slic3r::ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree& polytree); ClipperLib::Path Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input); ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polygons &input); +ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const ExPolygons &input); ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polylines &input); Slic3r::Polygon ClipperPath_to_Slic3rPolygon(const ClipperLib::Path &input); Slic3r::Polyline ClipperPath_to_Slic3rPolyline(const ClipperLib::Path &input); @@ -223,8 +224,19 @@ inline Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons &subject1, const Sli } ClipperLib::PolyTree union_pt(const Slic3r::Polygons &subject, bool safety_offset_ = false); +ClipperLib::PolyTree union_pt(const Slic3r::ExPolygons &subject, bool safety_offset_ = false); +ClipperLib::PolyTree union_pt(Slic3r::Polygons &&subject, bool safety_offset_ = false); +ClipperLib::PolyTree union_pt(Slic3r::ExPolygons &&subject, bool safety_offset_ = false); + Slic3r::Polygons union_pt_chained(const Slic3r::Polygons &subject, bool safety_offset_ = false); -void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval); + +void traverse_pt(const ClipperLib::PolyNodes &nodes, Slic3r::Polygons *retval); +void traverse_pt(const ClipperLib::PolyNodes &nodes, Slic3r::ExPolygons *retval); +void traverse_pt(const ClipperLib::PolyNode *tree, Slic3r::ExPolygons *retval); + +void traverse_pt_unordered(const ClipperLib::PolyNodes &nodes, Slic3r::Polygons *retval); +void traverse_pt_unordered(const ClipperLib::PolyNodes &nodes, Slic3r::ExPolygons *retval); +void traverse_pt_unordered(const ClipperLib::PolyNode *tree, Slic3r::ExPolygons *retval); /* OTHER */ Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false); @@ -234,6 +246,11 @@ void safety_offset(ClipperLib::Paths* paths); Polygons top_level_islands(const Slic3r::Polygons &polygons); +Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit = 2.); +Polygons variable_offset_outer(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit = 2.); +ExPolygons variable_offset_outer_ex(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit = 2.); +ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit = 2.); + } #endif diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 93044c719..77937cd4e 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -280,7 +280,7 @@ ConfigOption* ConfigOptionDef::create_default_option() const // Special case: For a DynamicConfig, convert a templated enum to a generic enum. new ConfigOptionEnumGeneric(this->enum_keys_map, this->default_value->getInt()) : this->default_value->clone(); - return this->create_empty_option(); + return this->create_empty_option(); } // Assignment of the serialization IDs is not thread safe. The Defs shall be initialized from the main thread! @@ -302,8 +302,6 @@ ConfigOptionDef* ConfigDef::add_nullable(const t_config_option_key &opt_key, Con return def; } -std::string ConfigOptionDef::nocli = "~~~noCLI"; - std::ostream& ConfigDef::print_cli_help(std::ostream& out, bool show_defaults, std::function filter) const { // prepare a function for wrapping text @@ -477,7 +475,30 @@ std::string ConfigBase::opt_serialize(const t_config_option_key &opt_key) const return opt->serialize(); } -bool ConfigBase::set_deserialize(const t_config_option_key &opt_key_src, const std::string &value_src, bool append) +void ConfigBase::set(const std::string &opt_key, int value, bool create) +{ + ConfigOption *opt = this->option_throw(opt_key, create); + switch (opt->type()) { + case coInt: static_cast(opt)->value = value; break; + case coFloat: static_cast(opt)->value = value; break; + case coFloatOrPercent: static_cast(opt)->value = value; static_cast(opt)->percent = false; break; + case coString: static_cast(opt)->value = std::to_string(value); break; + default: throw BadOptionTypeException("Configbase::set() - conversion from int not possible"); + } +} + +void ConfigBase::set(const std::string &opt_key, double value, bool create) +{ + ConfigOption *opt = this->option_throw(opt_key, create); + switch (opt->type()) { + case coFloat: static_cast(opt)->value = value; break; + case coFloatOrPercent: static_cast(opt)->value = value; static_cast(opt)->percent = false; break; + case coString: static_cast(opt)->value = std::to_string(value); break; + default: throw BadOptionTypeException("Configbase::set() - conversion from float not possible"); + } +} + +bool ConfigBase::set_deserialize_nothrow(const t_config_option_key &opt_key_src, const std::string &value_src, bool append) { t_config_option_key opt_key = opt_key_src; std::string value = value_src; @@ -490,6 +511,18 @@ bool ConfigBase::set_deserialize(const t_config_option_key &opt_key_src, const s return this->set_deserialize_raw(opt_key, value, append); } +void ConfigBase::set_deserialize(const t_config_option_key &opt_key_src, const std::string &value_src, bool append) +{ + if (! this->set_deserialize_nothrow(opt_key_src, value_src, append)) + throw BadOptionTypeException("ConfigBase::set_deserialize() failed"); +} + +void ConfigBase::set_deserialize(std::initializer_list items) +{ + for (const SetDeserializeItem &item : items) + this->set_deserialize(item.opt_key, item.opt_value, item.append); +} + bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, const std::string &value, bool append) { t_config_option_key opt_key = opt_key_src; @@ -729,6 +762,12 @@ void ConfigBase::null_nullables() } } +DynamicConfig::DynamicConfig(const ConfigBase& rhs, const t_config_option_keys& keys) +{ + for (const t_config_option_key& opt_key : keys) + this->options[opt_key] = std::unique_ptr(rhs.option(opt_key)->clone()); +} + bool DynamicConfig::operator==(const DynamicConfig &rhs) const { auto it1 = this->options.begin(); @@ -878,7 +917,7 @@ bool DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra, static_cast(opt_base)->value = value; } else { // Any scalar value of a type different from Bool and String. - if (! this->set_deserialize(opt_key, value, false)) { + if (! this->set_deserialize_nothrow(opt_key, value, false)) { boost::nowide::cerr << "Invalid value supplied for --" << token.c_str() << std::endl; return false; } diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 79d9b568e..9d63555fd 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -95,6 +95,16 @@ public: std::runtime_error(std::string("Configuration exception: ") + opt_key) {} }; +/// Indicate that an unsupported accessor was called on a config option. +class BadOptionTypeException : public std::runtime_error +{ +public: + BadOptionTypeException() : + std::runtime_error("Bad option type exception") {} + BadOptionTypeException(const char* message) : + std::runtime_error(message) {} +}; + // Type of a configuration value. enum ConfigOptionType { coVectorType = 0x4000, @@ -145,6 +155,8 @@ enum PrinterTechnology : uint8_t ptSLA = 1 << 1, // Selective Laser-Sintering ptSLS = 1 << 2, + // Any technology, useful for parameters compatible with both ptFFF and ptSLA + ptAny = 1+2+4, // Unknown, useful for command line processing ptUnknown = 1 << 7 }; @@ -172,10 +184,10 @@ public: virtual ConfigOption* clone() const = 0; // Set a value from a ConfigOption. The two options should be compatible. virtual void set(const ConfigOption *option) = 0; - virtual int getInt() const { throw std::runtime_error("Calling ConfigOption::getInt on a non-int ConfigOption"); } - virtual double getFloat() const { throw std::runtime_error("Calling ConfigOption::getFloat on a non-float ConfigOption"); } - virtual bool getBool() const { throw std::runtime_error("Calling ConfigOption::getBool on a non-boolean ConfigOption"); } - virtual void setInt(int /* val */) { throw std::runtime_error("Calling ConfigOption::setInt on a non-int ConfigOption"); } + virtual int getInt() const { throw BadOptionTypeException("Calling ConfigOption::getInt on a non-int ConfigOption"); } + virtual double getFloat() const { throw BadOptionTypeException("Calling ConfigOption::getFloat on a non-float ConfigOption"); } + virtual bool getBool() const { throw BadOptionTypeException("Calling ConfigOption::getBool on a non-boolean ConfigOption"); } + virtual void setInt(int /* val */) { throw BadOptionTypeException("Calling ConfigOption::setInt on a non-int ConfigOption"); } virtual bool operator==(const ConfigOption &rhs) const = 0; bool operator!=(const ConfigOption &rhs) const { return ! (*this == rhs); } bool is_scalar() const { return (int(this->type()) & int(coVectorType)) == 0; } @@ -408,7 +420,7 @@ public: bool apply_override(const ConfigOption *rhs) override { if (this->nullable()) throw std::runtime_error("Cannot override a nullable ConfigOption."); - if (rhs->type() != this->type()) + if (rhs->type() != this->type()) throw std::runtime_error("ConfigOptionVector.apply_override() applied to different types."); auto rhs_vec = static_cast*>(rhs); if (! rhs->nullable()) { @@ -516,7 +528,7 @@ public: for (const double &v : this->values) { if (&v != &this->values.front()) ss << ","; - serialize_single_value(ss, v); + serialize_single_value(ss, v); } return ss.str(); } @@ -662,7 +674,7 @@ public: for (const int &v : this->values) { if (&v != &this->values.front()) ss << ","; - serialize_single_value(ss, v); + serialize_single_value(ss, v); } return ss.str(); } @@ -1498,7 +1510,7 @@ public: std::vector cli_args(const std::string &key) const; // Assign this key to cli to disable CLI for this option. - static std::string nocli; + static const constexpr char *nocli = "~~~noCLI"; }; // Map from a config option name to its definition. @@ -1567,32 +1579,48 @@ protected: public: // 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); } + template TYPE* option(const t_config_option_key &opt_key, bool create = false) { ConfigOption *opt = this->optptr(opt_key, create); 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); } - template - TYPE* option_throw(const t_config_option_key &opt_key, bool create = false) + + ConfigOption* option_throw(const t_config_option_key &opt_key, bool create = false) { ConfigOption *opt = this->optptr(opt_key, create); if (opt == nullptr) throw UnknownOptionException(opt_key); + 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) + { + ConfigOption *opt = this->option_throw(opt_key, create); if (opt->type() != TYPE::static_type()) - throw std::runtime_error("Conversion to a wrong type"); + throw BadOptionTypeException("Conversion to a wrong type"); 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. @@ -1605,9 +1633,40 @@ public: t_config_option_keys diff(const ConfigBase &other) const; t_config_option_keys equal(const ConfigBase &other) const; std::string opt_serialize(const t_config_option_key &opt_key) const; + + // Set a value. Convert numeric types using a C style implicit conversion / promotion model. + // Throw if option is not avaiable and create is not enabled, + // or if the conversion is not possible. + // Conversion to string is always possible. + void set(const std::string &opt_key, bool value, bool create = false) + { this->option_throw(opt_key, create)->value = value; } + void set(const std::string &opt_key, int value, bool create = false); + void set(const std::string &opt_key, double value, bool create = false); + void set(const std::string &opt_key, const char *value, bool create = false) + { this->option_throw(opt_key, create)->value = value; } + void set(const std::string &opt_key, const std::string &value, bool create = false) + { this->option_throw(opt_key, create)->value = value; } + // Set a configuration value from a string, it will call an overridable handle_legacy() // to resolve renamed and removed configuration keys. - bool set_deserialize(const t_config_option_key &opt_key, const std::string &str, bool append = false); + bool set_deserialize_nothrow(const t_config_option_key &opt_key_src, const std::string &value_src, bool append = false); + // May throw BadOptionTypeException() if the operation fails. + void set_deserialize(const t_config_option_key &opt_key, const std::string &str, bool append = false); + struct SetDeserializeItem { + SetDeserializeItem(const char *opt_key, const char *opt_value, bool append = false) : opt_key(opt_key), opt_value(opt_value), append(append) {} + SetDeserializeItem(const std::string &opt_key, const std::string &opt_value, bool append = false) : opt_key(opt_key), opt_value(opt_value), append(append) {} + SetDeserializeItem(const char *opt_key, const bool value, bool append = false) : opt_key(opt_key), opt_value(value ? "1" : "0"), append(append) {} + SetDeserializeItem(const std::string &opt_key, const bool value, bool append = false) : opt_key(opt_key), opt_value(value ? "1" : "0"), append(append) {} + SetDeserializeItem(const char *opt_key, const int value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {} + SetDeserializeItem(const std::string &opt_key, const int value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {} + SetDeserializeItem(const char *opt_key, const float value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {} + SetDeserializeItem(const std::string &opt_key, const float value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {} + SetDeserializeItem(const char *opt_key, const double value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {} + SetDeserializeItem(const std::string &opt_key, const double value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {} + std::string opt_key; std::string opt_value; bool append = false; + }; + // May throw BadOptionTypeException() if the operation fails. + void set_deserialize(std::initializer_list items); double get_abs_value(const t_config_option_key &opt_key) const; double get_abs_value(const t_config_option_key &opt_key, double ratio_over) const; @@ -1634,9 +1693,11 @@ class DynamicConfig : public virtual ConfigBase { public: DynamicConfig() {} - DynamicConfig(const DynamicConfig& other) { *this = other; } - DynamicConfig(DynamicConfig&& other) : options(std::move(other.options)) { other.options.clear(); } - virtual ~DynamicConfig() override { clear(); } + DynamicConfig(const DynamicConfig &rhs) { *this = rhs; } + DynamicConfig(DynamicConfig &&rhs) : options(std::move(rhs.options)) { rhs.options.clear(); } + explicit DynamicConfig(const ConfigBase &rhs, const t_config_option_keys &keys); + explicit DynamicConfig(const ConfigBase& rhs) : DynamicConfig(rhs, rhs.keys()) {} + virtual ~DynamicConfig() override { clear(); } // Copy a content of one DynamicConfig to another DynamicConfig. // If rhs.def() is not null, then it has to be equal to this->def(). diff --git a/src/libslic3r/EdgeGrid.cpp b/src/libslic3r/EdgeGrid.cpp index c69c52179..587afd372 100644 --- a/src/libslic3r/EdgeGrid.cpp +++ b/src/libslic3r/EdgeGrid.cpp @@ -46,11 +46,29 @@ void EdgeGrid::Grid::create(const Polygons &polygons, coord_t resolution) ++ ncontours; // Collect the contours. - m_contours.assign(ncontours, NULL); + m_contours.assign(ncontours, nullptr); ncontours = 0; for (size_t j = 0; j < polygons.size(); ++ j) if (! polygons[j].points.empty()) - m_contours[ncontours++] = &polygons[j].points; + m_contours[ncontours ++] = &polygons[j].points; + + create_from_m_contours(resolution); +} + +void EdgeGrid::Grid::create(const std::vector &polygons, coord_t resolution) +{ + // Count the contours. + size_t ncontours = 0; + for (size_t j = 0; j < polygons.size(); ++ j) + if (! polygons[j].empty()) + ++ ncontours; + + // Collect the contours. + m_contours.assign(ncontours, nullptr); + ncontours = 0; + for (size_t j = 0; j < polygons.size(); ++ j) + if (! polygons[j].empty()) + m_contours[ncontours ++] = &polygons[j]; create_from_m_contours(resolution); } @@ -66,7 +84,7 @@ void EdgeGrid::Grid::create(const ExPolygon &expoly, coord_t resolution) ++ ncontours; // Collect the contours. - m_contours.assign(ncontours, NULL); + m_contours.assign(ncontours, nullptr); ncontours = 0; if (! expoly.contour.points.empty()) m_contours[ncontours++] = &expoly.contour.points; @@ -91,7 +109,7 @@ void EdgeGrid::Grid::create(const ExPolygons &expolygons, coord_t resolution) } // Collect the contours. - m_contours.assign(ncontours, NULL); + m_contours.assign(ncontours, nullptr); ncontours = 0; for (size_t i = 0; i < expolygons.size(); ++ i) { const ExPolygon &expoly = expolygons[i]; @@ -113,6 +131,7 @@ void EdgeGrid::Grid::create(const ExPolygonCollection &expolygons, coord_t resol // m_contours has been initialized. Now fill in the edge grid. void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) { + assert(resolution > 0); // 1) Measure the bounding box. for (size_t i = 0; i < m_contours.size(); ++ i) { const Slic3r::Points &pts = *m_contours[i]; @@ -281,7 +300,11 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) Visitor(std::vector> &cell_data, std::vector &cells, size_t cols) : cell_data(cell_data), cells(cells), cols(cols), i(0), j(0) {} - void operator()(coord_t iy, coord_t ix) { cell_data[cells[iy*cols + ix].end++] = std::pair(i, j); } + inline bool operator()(coord_t iy, coord_t ix) { + cell_data[cells[iy*cols + ix].end++] = std::pair(i, j); + // Continue traversing the grid along the edge. + return true; + } std::vector> &cell_data; std::vector &cells; @@ -1017,8 +1040,139 @@ float EdgeGrid::Grid::signed_distance_bilinear(const Point &pt) const return f; } - -bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radius, coordf_t &result_min_dist, bool *pon_segment) const { + +EdgeGrid::Grid::ClosestPointResult EdgeGrid::Grid::closest_point(const Point &pt, coord_t search_radius) const +{ + BoundingBox bbox; + bbox.min = bbox.max = Point(pt(0) - m_bbox.min(0), pt(1) - m_bbox.min(1)); + bbox.defined = true; + // Upper boundary, round to grid and test validity. + bbox.max(0) += search_radius; + bbox.max(1) += search_radius; + ClosestPointResult result; + if (bbox.max(0) < 0 || bbox.max(1) < 0) + return result; + bbox.max(0) /= m_resolution; + bbox.max(1) /= m_resolution; + if ((size_t)bbox.max(0) >= m_cols) + bbox.max(0) = m_cols - 1; + if ((size_t)bbox.max(1) >= m_rows) + bbox.max(1) = m_rows - 1; + // Lower boundary, round to grid and test validity. + bbox.min(0) -= search_radius; + bbox.min(1) -= search_radius; + if (bbox.min(0) < 0) + bbox.min(0) = 0; + if (bbox.min(1) < 0) + bbox.min(1) = 0; + bbox.min(0) /= m_resolution; + bbox.min(1) /= m_resolution; + // Is the interval empty? + if (bbox.min(0) > bbox.max(0) || + bbox.min(1) > bbox.max(1)) + return result; + // Traverse all cells in the bounding box. + double d_min = double(search_radius); + // Signum of the distance field at pt. + int sign_min = 0; + double l2_seg_min = 1.; + for (int r = bbox.min(1); r <= bbox.max(1); ++ r) { + for (int c = bbox.min(0); c <= bbox.max(0); ++ c) { + const Cell &cell = m_cells[r * m_cols + c]; + for (size_t i = cell.begin; i < cell.end; ++ i) { + const size_t contour_idx = m_cell_data[i].first; + const Slic3r::Points &pts = *m_contours[contour_idx]; + size_t ipt = m_cell_data[i].second; + // End points of the line segment. + const Slic3r::Point &p1 = pts[ipt]; + const Slic3r::Point &p2 = pts[(ipt + 1 == pts.size()) ? 0 : ipt + 1]; + const Slic3r::Point v_seg = p2 - p1; + const Slic3r::Point v_pt = pt - p1; + // dot(p2-p1, pt-p1) + int64_t t_pt = int64_t(v_seg(0)) * int64_t(v_pt(0)) + int64_t(v_seg(1)) * int64_t(v_pt(1)); + // l2 of seg + int64_t l2_seg = int64_t(v_seg(0)) * int64_t(v_seg(0)) + int64_t(v_seg(1)) * int64_t(v_seg(1)); + if (t_pt < 0) { + // Closest to p1. + double dabs = sqrt(int64_t(v_pt(0)) * int64_t(v_pt(0)) + int64_t(v_pt(1)) * int64_t(v_pt(1))); + if (dabs < d_min) { + // Previous point. + const Slic3r::Point &p0 = pts[(ipt == 0) ? (pts.size() - 1) : ipt - 1]; + Slic3r::Point v_seg_prev = p1 - p0; + int64_t t2_pt = int64_t(v_seg_prev(0)) * int64_t(v_pt(0)) + int64_t(v_seg_prev(1)) * int64_t(v_pt(1)); + if (t2_pt > 0) { + // Inside the wedge between the previous and the next segment. + d_min = dabs; + // Set the signum depending on whether the vertex is convex or reflex. + int64_t det = int64_t(v_seg_prev(0)) * int64_t(v_seg(1)) - int64_t(v_seg_prev(1)) * int64_t(v_seg(0)); + assert(det != 0); + sign_min = (det > 0) ? 1 : -1; + result.contour_idx = contour_idx; + result.start_point_idx = ipt; + result.t = 0.; +#ifndef NDEBUG + Vec2d vfoot = (p1 - pt).cast(); + double dist_foot = vfoot.norm(); + double dist_foot_err = dist_foot - d_min; + assert(std::abs(dist_foot_err) < 1e-7 * d_min); +#endif /* NDEBUG */ + } + } + } + else if (t_pt > l2_seg) { + // Closest to p2. Then p2 is the starting point of another segment, which shall be discovered in the same cell. + continue; + } else { + // Closest to the segment. + assert(t_pt >= 0 && t_pt <= l2_seg); + int64_t d_seg = int64_t(v_seg(1)) * int64_t(v_pt(0)) - int64_t(v_seg(0)) * int64_t(v_pt(1)); + double d = double(d_seg) / sqrt(double(l2_seg)); + double dabs = std::abs(d); + if (dabs < d_min) { + d_min = dabs; + sign_min = (d_seg < 0) ? -1 : ((d_seg == 0) ? 0 : 1); + l2_seg_min = l2_seg; + result.contour_idx = contour_idx; + result.start_point_idx = ipt; + result.t = t_pt; +#ifndef NDEBUG + Vec2d foot = p1.cast() * (1. - result.t / l2_seg_min) + p2.cast() * (result.t / l2_seg_min); + Vec2d vfoot = foot - pt.cast(); + double dist_foot = vfoot.norm(); + double dist_foot_err = dist_foot - d_min; + assert(std::abs(dist_foot_err) < 1e-7 || std::abs(dist_foot_err) < 1e-7 * d_min); +#endif /* NDEBUG */ + } + } + } + } + } + if (result.contour_idx != -1 && d_min <= double(search_radius)) { + result.distance = d_min * sign_min; + result.t /= l2_seg_min; + assert(result.t >= 0. && result.t < 1.); +#ifndef NDEBUG + { + const Slic3r::Points &pts = *m_contours[result.contour_idx]; + const Slic3r::Point &p1 = pts[result.start_point_idx]; + const Slic3r::Point &p2 = pts[(result.start_point_idx + 1 == pts.size()) ? 0 : result.start_point_idx + 1]; + Vec2d vfoot; + if (result.t == 0) + vfoot = p1.cast() - pt.cast(); + else + vfoot = p1.cast() * (1. - result.t) + p2.cast() * result.t - pt.cast(); + double dist_foot = vfoot.norm(); + double dist_foot_err = dist_foot - std::abs(result.distance); + assert(std::abs(dist_foot_err) < 1e-7 || std::abs(dist_foot_err) < 1e-7 * std::abs(result.distance)); + } +#endif /* NDEBUG */ + } else + result = ClosestPointResult(); + return result; +} + +bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radius, coordf_t &result_min_dist, bool *pon_segment) const +{ BoundingBox bbox; bbox.min = bbox.max = Point(pt(0) - m_bbox.min(0), pt(1) - m_bbox.min(1)); bbox.defined = true; @@ -1047,7 +1201,7 @@ bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radiu bbox.min(1) > bbox.max(1)) return false; // Traverse all cells in the bounding box. - float d_min = search_radius; + double d_min = double(search_radius); // Signum of the distance field at pt. int sign_min = 0; bool on_segment = false; diff --git a/src/libslic3r/EdgeGrid.hpp b/src/libslic3r/EdgeGrid.hpp index cad20e07b..81517a5c4 100644 --- a/src/libslic3r/EdgeGrid.hpp +++ b/src/libslic3r/EdgeGrid.hpp @@ -21,10 +21,13 @@ public: void set_bbox(const BoundingBox &bbox) { m_bbox = bbox; } void create(const Polygons &polygons, coord_t resolution); + void create(const std::vector &polygons, coord_t resolution); void create(const ExPolygon &expoly, coord_t resolution); void create(const ExPolygons &expolygons, coord_t resolution); void create(const ExPolygonCollection &expolygons, coord_t resolution); + const std::vector& contours() const { return m_contours; } + #if 0 // Test, whether the edges inside the grid intersect with the polygons provided. bool intersect(const MultiPoint &polyline, bool closed); @@ -46,7 +49,19 @@ public: float signed_distance_bilinear(const Point &pt) const; // Calculate a signed distance to the contours in search_radius from the point. - bool signed_distance_edges(const Point &pt, coord_t search_radius, coordf_t &result_min_dist, bool *pon_segment = NULL) const; + struct ClosestPointResult { + size_t contour_idx = size_t(-1); + size_t start_point_idx = size_t(-1); + // Signed distance to the closest point. + double distance = std::numeric_limits::max(); + // Parameter of the closest point on edge starting with start_point_idx <0, 1) + double t = 0.; + + bool valid() const { return contour_idx != size_t(-1); } + }; + ClosestPointResult closest_point(const Point &pt, coord_t search_radius) const; + + bool signed_distance_edges(const Point &pt, coord_t search_radius, coordf_t &result_min_dist, bool *pon_segment = nullptr) const; // Calculate a signed distance to the contours in search_radius from the point. If no edge is found in search_radius, // return an interpolated value from m_signed_distance_field, if it exists. @@ -65,7 +80,7 @@ public: std::vector> intersecting_edges() const; bool has_intersecting_edges() const; - template void visit_cells_intersecting_line(Slic3r::Point p1, Slic3r::Point p2, FUNCTION func) const + template void visit_cells_intersecting_line(Slic3r::Point p1, Slic3r::Point p2, VISITOR &visitor) const { // End points of the line segment. p1(0) -= m_bbox.min(0); @@ -82,8 +97,7 @@ public: assert(ixb >= 0 && size_t(ixb) < m_cols); assert(iyb >= 0 && size_t(iyb) < m_rows); // Account for the end points. - func(iy, ix); - if (ix == ixb && iy == iyb) + if (! visitor(iy, ix) || (ix == ixb && iy == iyb)) // Both ends fall into the same cell. return; // Raster the centeral part of the line. @@ -113,7 +127,8 @@ public: ey = int64_t(dx) * m_resolution; iy += 1; } - func(iy, ix); + if (! visitor(iy, ix)) + return; } while (ix != ixb || iy != iyb); } else { @@ -131,7 +146,8 @@ public: ey = int64_t(dx) * m_resolution; iy -= 1; } - func(iy, ix); + if (! visitor(iy, ix)) + return; } while (ix != ixb || iy != iyb); } } @@ -153,7 +169,8 @@ public: ey = int64_t(dx) * m_resolution; iy += 1; } - func(iy, ix); + if (! visitor(iy, ix)) + return; } while (ix != ixb || iy != iyb); } else { @@ -185,12 +202,32 @@ public: ey = int64_t(dx) * m_resolution; iy -= 1; } - func(iy, ix); + if (! visitor(iy, ix)) + return; } while (ix != ixb || iy != iyb); } } } + template void visit_cells_intersecting_box(BoundingBox bbox, VISITOR &visitor) const + { + // End points of the line segment. + bbox.min -= m_bbox.min; + bbox.max -= m_bbox.min + Point(1, 1); + // Get the cells of the end points. + bbox.min /= m_resolution; + bbox.max /= m_resolution; + // Trim with the cells. + bbox.min.x() = std::max(bbox.min.x(), 0); + bbox.min.y() = std::max(bbox.min.y(), 0); + bbox.max.x() = std::min(bbox.max.x(), (coord_t)m_cols - 1); + bbox.max.y() = std::min(bbox.max.y(), (coord_t)m_rows - 1); + for (coord_t iy = bbox.min.y(); iy <= bbox.max.y(); ++ iy) + for (coord_t ix = bbox.min.x(); ix <= bbox.max.x(); ++ ix) + if (! visitor(iy, ix)) + return; + } + std::pair>::const_iterator, std::vector>::const_iterator> cell_data_range(coord_t row, coord_t col) const { const EdgeGrid::Grid::Cell &cell = m_cells[row * m_cols + col]; diff --git a/src/libslic3r/ElephantFootCompensation.cpp b/src/libslic3r/ElephantFootCompensation.cpp new file mode 100644 index 000000000..69a20b5ec --- /dev/null +++ b/src/libslic3r/ElephantFootCompensation.cpp @@ -0,0 +1,609 @@ +#include "clipper/clipper_z.hpp" + +#include "libslic3r.h" +#include "ClipperUtils.hpp" +#include "EdgeGrid.hpp" +#include "ExPolygon.hpp" +#include "ElephantFootCompensation.hpp" +#include "Flow.hpp" +#include "Geometry.hpp" +#include "SVG.hpp" +#include "Utils.hpp" + +#include +#include + +// #define CONTOUR_DISTANCE_DEBUG_SVG + +namespace Slic3r { + +struct ResampledPoint { + ResampledPoint(size_t idx_src, bool interpolated, double curve_parameter) : idx_src(idx_src), interpolated(interpolated), curve_parameter(curve_parameter) {} + + size_t idx_src; + // Is this point interpolated or initial? + bool interpolated; + // Euclidean distance along the curve from the 0th point. + double curve_parameter; +}; + +// Distance calculated using SDF (Shape Diameter Function). +// The distance is calculated by casting a fan of rays and measuring the intersection distance. +// Thus the calculation is relatively slow. For the Elephant foot compensation purpose, this distance metric does not avoid +// pinching off small pieces of a contour, thus this function has been superseded by contour_distance2(). +std::vector contour_distance(const EdgeGrid::Grid &grid, const size_t idx_contour, const Slic3r::Points &contour, const std::vector &resampled_point_parameters, double search_radius) +{ + assert(! contour.empty()); + assert(contour.size() >= 2); + + std::vector out; + + if (contour.size() > 2) + { +#ifdef CONTOUR_DISTANCE_DEBUG_SVG + static int iRun = 0; + ++ iRun; + BoundingBox bbox = get_extents(contour); + bbox.merge(grid.bbox()); + ExPolygon expoly_grid; + expoly_grid.contour = Polygon(*grid.contours().front()); + for (size_t i = 1; i < grid.contours().size(); ++ i) + expoly_grid.holes.emplace_back(Polygon(*grid.contours()[i])); +#endif + struct Visitor { + Visitor(const EdgeGrid::Grid &grid, const size_t idx_contour, const std::vector &resampled_point_parameters, double dist_same_contour_reject) : + grid(grid), idx_contour(idx_contour), resampled_point_parameters(resampled_point_parameters), dist_same_contour_reject(dist_same_contour_reject) {} + + void init(const size_t aidx_point_start, const Point &apt_start, Vec2d dir, const double radius) { + this->idx_point_start = aidx_point_start; + this->pt = apt_start.cast() + SCALED_EPSILON * dir; + dir *= radius; + this->pt_start = this->pt.cast(); + // Trim the vector by the grid's bounding box. + const BoundingBox &bbox = this->grid.bbox(); + double t = 1.; + for (size_t axis = 0; axis < 2; ++ axis) { + double dx = std::abs(dir(axis)); + if (dx >= EPSILON) { + double tedge = (dir(axis) > 0) ? (double(bbox.max(axis)) - SCALED_EPSILON - this->pt(axis)) : (this->pt(axis) - double(bbox.min(axis)) - SCALED_EPSILON); + if (tedge < dx) + t = std::min(t, tedge / dx); + } + } + this->dir = dir; + if (t < 1.) + dir *= t; + this->pt_end = (this->pt + dir).cast(); + this->t_min = 1.; + assert(this->grid.bbox().contains(this->pt_start) && this->grid.bbox().contains(this->pt_end)); + } + + bool operator()(coord_t iy, coord_t ix) { + // Called with a row and colum of the grid cell, which is intersected by a line. + auto cell_data_range = this->grid.cell_data_range(iy, ix); + bool valid = true; + for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++ it_contour_and_segment) { + // End points of the line segment and their vector. + auto segment = this->grid.segment(*it_contour_and_segment); + if (Geometry::segments_intersect(segment.first, segment.second, this->pt_start, this->pt_end)) { + // The two segments intersect. Calculate the intersection. + Vec2d pt2 = segment.first.cast(); + Vec2d dir2 = segment.second.cast() - pt2; + Vec2d vptpt2 = pt - pt2; + double denom = dir(0) * dir2(1) - dir2(0) * dir(1); + + if (std::abs(denom) >= EPSILON) { + double t = cross2(dir2, vptpt2) / denom; + assert(t > - EPSILON && t < 1. + EPSILON); + bool this_valid = true; + if (it_contour_and_segment->first == idx_contour) { + // The intersected segment originates from the same contour as the starting point. + // Reject the intersection if it is close to the starting point. + // Find the start and end points of this segment + double param_lo = resampled_point_parameters[idx_point_start].curve_parameter; + double param_hi; + double param_end = resampled_point_parameters.back().curve_parameter; + { + const Slic3r::Points &ipts = *grid.contours()[it_contour_and_segment->first]; + size_t ipt = it_contour_and_segment->second; + ResampledPoint key(ipt, false, 0.); + auto lower = [](const ResampledPoint& l, const ResampledPoint r) { return l.idx_src < r.idx_src || (l.idx_src == r.idx_src && int(l.interpolated) > int(r.interpolated)); }; + auto it = std::lower_bound(resampled_point_parameters.begin(), resampled_point_parameters.end(), key, lower); + assert(it != resampled_point_parameters.end() && it->idx_src == ipt && ! it->interpolated); + double t2 = cross2(dir, vptpt2) / denom; + assert(t2 > - EPSILON && t2 < 1. + EPSILON); + if (++ ipt == ipts.size()) + param_hi = t2 * dir2.norm(); + else + param_hi = it->curve_parameter + t2 * dir2.norm(); + } + if (param_lo > param_hi) + std::swap(param_lo, param_hi); + assert(param_lo >= 0. && param_lo <= param_end); + assert(param_hi >= 0. && param_hi <= param_end); + this_valid = param_hi > param_lo + dist_same_contour_reject && param_hi - param_end < param_lo - dist_same_contour_reject; + } + if (t < this->t_min) { + this->t_min = t; + valid = this_valid; + } + } + } + if (! valid) + this->t_min = 1.; + } + // Continue traversing the grid along the edge. + return true; + } + + const EdgeGrid::Grid &grid; + const size_t idx_contour; + const std::vector &resampled_point_parameters; + const double dist_same_contour_reject; + + size_t idx_point_start; + Point pt_start; + Point pt_end; + Vec2d pt; + Vec2d dir; + // Minium parameter along the vector (pt_end - pt_start). + double t_min; + } visitor(grid, idx_contour, resampled_point_parameters, search_radius); + + const Point *pt_this = &contour.back(); + size_t idx_pt_this = contour.size() - 1; + const Point *pt_prev = pt_this - 1; + // perpenduclar vector + auto perp = [](const Vec2d& v) -> Vec2d { return Vec2d(v.y(), -v.x()); }; + Vec2d vprev = (*pt_this - *pt_prev).cast().normalized(); + out.reserve(contour.size() + 1); + for (const Point &pt_next : contour) { + Vec2d vnext = (pt_next - *pt_this).cast().normalized(); + Vec2d dir = - (perp(vprev) + perp(vnext)).normalized(); + Vec2d dir_perp = perp(dir); + double cross = cross2(vprev, vnext); + double dot = vprev.dot(vnext); + double a = (cross < 0 || dot > 0.5) ? (M_PI / 3.) : (0.48 * acos(std::min(1., - dot))); + // Throw rays, collect distances. + std::vector distances; + int num_rays = 15; + +#ifdef CONTOUR_DISTANCE_DEBUG_SVG + SVG svg(debug_out_path("contour_distance_raycasted-%d-%d.svg", iRun, &pt_next - contour.data()).c_str(), bbox); + svg.draw(expoly_grid); + svg.draw_outline(Polygon(contour), "blue", scale_(0.01)); + svg.draw(*pt_this, "red", coord_t(scale_(0.1))); +#endif /* CONTOUR_DISTANCE_DEBUG_SVG */ + + for (int i = - num_rays + 1; i < num_rays; ++ i) { + double angle = a * i / (int)num_rays; + double c = cos(angle); + double s = sin(angle); + Vec2d v = c * dir + s * dir_perp; + visitor.init(idx_pt_this, *pt_this, v, search_radius); + grid.visit_cells_intersecting_line(visitor.pt_start, visitor.pt_end, visitor); + distances.emplace_back(visitor.t_min); +#ifdef CONTOUR_DISTANCE_DEBUG_SVG + svg.draw(Line(visitor.pt_start, visitor.pt_end), "yellow", scale_(0.01)); + if (visitor.t_min < 1.) { + Vec2d pt = visitor.pt + visitor.dir * visitor.t_min; + svg.draw(Point(pt), "red", coord_t(scale_(0.1))); + } +#endif /* CONTOUR_DISTANCE_DEBUG_SVG */ + } +#ifdef CONTOUR_DISTANCE_DEBUG_SVG + svg.Close(); +#endif /* CONTOUR_DISTANCE_DEBUG_SVG */ + std::sort(distances.begin(), distances.end()); +#if 0 + double median = distances[distances.size() / 2]; + double standard_deviation = 0; + for (double d : distances) + standard_deviation += (d - median) * (d - median); + standard_deviation = sqrt(standard_deviation / (distances.size() - 1)); + double avg = 0; + size_t cnt = 0; + for (double d : distances) + if (d > median - standard_deviation - EPSILON && d < median + standard_deviation + EPSILON) { + avg += d; + ++ cnt; + } + avg /= double(cnt); + out.emplace_back(float(avg * search_radius)); +#else + out.emplace_back(float(distances.front() * search_radius)); +#endif +#ifdef CONTOUR_DISTANCE_DEBUG_SVG + printf("contour_distance_raycasted-%d-%d.svg - distance %lf\n", iRun, int(&pt_next - contour.data()), unscale(out.back())); +#endif /* CONTOUR_DISTANCE_DEBUG_SVG */ + pt_this = &pt_next; + idx_pt_this = &pt_next - contour.data(); + vprev = vnext; + } + // Rotate the vector by one item. + out.emplace_back(out.front()); + out.erase(out.begin()); + } + + return out; +} + +// Contour distance by measuring the closest point of an ExPolygon stored inside the EdgeGrid, while filtering out points of the same contour +// at concave regions, or convex regions with low curvature (curvature is estimated as a ratio between contour length and chordal distance crossing the contour ends). +std::vector contour_distance2(const EdgeGrid::Grid &grid, const size_t idx_contour, const Slic3r::Points &contour, const std::vector &resampled_point_parameters, double compensation, double search_radius) +{ + assert(! contour.empty()); + assert(contour.size() >= 2); + + std::vector out; + + if (contour.size() > 2) + { +#ifdef CONTOUR_DISTANCE_DEBUG_SVG + static int iRun = 0; + ++ iRun; + BoundingBox bbox = get_extents(contour); + bbox.merge(grid.bbox()); + ExPolygon expoly_grid; + expoly_grid.contour = Polygon(*grid.contours().front()); + for (size_t i = 1; i < grid.contours().size(); ++ i) + expoly_grid.holes.emplace_back(Polygon(*grid.contours()[i])); +#endif + struct Visitor { + Visitor(const EdgeGrid::Grid &grid, const size_t idx_contour, const std::vector &resampled_point_parameters, double dist_same_contour_accept, double dist_same_contour_reject) : + grid(grid), idx_contour(idx_contour), contour(*grid.contours()[idx_contour]), resampled_point_parameters(resampled_point_parameters), dist_same_contour_accept(dist_same_contour_accept), dist_same_contour_reject(dist_same_contour_reject) {} + + void init(const Points &contour, const Point &apoint) { + this->idx_point = &apoint - contour.data(); + this->point = apoint; + this->found = false; + this->dir_inside = this->dir_inside_at_point(contour, this->idx_point); + } + + bool operator()(coord_t iy, coord_t ix) { + // Called with a row and colum of the grid cell, which is intersected by a line. + auto cell_data_range = this->grid.cell_data_range(iy, ix); + for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++ it_contour_and_segment) { + // End points of the line segment and their vector. + std::pair segment = this->grid.segment(*it_contour_and_segment); + const Vec2d v = (segment.second - segment.first).cast(); + const Vec2d va = (this->point - segment.first).cast(); + const double l2 = v.squaredNorm(); // avoid a sqrt + const double t = (l2 == 0.0) ? 0. : clamp(0., 1., va.dot(v) / l2); + // Closest point from this->point to the segment. + const Vec2d foot = segment.first.cast() + t * v; + const Vec2d bisector = foot - this->point.cast(); + const double dist = bisector.norm(); + if ((! this->found || dist < this->distance) && this->dir_inside.dot(bisector) > 0) { + bool accept = true; + if (it_contour_and_segment->first == idx_contour) { + // Complex case: The closest segment originates from the same contour as the starting point. + // Reject the closest point if its distance along the contour is reasonable compared to the current contour bisector (this->pt, foot). + double param_lo = resampled_point_parameters[this->idx_point].curve_parameter; + double param_hi; + double param_end = resampled_point_parameters.back().curve_parameter; + const Slic3r::Points &ipts = *grid.contours()[it_contour_and_segment->first]; + const size_t ipt = it_contour_and_segment->second; + { + ResampledPoint key(ipt, false, 0.); + auto lower = [](const ResampledPoint& l, const ResampledPoint r) { return l.idx_src < r.idx_src || (l.idx_src == r.idx_src && int(l.interpolated) > int(r.interpolated)); }; + auto it = std::lower_bound(resampled_point_parameters.begin(), resampled_point_parameters.end(), key, lower); + assert(it != resampled_point_parameters.end() && it->idx_src == ipt && ! it->interpolated); + param_hi = t * sqrt(l2); + if (ipt + 1 < ipts.size()) + param_hi += it->curve_parameter; + } + if (param_lo > param_hi) + std::swap(param_lo, param_hi); + assert(param_lo > - SCALED_EPSILON && param_lo <= param_end + SCALED_EPSILON); + assert(param_hi > - SCALED_EPSILON && param_hi <= param_end + SCALED_EPSILON); + double dist_along_contour = std::min(param_hi - param_lo, param_lo + param_end - param_hi); + if (dist_along_contour < dist_same_contour_accept) + accept = false; + else if (dist < dist_same_contour_reject + SCALED_EPSILON) { + // this->point is close to foot. This point will only be accepted if the path along the contour is significantly + // longer than the bisector. That is, the path shall not bulge away from the bisector too much. + // Bulge is estimated by 0.6 of the circle circumference drawn around the bisector. + // Test whether the contour is convex or concave. + bool inside = + (t == 0.) ? this->inside_corner(ipts, ipt, this->point) : + (t == 1.) ? this->inside_corner(ipts, ipt + 1 == ipts.size() ? 0 : ipt + 1, this->point) : + this->left_of_segment(ipts, ipt, this->point); + accept = inside && dist_along_contour > 0.6 * M_PI * dist; + } + } + if (accept && (! this->found || dist < this->distance)) { + // Simple case: Just measure the shortest distance. + this->distance = dist; +#ifdef CONTOUR_DISTANCE_DEBUG_SVG + this->closest_point = foot.cast(); +#endif /* CONTOUR_DISTANCE_DEBUG_SVG */ + this->found = true; + } + } + } + // Continue traversing the grid. + return true; + } + + const EdgeGrid::Grid &grid; + const size_t idx_contour; + const Points &contour; + const std::vector &resampled_point_parameters; + const double dist_same_contour_accept; + const double dist_same_contour_reject; + + size_t idx_point; + Point point; + // Direction inside the contour from idx_point, not normalized. + Vec2d dir_inside; + bool found; + double distance; +#ifdef CONTOUR_DISTANCE_DEBUG_SVG + Point closest_point; +#endif /* CONTOUR_DISTANCE_DEBUG_SVG */ + + private: + static Vec2d dir_inside_at_point(const Points &contour, size_t i) { + size_t iprev = prev_idx_modulo(i, contour); + size_t inext = next_idx_modulo(i, contour); + Vec2d v1 = (contour[i] - contour[iprev]).cast(); + Vec2d v2 = (contour[inext] - contour[i]).cast(); + return Vec2d(- v1.y() - v2.y(), v1.x() + v2.x()); + } + static Vec2d dir_inside_at_segment(const Points& contour, size_t i) { + size_t inext = next_idx_modulo(i, contour); + Vec2d v = (contour[inext] - contour[i]).cast(); + return Vec2d(- v.y(), v.x()); + } + + static bool inside_corner(const Slic3r::Points &contour, size_t i, const Point &pt_oposite) { + const Vec2d pt = pt_oposite.cast(); + size_t iprev = prev_idx_modulo(i, contour); + size_t inext = next_idx_modulo(i, contour); + Vec2d v1 = (contour[i] - contour[iprev]).cast(); + Vec2d v2 = (contour[inext] - contour[i]).cast(); + bool left_of_v1 = cross2(v1, pt - contour[iprev].cast()) > 0.; + bool left_of_v2 = cross2(v2, pt - contour[i ].cast()) > 0.; + return cross2(v1, v2) > 0 ? + left_of_v1 && left_of_v2 : // convex corner + left_of_v1 || left_of_v2; // concave corner + } + static bool left_of_segment(const Slic3r::Points &contour, size_t i, const Point &pt_oposite) { + const Vec2d pt = pt_oposite.cast(); + size_t inext = next_idx_modulo(i, contour); + Vec2d v = (contour[inext] - contour[i]).cast(); + return cross2(v, pt - contour[i].cast()) > 0.; + } + } visitor(grid, idx_contour, resampled_point_parameters, 0.5 * compensation * M_PI, search_radius); + + out.reserve(contour.size()); + Point radius_vector(search_radius, search_radius); + for (const Point &pt : contour) { + visitor.init(contour, pt); + grid.visit_cells_intersecting_box(BoundingBox(pt - radius_vector, pt + radius_vector), visitor); + out.emplace_back(float(visitor.found ? std::min(visitor.distance, search_radius) : search_radius)); + +#if 0 +//#ifdef CONTOUR_DISTANCE_DEBUG_SVG + if (out.back() < search_radius) { + SVG svg(debug_out_path("contour_distance_filtered-%d-%d.svg", iRun, int(&pt - contour.data())).c_str(), bbox); + svg.draw(expoly_grid); + svg.draw_outline(Polygon(contour), "blue", scale_(0.01)); + svg.draw(pt, "green", coord_t(scale_(0.1))); + svg.draw(visitor.closest_point, "red", coord_t(scale_(0.1))); + printf("contour_distance_filtered-%d-%d.svg - distance %lf\n", iRun, int(&pt - contour.data()), unscale(out.back())); + } +#endif /* CONTOUR_DISTANCE_DEBUG_SVG */ + } +#ifdef CONTOUR_DISTANCE_DEBUG_SVG + if (out.back() < search_radius) { + SVG svg(debug_out_path("contour_distance_filtered-final-%d.svg", iRun).c_str(), bbox); + svg.draw(expoly_grid); + svg.draw_outline(Polygon(contour), "blue", scale_(0.01)); + for (size_t i = 0; i < contour.size(); ++ i) + svg.draw(contour[i], out[i] < float(search_radius - SCALED_EPSILON) ? "red" : "green", coord_t(scale_(0.1))); + } +#endif /* CONTOUR_DISTANCE_DEBUG_SVG */ + } + + return out; +} + +Points resample_polygon(const Points &contour, double dist, std::vector &resampled_point_parameters) +{ + Points out; + out.reserve(contour.size()); + resampled_point_parameters.reserve(contour.size()); + if (contour.size() > 2) { + Vec2d pt_prev = contour.back().cast(); + for (const Point &pt : contour) { + size_t idx_this = &pt - contour.data(); + const Vec2d pt_this = pt.cast(); + const Vec2d v = pt_this - pt_prev; + const double l = v.norm(); + const size_t n = size_t(ceil(l / dist)); + const double l_step = l / n; + for (size_t i = 1; i < n; ++ i) { + double interpolation_parameter = double(i) / n; + Vec2d new_pt = pt_prev + v * interpolation_parameter; + out.emplace_back(new_pt.cast()); + resampled_point_parameters.emplace_back(idx_this, true, l_step); + } + out.emplace_back(pt); + resampled_point_parameters.emplace_back(idx_this, false, l_step); + pt_prev = pt_this; + } + for (size_t i = 1; i < resampled_point_parameters.size(); ++i) + resampled_point_parameters[i].curve_parameter += resampled_point_parameters[i - 1].curve_parameter; + } + return out; +} + +static inline void smooth_compensation(std::vector &compensation, float strength, size_t num_iterations) +{ + std::vector out(compensation); + for (size_t iter = 0; iter < num_iterations; ++ iter) { + for (size_t i = 0; i < compensation.size(); ++ i) { + float prev = prev_value_modulo(i, compensation); + float next = next_value_modulo(i, compensation); + float laplacian = compensation[i] * (1.f - strength) + 0.5f * strength * (prev + next); + // Compensations are negative. Only apply the laplacian if it leads to lower compensation. + out[i] = std::max(laplacian, compensation[i]); + } + out.swap(compensation); + } +} + +static inline void smooth_compensation_banded(const Points &contour, float band, std::vector &compensation, float strength, size_t num_iterations) +{ + assert(contour.size() == compensation.size()); + assert(contour.size() > 2); + std::vector out(compensation); + float dist_min2 = band * band; + static constexpr bool use_min = false; + for (size_t iter = 0; iter < num_iterations; ++ iter) { + for (int i = 0; i < int(compensation.size()); ++ i) { + const Vec2f pthis = contour[i].cast(); + + int j = prev_idx_modulo(i, contour); + Vec2f pprev = contour[j].cast(); + float prev = compensation[j]; + float l2 = (pthis - pprev).squaredNorm(); + if (l2 < dist_min2) { + float l = sqrt(l2); + int jprev = exchange(j, prev_idx_modulo(j, contour)); + while (j != i) { + const Vec2f pp = contour[j].cast(); + const float lthis = (pp - pprev).norm(); + const float lnext = l + lthis; + if (lnext > band) { + // Interpolate the compensation value. + prev = use_min ? + std::min(prev, lerp(compensation[jprev], compensation[j], (band - l) / lthis)) : + lerp(compensation[jprev], compensation[j], (band - l) / lthis); + break; + } + prev = use_min ? std::min(prev, compensation[j]) : compensation[j]; + pprev = pp; + l = lnext; + jprev = exchange(j, prev_idx_modulo(j, contour)); + } + } + + j = next_idx_modulo(i, contour); + pprev = contour[j].cast(); + float next = compensation[j]; + l2 = (pprev - pthis).squaredNorm(); + if (l2 < dist_min2) { + float l = sqrt(l2); + int jprev = exchange(j, next_idx_modulo(j, contour)); + while (j != i) { + const Vec2f pp = contour[j].cast(); + const float lthis = (pp - pprev).norm(); + const float lnext = l + lthis; + if (lnext > band) { + // Interpolate the compensation value. + next = use_min ? + std::min(next, lerp(compensation[jprev], compensation[j], (band - l) / lthis)) : + lerp(compensation[jprev], compensation[j], (band - l) / lthis); + break; + } + next = use_min ? std::min(next, compensation[j]) : compensation[j]; + pprev = pp; + l = lnext; + jprev = exchange(j, next_idx_modulo(j, contour)); + } + } + + float laplacian = compensation[i] * (1.f - strength) + 0.5f * strength * (prev + next); + // Compensations are negative. Only apply the laplacian if it leads to lower compensation. + out[i] = std::max(laplacian, compensation[i]); + } + out.swap(compensation); + } +} + +ExPolygon elephant_foot_compensation(const ExPolygon &input_expoly, const Flow &external_perimeter_flow, const double compensation) +{ + // The contour shall be wide enough to apply the external perimeter plus compensation on both sides. + double min_contour_width = double(external_perimeter_flow.scaled_width() + external_perimeter_flow.scaled_spacing()); + double scaled_compensation = scale_(compensation); + double min_contour_width_compensated = min_contour_width + 2. * scaled_compensation; + // Make the search radius a bit larger for the averaging in contour_distance over a fan of rays to work. + double search_radius = min_contour_width_compensated + min_contour_width * 0.5; + + BoundingBox bbox = get_extents(input_expoly.contour); + Point bbox_size = bbox.size(); + ExPolygon out; + if (bbox_size.x() < min_contour_width_compensated + SCALED_EPSILON || + bbox_size.y() < min_contour_width_compensated + SCALED_EPSILON || + input_expoly.area() < min_contour_width_compensated * min_contour_width_compensated * 5.) + { + // The contour is tiny. Don't correct it. + out = input_expoly; + } + else + { + EdgeGrid::Grid grid; + ExPolygon simplified = input_expoly.simplify(SCALED_EPSILON).front(); + BoundingBox bbox = get_extents(simplified.contour); + bbox.offset(SCALED_EPSILON); + grid.set_bbox(bbox); + grid.create(simplified, coord_t(0.7 * search_radius)); + std::vector> deltas; + deltas.reserve(simplified.holes.size() + 1); + ExPolygon resampled(simplified); + double resample_interval = scale_(0.5); + for (size_t idx_contour = 0; idx_contour <= simplified.holes.size(); ++ idx_contour) { + Polygon &poly = (idx_contour == 0) ? resampled.contour : resampled.holes[idx_contour - 1]; + std::vector resampled_point_parameters; + poly.points = resample_polygon(poly.points, resample_interval, resampled_point_parameters); + std::vector dists = contour_distance2(grid, idx_contour, poly.points, resampled_point_parameters, scaled_compensation, search_radius); + for (float &d : dists) { + // printf("Point %d, Distance: %lf\n", int(&d - dists.data()), unscale(d)); + // Convert contour width to available compensation distance. + if (d < min_contour_width) + d = 0.f; + else if (d > min_contour_width_compensated) + d = - float(scaled_compensation); + else + d = - (d - float(min_contour_width)) / 2.f; + assert(d >= - float(scaled_compensation) && d <= 0.f); + } + // smooth_compensation(dists, 0.4f, 10); + smooth_compensation_banded(poly.points, float(0.8 * resample_interval), dists, 0.3f, 3); + deltas.emplace_back(dists); + } + + ExPolygons out_vec = variable_offset_inner_ex(resampled, deltas, 2.); + if (out_vec.size() == 1) + out = std::move(out_vec.front()); + else { + // Something went wrong, don't compensate. + out = input_expoly; +#ifdef TESTS_EXPORT_SVGS + if (out_vec.size() > 1) { + static int iRun = 0; + SVG::export_expolygons(debug_out_path("elephant_foot_compensation-many_contours-%d.svg", iRun ++).c_str(), + { { { input_expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, + { { out_vec }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } } }); + } +#endif /* TESTS_EXPORT_SVGS */ + assert(out_vec.size() == 1); + } + } + + return out; +} + +ExPolygons elephant_foot_compensation(const ExPolygons &input, const Flow &external_perimeter_flow, const double compensation) +{ + ExPolygons out; + out.reserve(input.size()); + for (const ExPolygon &expoly : input) + out.emplace_back(elephant_foot_compensation(expoly, external_perimeter_flow, compensation)); + return out; +} + +} // namespace Slic3r diff --git a/src/libslic3r/ElephantFootCompensation.hpp b/src/libslic3r/ElephantFootCompensation.hpp new file mode 100644 index 000000000..0119df1af --- /dev/null +++ b/src/libslic3r/ElephantFootCompensation.hpp @@ -0,0 +1,16 @@ +#ifndef slic3r_ElephantFootCompensation_hpp_ +#define slic3r_ElephantFootCompensation_hpp_ + +#include "libslic3r.h" +#include + +namespace Slic3r { + +class Flow; + +ExPolygon elephant_foot_compensation(const ExPolygon &input, const Flow &external_perimeter_flow, const double compensation); +ExPolygons elephant_foot_compensation(const ExPolygons &input, const Flow &external_perimeter_flow, const double compensation); + +} // Slic3r + +#endif /* slic3r_ElephantFootCompensation_hpp_ */ diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp index 3240104ea..5479a097a 100644 --- a/src/libslic3r/ExPolygon.hpp +++ b/src/libslic3r/ExPolygon.hpp @@ -18,8 +18,18 @@ class ExPolygon { public: ExPolygon() {} - ExPolygon(const ExPolygon &other) : contour(other.contour), holes(other.holes) {} + ExPolygon(const ExPolygon &other) : contour(other.contour), holes(other.holes) {} ExPolygon(ExPolygon &&other) : contour(std::move(other.contour)), holes(std::move(other.holes)) {} + explicit ExPolygon(const Polygon &contour) : contour(contour) {} + explicit ExPolygon(Polygon &&contour) : contour(std::move(contour)) {} + explicit ExPolygon(const Points &contour) : contour(contour) {} + explicit ExPolygon(Points &&contour) : contour(std::move(contour)) {} + explicit ExPolygon(const Polygon &contour, const Polygon &hole) : contour(contour) { holes.emplace_back(hole); } + explicit ExPolygon(Polygon &&contour, Polygon &&hole) : contour(std::move(contour)) { holes.emplace_back(std::move(hole)); } + explicit ExPolygon(const Points &contour, const Points &hole) : contour(contour) { holes.emplace_back(hole); } + explicit ExPolygon(Points &&contour, Polygon &&hole) : contour(std::move(contour)) { holes.emplace_back(std::move(hole)); } + ExPolygon(std::initializer_list contour) : contour(contour) {} + ExPolygon(std::initializer_list contour, std::initializer_list hole) : contour(contour), holes({ hole }) {} ExPolygon& operator=(const ExPolygon &other) { contour = other.contour; holes = other.holes; return *this; } ExPolygon& operator=(ExPolygon &&other) { contour = std::move(other.contour); holes = std::move(other.holes); return *this; } @@ -68,8 +78,16 @@ public: void triangulate_pp(Points *triangles) const; void triangulate_p2t(Polygons* polygons) const; Lines lines() const; + + // Number of contours (outer contour with holes). + size_t num_contours() const { return this->holes.size() + 1; } + Polygon& contour_or_hole(size_t idx) { return (idx == 0) ? this->contour : this->holes[idx - 1]; } + const Polygon& contour_or_hole(size_t idx) const { return (idx == 0) ? this->contour : this->holes[idx - 1]; } }; +inline bool operator==(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour == rhs.contour && lhs.holes == rhs.holes; } +inline bool operator!=(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour != rhs.contour || lhs.holes != rhs.holes; } + // Count a nuber of polygons stored inside the vector of expolygons. // Useful for allocating space for polygons when converting expolygons to polygons. inline size_t number_polygons(const ExPolygons &expolys) @@ -316,6 +334,15 @@ inline bool expolygons_contain(ExPolygons &expolys, const Point &pt) return false; } +inline ExPolygons expolygons_simplify(const ExPolygons &expolys, double tolerance) +{ + ExPolygons out; + out.reserve(expolys.size()); + for (const ExPolygon &exp : expolys) + exp.simplify(tolerance, &out); + return out; +} + extern BoundingBox get_extents(const ExPolygon &expolygon); extern BoundingBox get_extents(const ExPolygons &expolygons); extern BoundingBox get_extents_rotated(const ExPolygon &poly, double angle); diff --git a/src/libslic3r/ExPolygonCollection.cpp b/src/libslic3r/ExPolygonCollection.cpp index 6933544b6..c33df0f29 100644 --- a/src/libslic3r/ExPolygonCollection.cpp +++ b/src/libslic3r/ExPolygonCollection.cpp @@ -11,7 +11,7 @@ ExPolygonCollection::ExPolygonCollection(const ExPolygon &expolygon) ExPolygonCollection::operator Points() const { Points points; - Polygons pp = *this; + Polygons pp = (Polygons)*this; for (Polygons::const_iterator poly = pp.begin(); poly != pp.end(); ++poly) { for (Points::const_iterator point = poly->points.begin(); point != poly->points.end(); ++point) points.push_back(*point); diff --git a/src/libslic3r/ExPolygonCollection.hpp b/src/libslic3r/ExPolygonCollection.hpp index 4c181cd6a..35e1eef4e 100644 --- a/src/libslic3r/ExPolygonCollection.hpp +++ b/src/libslic3r/ExPolygonCollection.hpp @@ -13,15 +13,15 @@ typedef std::vector ExPolygonCollections; class ExPolygonCollection { - public: +public: ExPolygons expolygons; - ExPolygonCollection() {}; - ExPolygonCollection(const ExPolygon &expolygon); - ExPolygonCollection(const ExPolygons &expolygons) : expolygons(expolygons) {}; - operator Points() const; - operator Polygons() const; - operator ExPolygons&(); + ExPolygonCollection() {} + explicit ExPolygonCollection(const ExPolygon &expolygon); + explicit ExPolygonCollection(const ExPolygons &expolygons) : expolygons(expolygons) {} + explicit operator Points() const; + explicit operator Polygons() const; + explicit operator ExPolygons&(); void scale(double factor); void translate(double x, double y); void rotate(double angle, const Point ¢er); diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index 5e172afc1..803a75568 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -30,12 +30,12 @@ void ExtrusionVisitorConst::use(const ExtrusionEntityCollection &collection) { d void ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const { - this->_inflate_collection(intersection_pl(this->polyline, collection), retval); + this->_inflate_collection(intersection_pl(this->polyline, (Polygons)collection), retval); } void ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const { - this->_inflate_collection(diff_pl(this->polyline, collection), retval); + this->_inflate_collection(diff_pl(this->polyline, (Polygons)collection), retval); } void ExtrusionPath::clip_end(double distance) diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 1fc3c54d5..97ade53b7 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -5,6 +5,8 @@ #include "Polygon.hpp" #include "Polyline.hpp" +#include + namespace Slic3r { class ExPolygonCollection; @@ -12,7 +14,7 @@ class ExtrusionEntityCollection; class Extruder; // Each ExtrusionRole value identifies a distinct set of { extruder, speed } -enum ExtrusionRole { +enum ExtrusionRole : uint8_t { erNone, erPerimeter, erExternalPerimeter, @@ -132,8 +134,8 @@ public: virtual ExtrusionEntity* clone_move() = 0; virtual ~ExtrusionEntity() {} virtual void reverse() = 0; - virtual Point first_point() const = 0; - virtual Point last_point() const = 0; + virtual const Point& first_point() const = 0; + virtual const Point& last_point() const = 0; // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. virtual void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const = 0; @@ -170,30 +172,23 @@ public: float width; // Height of the extrusion, used for visualization purposes. float height; - // Feedrate of the extrusion, used for visualization purposes. - float feedrate; - // Id of the extruder, used for visualization purposes. - unsigned int extruder_id; - // Id of the color, used for visualization purposes in the color printing case. - unsigned int cp_color_id; - ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), feedrate(0.0f), extruder_id(0), cp_color_id(0), m_role(role) {} - ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), feedrate(0.0f), extruder_id(0), cp_color_id(0), m_role(role) {} - ExtrusionPath(const ExtrusionPath &rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {} - ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs) : polyline(polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {} - ExtrusionPath(ExtrusionPath &&rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {} - ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs) : polyline(std::move(polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {} -// ExtrusionPath(ExtrusionRole role, const Flow &flow) : m_role(role), mm3_per_mm(flow.mm3_per_mm()), width(flow.width), height(flow.height), feedrate(0.0f), extruder_id(0) {}; + ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), m_role(role) {}; + ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role) {}; + ExtrusionPath(const ExtrusionPath& rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} + ExtrusionPath(ExtrusionPath&& rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} + ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs) : polyline(polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} + ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs) : polyline(std::move(polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} - ExtrusionPath& operator=(const ExtrusionPath &rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate; this->extruder_id = rhs.extruder_id; this->cp_color_id = rhs.cp_color_id; this->polyline = rhs.polyline; return *this; } - ExtrusionPath& operator=(ExtrusionPath &&rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate; this->extruder_id = rhs.extruder_id; this->cp_color_id = rhs.cp_color_id; this->polyline = std::move(rhs.polyline); return *this; } + ExtrusionPath& operator=(const ExtrusionPath& rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->polyline = rhs.polyline; return *this; } + ExtrusionPath& operator=(ExtrusionPath&& rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->polyline = std::move(rhs.polyline); return *this; } virtual ExtrusionPath* clone() const override { return new ExtrusionPath(*this); } // Create a new object, initialize it with this object using the move semantics. virtual ExtrusionPath* clone_move() override { return new ExtrusionPath(std::move(*this)); } void reverse() override { this->polyline.reverse(); } - Point first_point() const override { return this->polyline.points.front(); } - Point last_point() const override { return this->polyline.points.back(); } + const Point& first_point() const override { return this->polyline.points.front(); } + const Point& last_point() const override { return this->polyline.points.back(); } size_t size() const { return this->polyline.size(); } bool empty() const { return this->polyline.empty(); } bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); } @@ -247,10 +242,10 @@ public: // ExtrusionPath(ExtrusionRole role, const Flow &flow) : m_role(role), mm3_per_mm(flow.mm3_per_mm()), width(flow.width), height(flow.height), feedrate(0.0f), extruder_id(0) {}; ExtrusionPath3D& operator=(const ExtrusionPath3D &rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; - this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->cp_color_id = rhs.cp_color_id, this->polyline = rhs.polyline; z_offsets = rhs.z_offsets; return *this; + this->polyline = rhs.polyline; z_offsets = rhs.z_offsets; return *this; } ExtrusionPath3D& operator=(ExtrusionPath3D &&rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; - this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->cp_color_id = rhs.cp_color_id, this->polyline = std::move(rhs.polyline); z_offsets = std::move(rhs.z_offsets); return *this; + this->polyline = std::move(rhs.polyline); z_offsets = std::move(rhs.z_offsets); return *this; } virtual ExtrusionPath3D* clone() const { return new ExtrusionPath3D(*this); } virtual ExtrusionPath3D* clone_move() override { return new ExtrusionPath3D(std::move(*this)); } @@ -281,8 +276,8 @@ public: bool is_loop() const override { return false; } ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); } - virtual Point first_point() const override { return this->paths.back().as_polyline().points.back(); } - virtual Point last_point() const override { return this->paths.back().as_polyline().points.back(); } + virtual const Point& first_point() const override { return this->paths.back().as_polyline().points.back(); } + virtual const Point& last_point() const override { return this->paths.back().as_polyline().points.back(); } virtual void reverse() override { for (THING &entity : this->paths) @@ -407,8 +402,8 @@ public: bool make_clockwise(); bool make_counter_clockwise(); virtual void reverse() override; - Point first_point() const override { return this->paths.front().polyline.points.front(); } - Point last_point() const override { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); } + const Point& first_point() const override { return this->paths.front().polyline.points.front(); } + const Point& last_point() const override { assert(this->first_point() == this->paths.back().polyline.points.back()); return this->first_point(); } Polygon polygon() const; double length() const override; bool split_at_vertex(const Point &point); @@ -440,6 +435,15 @@ public: //static inline std::string role_to_string(ExtrusionLoopRole role); +#ifndef NDEBUG + bool validate() const { + assert(this->first_point() == this->paths.back().polyline.points.back()); + for (size_t i = 1; i < paths.size(); ++ i) + assert(this->paths[i - 1].polyline.points.back() == this->paths[i].polyline.points.front()); + return true; + } +#endif /* NDEBUG */ + private: ExtrusionLoopRole m_loop_role; }; diff --git a/src/libslic3r/ExtrusionEntityCollection.cpp b/src/libslic3r/ExtrusionEntityCollection.cpp index ac04825fc..516974331 100644 --- a/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/src/libslic3r/ExtrusionEntityCollection.cpp @@ -1,4 +1,5 @@ #include "ExtrusionEntityCollection.hpp" +#include "ShortestPath.hpp" #include #include #include @@ -16,7 +17,6 @@ ExtrusionEntityCollection& ExtrusionEntityCollection::operator= (const Extrusion this->entities = other.entities; for (size_t i = 0; i < this->entities.size(); ++i) this->entities[i] = this->entities[i]->clone(); - this->orig_indices = other.orig_indices; this->no_sort = other.no_sort; return *this; } @@ -24,7 +24,6 @@ ExtrusionEntityCollection& ExtrusionEntityCollection::operator= (const Extrusion void ExtrusionEntityCollection::swap(ExtrusionEntityCollection &c) { std::swap(this->entities, c.entities); - std::swap(this->orig_indices, c.orig_indices); std::swap(this->no_sort, c.no_sort); } @@ -77,82 +76,31 @@ void ExtrusionEntityCollection::remove(size_t i) this->entities.erase(this->entities.begin() + i); } -ExtrusionEntityCollection ExtrusionEntityCollection::chained_path(bool no_reverse, ExtrusionRole role) const -{ - ExtrusionEntityCollection coll; - this->chained_path(&coll, no_reverse, role); - return coll; -} - -void ExtrusionEntityCollection::chained_path(ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role, std::vector* orig_indices) const -{ - if (this->entities.empty()) return; - this->chained_path_from(this->entities.front()->first_point(), retval, no_reverse, role, orig_indices); -} - -ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(Point start_near, bool no_reverse, ExtrusionRole role) const -{ - ExtrusionEntityCollection coll; - this->chained_path_from(start_near, &coll, no_reverse, role); - return coll; -} - -void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role, std::vector* orig_indices) const +ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(const Point &start_near, ExtrusionRole role) const { + ExtrusionEntityCollection out; if (this->no_sort) { - *retval = *this; - return; - } - - retval->entities.reserve(this->entities.size()); - retval->orig_indices.reserve(this->entities.size()); - - // if we're asked to return the original indices, build a map - std::map indices_map; - - ExtrusionEntitiesPtr my_paths; - for (ExtrusionEntity * const &entity_src : this->entities) { - if (role != erMixed) { - // The caller wants only paths with a specific extrusion role. - ExtrusionRole role2 = entity_src->role(); - if (role != role2) { - // This extrusion entity does not match the role asked. - assert(role2 != erMixed); - continue; + out = *this; + } else { + if (role == erMixed) + out = *this; + else { + for (const ExtrusionEntity *ee : this->entities) { + if (role != erMixed) { + // The caller wants only paths with a specific extrusion role. + auto role2 = ee->role(); + if (role != role2) { + // This extrusion entity does not match the role asked. + assert(role2 != erMixed); + continue; + } + } + out.entities.emplace_back(ee->clone()); } } - - ExtrusionEntity *entity = entity_src->clone(); - my_paths.push_back(entity); - if (orig_indices != nullptr) - indices_map[entity] = &entity_src - &this->entities.front(); - } - - Points endpoints; - for (const ExtrusionEntity *entity : my_paths) { - endpoints.push_back(entity->first_point()); - if (no_reverse || !entity->can_reverse()) { - endpoints.push_back(entity->first_point()); - } else { - endpoints.push_back(entity->last_point()); - } - } - - while (!my_paths.empty()) { - // find nearest point - int start_index = start_near.nearest_point_index(endpoints); - int path_index = start_index/2; - ExtrusionEntity* entity = my_paths.at(path_index); - // never reverse loops, since it's pointless for chained path and callers might depend on orientation - if (start_index % 2 && !no_reverse && entity->can_reverse()) - entity->reverse(); - retval->entities.push_back(my_paths.at(path_index)); - if (orig_indices != nullptr) - orig_indices->push_back(indices_map[entity]); - my_paths.erase(my_paths.begin() + path_index); - endpoints.erase(endpoints.begin() + 2*path_index, endpoints.begin() + 2*path_index + 2); - start_near = retval->entities.back()->last_point(); + chain_and_reorder_extrusion_entities(out.entities, &start_near); } + return out; } void ExtrusionEntityCollection::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const @@ -179,39 +127,9 @@ CountEntities::use(const ExtrusionEntityCollection &coll) { entity->visit(*this); } } -// -//void -//ExtrusionEntityCollection::flatten(ExtrusionEntityCollection* retval, bool preserve_ordering) const -//{ -// if (this->no_sort && preserve_ordering) { -// /// if we want to preserve ordering and we can't sort, break out the unsorted ones first. -// ExtrusionEntityCollection *unsortable = new ExtrusionEntityCollection(); -// unsortable->no_sort = true; -// unsortable->orig_indices = this->orig_indices; -// retval->append(*unsortable); -// for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) { -// if ((*it)->is_collection()) { -// ExtrusionEntityCollection* collection = dynamic_cast(*it); -// collection->flatten(unsortable, preserve_ordering); -// } else { -// unsortable->append(**it); -// } -// } -// } else { -// for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) { -// if ((*it)->is_collection()) { -// ExtrusionEntityCollection* collection = dynamic_cast(*it); -// retval->append(collection->flatten().entities); -// } else { -// retval->append(**it); -// } -// } -// } -//} -/* Returns a single vector of chained (new) pointers to all non-collection items contained in this one */ -ExtrusionEntityCollection -ExtrusionEntityCollection::flatten(bool preserve_ordering) const +// Returns a single vector of pointers to all non-collection items contained in this one. +ExtrusionEntityCollection ExtrusionEntityCollection::flatten(bool preserve_ordering) const { //ExtrusionEntityCollection coll; //this->flatten(&coll, preserve_ordering); diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp index 895c05e30..3f83a9bac 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -17,23 +17,20 @@ public: /// Owned ExtrusionEntities and descendent ExtrusionEntityCollections. /// Iterating over this needs to check each child to see if it, too is a collection. ExtrusionEntitiesPtr entities; // we own these entities - - std::vector orig_indices; // handy for XS bool no_sort; - - ExtrusionEntityCollection(): no_sort(false) {}; - ExtrusionEntityCollection(const ExtrusionEntityCollection &other) : orig_indices(other.orig_indices), no_sort(other.no_sort) { this->append(other.entities); } - ExtrusionEntityCollection(ExtrusionEntityCollection &&other) : entities(std::move(other.entities)), orig_indices(std::move(other.orig_indices)), no_sort(other.no_sort) {} + ExtrusionEntityCollection(): no_sort(false) {} + ExtrusionEntityCollection(const ExtrusionEntityCollection &other) : no_sort(other.no_sort) { this->append(other.entities); } + ExtrusionEntityCollection(ExtrusionEntityCollection &&other) : entities(std::move(other.entities)), no_sort(other.no_sort) {} explicit ExtrusionEntityCollection(const ExtrusionPaths &paths); ExtrusionEntityCollection& operator=(const ExtrusionEntityCollection &other); - ExtrusionEntityCollection& operator=(ExtrusionEntityCollection &&other) - { this->entities = std::move(other.entities); this->orig_indices = std::move(other.orig_indices); this->no_sort = other.no_sort; return *this; } + ExtrusionEntityCollection& operator=(ExtrusionEntityCollection &&other) + { this->entities = std::move(other.entities); this->no_sort = other.no_sort; return *this; } ~ExtrusionEntityCollection() { clear(); } /// Operator to convert and flatten this collection to a single vector of ExtrusionPaths. explicit operator ExtrusionPaths() const; - bool is_collection() const { return true; }; + bool is_collection() const { return true; } ExtrusionRole role() const override { ExtrusionRole out = erNone; for (const ExtrusionEntity *ee : entities) { @@ -42,8 +39,8 @@ public: } return out; } - bool can_reverse() const { return !this->no_sort; }; - bool empty() const { return this->entities.empty(); }; + bool can_reverse() const { return !this->no_sort; } + bool empty() const { return this->entities.empty(); } void clear(); void swap (ExtrusionEntityCollection &c); void append(const ExtrusionEntity &entity) { this->entities.emplace_back(entity.clone()); } @@ -73,13 +70,10 @@ public: } void replace(size_t i, const ExtrusionEntity &entity); void remove(size_t i); - ExtrusionEntityCollection chained_path(bool no_reverse = false, ExtrusionRole role = erMixed) const; - void chained_path(ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed, std::vector* orig_indices = nullptr) const; - ExtrusionEntityCollection chained_path_from(Point start_near, bool no_reverse = false, ExtrusionRole role = erMixed) const; - void chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed, std::vector* orig_indices = nullptr) const; + ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = erMixed) const; void reverse(); - Point first_point() const { return this->entities.front()->first_point(); } - Point last_point() const { return this->entities.back()->last_point(); } + const Point& first_point() const { return this->entities.front()->first_point(); } + const Point& last_point() const { return this->entities.back()->last_point(); } // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override; @@ -94,17 +88,10 @@ public: /// Recursively count paths and loops contained in this collection size_t items_count() const; - - /// Returns a single vector of pointers to all non-collection items contained in this one - /// \param retval a pointer to the output memory space. - /// \param preserve_ordering Flag to method that will flatten if and only if the underlying collection is sortable when True (default: False). - void flatten(ExtrusionEntityCollection* retval, bool preserve_ordering = false) const; - /// Returns a flattened copy of this ExtrusionEntityCollection. That is, all of the items in its entities vector are not collections. /// You should be iterating over flatten().entities if you are interested in the underlying ExtrusionEntities (and don't care about hierarchy). /// \param preserve_ordering Flag to method that will flatten if and only if the underlying collection is sortable when True (default: False). ExtrusionEntityCollection flatten(bool preserve_ordering = false) const; - double min_mm3_per_mm() const; double total_volume() const override { double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; } diff --git a/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/src/libslic3r/Fill/Fill3DHoneycomb.cpp index 4546f8ec2..32b28d3de 100644 --- a/src/libslic3r/Fill/Fill3DHoneycomb.cpp +++ b/src/libslic3r/Fill/Fill3DHoneycomb.cpp @@ -1,5 +1,5 @@ #include "../ClipperUtils.hpp" -#include "../PolylineCollection.hpp" +#include "../ShortestPath.hpp" #include "../Surface.hpp" #include "Fill3DHoneycomb.hpp" @@ -159,42 +159,19 @@ void Fill3DHoneycomb::_fill_surface_single( //makeGrid(coord_t z, coord_t gridSize, size_t gridWidth, size_t gridHeight, size_t curveType) // move pattern in place - for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) - it->translate(bb.min(0), bb.min(1)); + for (Polyline &pl : polylines) + pl.translate(bb.min); + + // clip pattern to boundaries, chain the clipped polylines + Polylines polylines_chained = chain_polylines(intersection_pl(polylines, to_polygons(expolygon))); - // clip pattern to boundaries, keeping the polyline order & ordering the fragment to be able to join them easily - Polylines polylines_chained; - for (size_t idx_polyline = 0; idx_polyline < polylines.size(); ++idx_polyline) { - Polyline &poly_to_cut = polylines[idx_polyline]; - Polylines polylines_to_sort = intersection_pl(Polylines() = { poly_to_cut }, (Polygons)expolygon); - for (Polyline &polyline : polylines_to_sort) { - //TODO: replace by closest_index_point() - if (poly_to_cut.points.front().distance_to_square(polyline.points.front()) > poly_to_cut.points.front().distance_to_square(polyline.points.back())) { - polyline.reverse(); - } - } - if (polylines_to_sort.size() > 1) { - Point nearest = poly_to_cut.points.front(); - //Bubble sort - for (size_t idx_sort = polylines_to_sort.size() - 1; idx_sort > 0; idx_sort--) { - for (size_t idx_bubble = 0; idx_bubble < idx_sort; idx_bubble++) { - if (polylines_to_sort[idx_bubble + 1].points.front().distance_to_square(nearest) < polylines_to_sort[idx_bubble].points.front().distance_to_square(nearest)) { - iter_swap(polylines_to_sort.begin() + idx_bubble, polylines_to_sort.begin() + idx_bubble + 1); - } - } - } - } - polylines_chained.insert(polylines_chained.end(), polylines_to_sort.begin(), polylines_to_sort.end()); - } // connect lines if needed - if (!polylines_chained.empty()) { - if (params.dont_connect) { - polylines_out.insert(polylines_out.end(), polylines_chained.begin(), polylines_chained.end()); - } else { - this->connect_infill(polylines_chained, expolygon, polylines_out, params); + if (! polylines_chained.empty()) { + if (params.dont_connect) + append(polylines_out, std::move(polylines_chained)); + else + this->connect_infill(std::move(polylines_chained), expolygon, polylines_out, this->spacing, params); + } } - } -} - } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index ed413c509..51f862612 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -1,9 +1,12 @@ #include #include "../ClipperUtils.hpp" +#include "../EdgeGrid.hpp" +#include "../Geometry.hpp" #include "../Surface.hpp" #include "../PrintConfig.hpp" #include "../ExtrusionEntityCollection.hpp" +#include "../libslic3r.h" #include "FillBase.hpp" #include "FillConcentric.hpp" @@ -623,4 +626,488 @@ Fill::do_gap_fill(const ExPolygons &gapfill_areas, const FillParams ¶ms, Ext } +struct ContourPointData { + ContourPointData(float param) : param(param) {} + // Eucleidean position of the contour point along the contour. + float param = 0.f; + // Was the segment starting with this contour point extruded? + bool segment_consumed = false; + // Was this point extruded over? + bool point_consumed = false; +}; + +// Verify whether the contour from point idx_start to point idx_end could be taken (whether all segments along the contour were not yet extruded). +static bool could_take(const std::vector &contour_data, size_t idx_start, size_t idx_end) +{ + assert(idx_start != idx_end); + for (size_t i = idx_start; i != idx_end; ) { + if (contour_data[i].segment_consumed || contour_data[i].point_consumed) + return false; + if (++ i == contour_data.size()) + i = 0; + } + return ! contour_data[idx_end].point_consumed; +} + +// Connect end of pl1 to the start of pl2 using the perimeter contour. +// The idx_start and idx_end are ordered so that the connecting polyline points will be taken with increasing indices. +static void take(Polyline &pl1, Polyline &&pl2, const Points &contour, std::vector &contour_data, size_t idx_start, size_t idx_end, bool reversed) +{ +#ifndef NDEBUG + size_t num_points_initial = pl1.points.size(); + assert(idx_start != idx_end); +#endif /* NDEBUG */ + + { + // Reserve memory at pl1 for the connecting contour and pl2. + int new_points = int(idx_end) - int(idx_start) - 1; + if (new_points < 0) + new_points += int(contour.size()); + pl1.points.reserve(pl1.points.size() + size_t(new_points) + pl2.points.size()); + } + + contour_data[idx_start].point_consumed = true; + contour_data[idx_start].segment_consumed = true; + contour_data[idx_end ].point_consumed = true; + + if (reversed) { + size_t i = (idx_end == 0) ? contour_data.size() - 1 : idx_end - 1; + while (i != idx_start) { + contour_data[i].point_consumed = true; + contour_data[i].segment_consumed = true; + pl1.points.emplace_back(contour[i]); + if (i == 0) + i = contour_data.size(); + -- i; + } + } else { + size_t i = idx_start; + if (++ i == contour_data.size()) + i = 0; + while (i != idx_end) { + contour_data[i].point_consumed = true; + contour_data[i].segment_consumed = true; + pl1.points.emplace_back(contour[i]); + if (++ i == contour_data.size()) + i = 0; + } + } + + append(pl1.points, std::move(pl2.points)); +} + +// Return an index of start of a segment and a point of the clipping point at distance from the end of polyline. +struct SegmentPoint { + // Segment index, defining a line ::max(); + // Parameter of point in <0, 1) along the line ::max(); } +}; + +static inline SegmentPoint clip_start_segment_and_point(const Points &polyline, double distance) +{ + assert(polyline.size() >= 2); + assert(distance > 0.); + // Initialized to "invalid". + SegmentPoint out; + if (polyline.size() >= 2) { + Vec2d pt_prev = polyline.front().cast(); + for (int i = 1; i < polyline.size(); ++ i) { + Vec2d pt = polyline[i].cast(); + Vec2d v = pt - pt_prev; + double l2 = v.squaredNorm(); + if (l2 > distance * distance) { + out.idx_segment = i; + out.t = distance / sqrt(l2); + out.point = pt_prev + out.t * v; + break; + } + distance -= sqrt(l2); + pt_prev = pt; + } + } + return out; +} + +static inline SegmentPoint clip_end_segment_and_point(const Points &polyline, double distance) +{ + assert(polyline.size() >= 2); + assert(distance > 0.); + // Initialized to "invalid". + SegmentPoint out; + if (polyline.size() >= 2) { + Vec2d pt_next = polyline.back().cast(); + for (int i = int(polyline.size()) - 2; i >= 0; -- i) { + Vec2d pt = polyline[i].cast(); + Vec2d v = pt - pt_next; + double l2 = v.squaredNorm(); + if (l2 > distance * distance) { + out.idx_segment = i; + out.t = distance / sqrt(l2); + out.point = pt_next + out.t * v; + // Store the parameter referenced to the starting point of a segment. + out.t = 1. - out.t; + break; + } + distance -= sqrt(l2); + pt_next = pt; + } + } + return out; +} + +// Optimized version with the precalculated v1 = p1b - p1a and l1_2 = v1.squaredNorm(). +// Assumption: l1_2 < EPSILON. +static inline double segment_point_distance_squared(const Vec2d &p1a, const Vec2d &p1b, const Vec2d &v1, const double l1_2, const Vec2d &p2) +{ + assert(l1_2 > EPSILON); + Vec2d v12 = p2 - p1a; + double t = v12.dot(v1); + return (t <= 0. ) ? v12.squaredNorm() : + (t >= l1_2) ? (p2 - p1a).squaredNorm() : + ((t / l1_2) * v1 - v12).squaredNorm(); +} + +static inline double segment_point_distance_squared(const Vec2d &p1a, const Vec2d &p1b, const Vec2d &p2) +{ + const Vec2d v = p1b - p1a; + const double l2 = v.squaredNorm(); + if (l2 < EPSILON) + // p1a == p1b + return (p2 - p1a).squaredNorm(); + return segment_point_distance_squared(p1a, p1b, v, v.squaredNorm(), p2); +} + +// Distance to the closest point of line. +static inline double min_distance_of_segments(const Vec2d &p1a, const Vec2d &p1b, const Vec2d &p2a, const Vec2d &p2b) +{ + Vec2d v1 = p1b - p1a; + double l1_2 = v1.squaredNorm(); + if (l1_2 < EPSILON) + // p1a == p1b: Return distance of p1a from the (p2a, p2b) segment. + return segment_point_distance_squared(p2a, p2b, p1a); + + Vec2d v2 = p2b - p2a; + double l2_2 = v2.squaredNorm(); + if (l2_2 < EPSILON) + // p2a == p2b: Return distance of p2a from the (p1a, p1b) segment. + return segment_point_distance_squared(p1a, p1b, v1, l1_2, p2a); + + return std::min( + std::min(segment_point_distance_squared(p1a, p1b, v1, l1_2, p2a), segment_point_distance_squared(p1a, p1b, v1, l1_2, p2b)), + std::min(segment_point_distance_squared(p2a, p2b, v2, l2_2, p1a), segment_point_distance_squared(p2a, p2b, v2, l2_2, p1b))); +} + +// Mark the segments of split boundary as consumed if they are very close to some of the infill line. +void mark_boundary_segments_touching_infill( + const std::vector &boundary, + std::vector> &boundary_data, + const BoundingBox &boundary_bbox, + const Polylines &infill, + const double clip_distance, + const double distance_colliding) +{ + EdgeGrid::Grid grid; + grid.set_bbox(boundary_bbox); + // Inflate the bounding box by a thick line width. + grid.create(boundary, clip_distance + scale_(10.)); + + struct Visitor { + Visitor(const EdgeGrid::Grid &grid, const std::vector &boundary, std::vector> &boundary_data, const double dist2_max) : + grid(grid), boundary(boundary), boundary_data(boundary_data), dist2_max(dist2_max) {} + + void init(const Vec2d &pt1, const Vec2d &pt2) { + this->pt1 = &pt1; + this->pt2 = &pt2; + } + + bool operator()(coord_t iy, coord_t ix) { + // Called with a row and colum of the grid cell, which is intersected by a line. + auto cell_data_range = this->grid.cell_data_range(iy, ix); + for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++ it_contour_and_segment) { + // End points of the line segment and their vector. + auto segment = this->grid.segment(*it_contour_and_segment); + const Vec2d seg_pt1 = segment.first.cast(); + const Vec2d seg_pt2 = segment.second.cast(); + if (min_distance_of_segments(seg_pt1, seg_pt2, *this->pt1, *this->pt2) < this->dist2_max) { + // Mark this boundary segment as touching the infill line. + ContourPointData &bdp = boundary_data[it_contour_and_segment->first][it_contour_and_segment->second]; + bdp.segment_consumed = true; + // There is no need for checking seg_pt2 as it will be checked the next time. + bool point_touching = false; + if (segment_point_distance_squared(*this->pt1, *this->pt2, seg_pt1) < this->dist2_max) { + point_touching = true; + bdp.point_consumed = true; + } +#if 0 + { + static size_t iRun = 0; + ExPolygon expoly(Polygon(*grid.contours().front())); + for (size_t i = 1; i < grid.contours().size(); ++i) + expoly.holes.emplace_back(Polygon(*grid.contours()[i])); + SVG svg(debug_out_path("%s-%d.svg", "FillBase-mark_boundary_segments_touching_infill", iRun ++).c_str(), get_extents(expoly)); + svg.draw(expoly, "green"); + svg.draw(Line(segment.first, segment.second), "red"); + svg.draw(Line(this->pt1->cast(), this->pt2->cast()), "magenta"); + } +#endif + } + } + // Continue traversing the grid along the edge. + return true; + } + + const EdgeGrid::Grid &grid; + const std::vector &boundary; + std::vector> &boundary_data; + // Maximum distance between the boundary and the infill line allowed to consider the boundary not touching the infill line. + const double dist2_max; + + const Vec2d *pt1; + const Vec2d *pt2; + } visitor(grid, boundary, boundary_data, distance_colliding * distance_colliding); + + BoundingBoxf bboxf(boundary_bbox.min.cast(), boundary_bbox.max.cast()); + bboxf.offset(- SCALED_EPSILON); + + for (const Polyline &polyline : infill) { + // Clip the infill polyline by the Eucledian distance along the polyline. + SegmentPoint start_point = clip_start_segment_and_point(polyline.points, clip_distance); + SegmentPoint end_point = clip_end_segment_and_point(polyline.points, clip_distance); + if (start_point.valid() && end_point.valid() && + (start_point.idx_segment < end_point.idx_segment || (start_point.idx_segment == end_point.idx_segment && start_point.t < end_point.t))) { + // The clipped polyline is non-empty. + for (size_t point_idx = start_point.idx_segment; point_idx <= end_point.idx_segment; ++ point_idx) { +//FIXME extend the EdgeGrid to suport tracing a thick line. +#if 0 + Point pt1, pt2; + Vec2d pt1d, pt2d; + if (point_idx == start_point.idx_segment) { + pt1d = start_point.point; + pt1 = pt1d.cast(); + } else { + pt1 = polyline.points[point_idx]; + pt1d = pt1.cast(); + } + if (point_idx == start_point.idx_segment) { + pt2d = end_point.point; + pt2 = pt1d.cast(); + } else { + pt2 = polyline.points[point_idx]; + pt2d = pt2.cast(); + } + visitor.init(pt1d, pt2d); + grid.visit_cells_intersecting_thick_line(pt1, pt2, distance_colliding, visitor); +#else + Vec2d pt1 = (point_idx == start_point.idx_segment) ? start_point.point : polyline.points[point_idx ].cast(); + Vec2d pt2 = (point_idx == end_point .idx_segment) ? end_point .point : polyline.points[point_idx + 1].cast(); +#if 0 + { + static size_t iRun = 0; + ExPolygon expoly(Polygon(*grid.contours().front())); + for (size_t i = 1; i < grid.contours().size(); ++i) + expoly.holes.emplace_back(Polygon(*grid.contours()[i])); + SVG svg(debug_out_path("%s-%d.svg", "FillBase-mark_boundary_segments_touching_infill0", iRun ++).c_str(), get_extents(expoly)); + svg.draw(expoly, "green"); + svg.draw(polyline, "blue"); + svg.draw(Line(pt1.cast(), pt2.cast()), "magenta", scale_(0.1)); + } +#endif + visitor.init(pt1, pt2); + // Simulate tracing of a thick line. This only works reliably if distance_colliding <= grid cell size. + Vec2d v = (pt2 - pt1).normalized() * distance_colliding; + Vec2d vperp(-v.y(), v.x()); + Vec2d a = pt1 - v - vperp; + Vec2d b = pt1 + v - vperp; + if (Geometry::liang_barsky_line_clipping(a, b, bboxf)) + grid.visit_cells_intersecting_line(a.cast(), b.cast(), visitor); + a = pt1 - v + vperp; + b = pt1 + v + vperp; + if (Geometry::liang_barsky_line_clipping(a, b, bboxf)) + grid.visit_cells_intersecting_line(a.cast(), b.cast(), visitor); +#endif + } + } + } +} + +void Fill::connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary_src, Polylines &polylines_out, const double spacing, const FillParams ¶ms) +{ + assert(! infill_ordered.empty()); + assert(! boundary_src.contour.points.empty()); + + BoundingBox bbox = get_extents(boundary_src.contour); + bbox.offset(SCALED_EPSILON); + + // 1) Add the end points of infill_ordered to boundary_src. + std::vector boundary; + std::vector> boundary_data; + boundary.assign(boundary_src.holes.size() + 1, Points()); + boundary_data.assign(boundary_src.holes.size() + 1, std::vector()); + // Mapping the infill_ordered end point to a (contour, point) of boundary. + std::vector> map_infill_end_point_to_boundary; + map_infill_end_point_to_boundary.assign(infill_ordered.size() * 2, std::pair(std::numeric_limits::max(), std::numeric_limits::max())); + { + // Project the infill_ordered end points onto boundary_src. + std::vector> intersection_points; + { + EdgeGrid::Grid grid; + grid.set_bbox(bbox); + grid.create(boundary_src, scale_(10.)); + intersection_points.reserve(infill_ordered.size() * 2); + for (const Polyline &pl : infill_ordered) + for (const Point *pt : { &pl.points.front(), &pl.points.back() }) { + EdgeGrid::Grid::ClosestPointResult cp = grid.closest_point(*pt, SCALED_EPSILON); + if (cp.valid()) { + // The infill end point shall lie on the contour. + assert(cp.distance < 2.); + intersection_points.emplace_back(cp, (&pl - infill_ordered.data()) * 2 + (pt == &pl.points.front() ? 0 : 1)); + } + } + std::sort(intersection_points.begin(), intersection_points.end(), [](const std::pair &cp1, const std::pair &cp2) { + return cp1.first.contour_idx < cp2.first.contour_idx || + (cp1.first.contour_idx == cp2.first.contour_idx && + (cp1.first.start_point_idx < cp2.first.start_point_idx || + (cp1.first.start_point_idx == cp2.first.start_point_idx && cp1.first.t < cp2.first.t))); + }); + } + auto it = intersection_points.begin(); + auto it_end = intersection_points.end(); + for (size_t idx_contour = 0; idx_contour <= boundary_src.holes.size(); ++ idx_contour) { + const Polygon &contour_src = (idx_contour == 0) ? boundary_src.contour : boundary_src.holes[idx_contour - 1]; + Points &contour_dst = boundary[idx_contour]; + for (size_t idx_point = 0; idx_point < contour_src.points.size(); ++ idx_point) { + contour_dst.emplace_back(contour_src.points[idx_point]); + for (; it != it_end && it->first.contour_idx == idx_contour && it->first.start_point_idx == idx_point; ++ it) { + // Add these points to the destination contour. + const Vec2d pt1 = contour_src[idx_point].cast(); + const Vec2d pt2 = (idx_point + 1 == contour_src.size() ? contour_src.points.front() : contour_src.points[idx_point + 1]).cast(); + const Vec2d pt = lerp(pt1, pt2, it->first.t); + map_infill_end_point_to_boundary[it->second] = std::make_pair(idx_contour, contour_dst.size()); + contour_dst.emplace_back(pt.cast()); + } + } + // Parametrize the curve. + std::vector &contour_data = boundary_data[idx_contour]; + contour_data.reserve(contour_dst.size()); + contour_data.emplace_back(ContourPointData(0.f)); + for (size_t i = 1; i < contour_dst.size(); ++ i) + contour_data.emplace_back(contour_data.back().param + (contour_dst[i].cast() - contour_dst[i - 1].cast()).norm()); + contour_data.front().param = contour_data.back().param + (contour_dst.back().cast() - contour_dst.front().cast()).norm(); + } + +#ifndef NDEBUG + assert(boundary.size() == boundary_src.num_contours()); + assert(std::all_of(map_infill_end_point_to_boundary.begin(), map_infill_end_point_to_boundary.end(), + [&boundary](const std::pair &contour_point) { + return contour_point.first < boundary.size() && contour_point.second < boundary[contour_point.first].size(); + })); +#endif /* NDEBUG */ + } + + // Mark the points and segments of split boundary as consumed if they are very close to some of the infill line. + { + // @supermerill used 2. * scale_(spacing) + const double clip_distance = 3. * scale_(spacing); + const double distance_colliding = 1.1 * scale_(spacing); + mark_boundary_segments_touching_infill(boundary, boundary_data, bbox, infill_ordered, clip_distance, distance_colliding); + } + + // Connection from end of one infill line to the start of another infill line. + //const float length_max = scale_(spacing); +// const float length_max = scale_((2. / params.density) * spacing); + const float length_max = scale_((1000. / params.density) * spacing); + std::vector merged_with(infill_ordered.size()); + for (size_t i = 0; i < merged_with.size(); ++ i) + merged_with[i] = i; + struct ConnectionCost { + ConnectionCost(size_t idx_first, double cost, bool reversed) : idx_first(idx_first), cost(cost), reversed(reversed) {} + size_t idx_first; + double cost; + bool reversed; + }; + std::vector connections_sorted; + connections_sorted.reserve(infill_ordered.size() * 2 - 2); + for (size_t idx_chain = 1; idx_chain < infill_ordered.size(); ++ idx_chain) { + const Polyline &pl1 = infill_ordered[idx_chain - 1]; + const Polyline &pl2 = infill_ordered[idx_chain]; + const std::pair *cp1 = &map_infill_end_point_to_boundary[(idx_chain - 1) * 2 + 1]; + const std::pair *cp2 = &map_infill_end_point_to_boundary[idx_chain * 2]; + const std::vector &contour_data = boundary_data[cp1->first]; + if (cp1->first == cp2->first) { + // End points on the same contour. Try to connect them. + float param_lo = (cp1->second == 0) ? 0.f : contour_data[cp1->second].param; + float param_hi = (cp2->second == 0) ? 0.f : contour_data[cp2->second].param; + float param_end = contour_data.front().param; + bool reversed = false; + if (param_lo > param_hi) { + std::swap(param_lo, param_hi); + reversed = true; + } + assert(param_lo >= 0.f && param_lo <= param_end); + assert(param_hi >= 0.f && param_hi <= param_end); + double len = param_hi - param_lo; + if (len < length_max) + connections_sorted.emplace_back(idx_chain - 1, len, reversed); + len = param_lo + param_end - param_hi; + if (len < length_max) + connections_sorted.emplace_back(idx_chain - 1, len, ! reversed); + } + } + std::sort(connections_sorted.begin(), connections_sorted.end(), [](const ConnectionCost& l, const ConnectionCost& r) { return l.cost < r.cost; }); + + size_t idx_chain_last = 0; + for (ConnectionCost &connection_cost : connections_sorted) { + const std::pair *cp1 = &map_infill_end_point_to_boundary[connection_cost.idx_first * 2 + 1]; + const std::pair *cp1prev = cp1 - 1; + const std::pair *cp2 = &map_infill_end_point_to_boundary[(connection_cost.idx_first + 1) * 2]; + const std::pair *cp2next = cp2 + 1; + assert(cp1->first == cp2->first); + std::vector &contour_data = boundary_data[cp1->first]; + if (connection_cost.reversed) + std::swap(cp1, cp2); + // Mark the the other end points of the segments to be taken as consumed temporarily, so they will not be crossed + // by the new connection line. + bool prev_marked = false; + bool next_marked = false; + if (cp1prev->first == cp1->first && ! contour_data[cp1prev->second].point_consumed) { + contour_data[cp1prev->second].point_consumed = true; + prev_marked = true; + } + if (cp2next->first == cp1->first && ! contour_data[cp2next->second].point_consumed) { + contour_data[cp2next->second].point_consumed = true; + next_marked = true; + } + if (could_take(contour_data, cp1->second, cp2->second)) { + // Indices of the polygons to be connected. + size_t idx_first = connection_cost.idx_first; + size_t idx_second = idx_first + 1; + for (size_t last = idx_first;;) { + size_t lower = merged_with[last]; + if (lower == last) { + merged_with[idx_first] = lower; + idx_first = lower; + break; + } + last = lower; + } + // Connect the two polygons using the boundary contour. + take(infill_ordered[idx_first], std::move(infill_ordered[idx_second]), boundary[cp1->first], contour_data, cp1->second, cp2->second, connection_cost.reversed); + // Mark the second polygon as merged with the first one. + merged_with[idx_second] = merged_with[idx_first]; + } + if (prev_marked) + contour_data[cp1prev->second].point_consumed = false; + if (next_marked) + contour_data[cp2next->second].point_consumed = false; + } + polylines_out.reserve(polylines_out.size() + std::count_if(infill_ordered.begin(), infill_ordered.end(), [](const Polyline &pl) { return ! pl.empty(); })); + for (Polyline &pl : infill_ordered) + if (! pl.empty()) + polylines_out.emplace_back(std::move(pl)); +} + } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index a13aa1b8f..e051c6cec 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -18,6 +18,7 @@ namespace Slic3r { +class ExPolygon; class Surface; struct FillParams @@ -154,6 +155,8 @@ protected: } public: + static void connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, double spacing, const FillParams ¶ms); + static coord_t _adjust_solid_spacing(const coord_t width, const coord_t distance); // Align a coordinate to a grid. The coordinate may be negative, diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index 341815e9b..2b12632da 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -1,5 +1,5 @@ #include "../ClipperUtils.hpp" -#include "../PolylineCollection.hpp" +#include "../ShortestPath.hpp" #include "../Surface.hpp" #include #include @@ -145,6 +145,9 @@ static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double return result; } +// FIXME: needed to fix build on Mac on buildserver +constexpr double FillGyroid::PatternTolerance; + void FillGyroid::_fill_surface_single( const FillParams ¶ms, unsigned int thickness_layers, @@ -173,68 +176,32 @@ void FillGyroid::_fill_surface_single( ceil(bb.size()(0) / distance) + 1., ceil(bb.size()(1) / distance) + 1.); - // clip pattern to boundaries, keeping the polyline order & ordering the fragment to be able to join them easily - Polylines polylines_chained; - for (size_t idx_polyline = 0; idx_polyline < polylines_square.size(); ++idx_polyline) { - // shift the polyline to the grid origin - Polyline &poly_to_cut = polylines_square[idx_polyline]; - poly_to_cut.translate(bb.min); + // shift the polyline to the grid origin + for (Polyline &pl : polylines) + pl.translate(bb.min); - // intersect - Polylines polylines_to_sort = intersection_pl(Polylines() = { poly_to_cut }, (Polygons)expolygon); - for (Polyline &polyline : polylines_to_sort) { - //TODO: replace by closest_index_point() - if (idx_polyline % 2 == 0) { - if (poly_to_cut.points.front().distance_to_square(polyline.points.front()) > poly_to_cut.points.front().distance_to_square(polyline.points.back())) { - polyline.reverse(); - } - } else { - if (poly_to_cut.points.back().distance_to_square(polyline.points.front()) > poly_to_cut.points.back().distance_to_square(polyline.points.back())) { - polyline.reverse(); - } - } - } - if (polylines_to_sort.size() > 1) { - Point nearest = poly_to_cut.points.front(); - if (idx_polyline % 2 != 0) { - nearest = poly_to_cut.points.back(); - } - //Bubble sort - for (size_t idx_sort = polylines_to_sort.size() - 1; idx_sort > 0; idx_sort--) { - for (size_t idx_bubble = 0; idx_bubble < idx_sort; idx_bubble++) { - if (polylines_to_sort[idx_bubble + 1].points.front().distance_to_square(nearest) < polylines_to_sort[idx_bubble].points.front().distance_to_square(nearest)) { - iter_swap(polylines_to_sort.begin() + idx_bubble, polylines_to_sort.begin() + idx_bubble + 1); - } - } - } - } - polylines_chained.insert(polylines_chained.end(), polylines_to_sort.begin(), polylines_to_sort.end()); - } + polylines = intersection_pl(polylines, to_polygons(expolygon)); - size_t polylines_out_first_idx = polylines_out.size(); - if (!polylines_chained.empty()) { - // connect lines - if (params.dont_connect) { - polylines_out.insert(polylines_out.end(), polylines_chained.begin(), polylines_chained.end()); - } else { - this->connect_infill(polylines_chained, expolygon, polylines_out, params); - } - } + if (! polylines.empty()) + // remove too small bits (larger than longer) + polylines.erase( + //FIXME what is the small size? Removing tiny extrusions disconnects walls! + std::remove_if(polylines.begin(), polylines.end(), [this](const Polyline &pl) { return pl.length() < scale_(this->spacing * 3); }), + polylines.end()); - //remove too small bits (larger than longer); - for (size_t idx = polylines_out_first_idx; idx < polylines_out.size(); idx++) { - if (polylines_out[idx].length() < scale_(this->spacing * 3)) { - polylines_out.erase(polylines_out.begin() + idx); - idx--; - } - } - - // new paths must be rotated back - if(abs(infill_angle) >= EPSILON) { - for (Polylines::iterator it = polylines_out.begin() + polylines_out_first_idx; - it != polylines_out.end(); ++it) { - it->rotate(infill_angle); - } + if (! polylines.empty()) { + polylines = chain_polylines(polylines); + // connect lines + size_t polylines_out_first_idx = polylines_out.size(); + if (params.dont_connect) + append(polylines_out, std::move(polylines)); + else + this->connect_infill(std::move(polylines), expolygon, polylines_out, this->spacing, params); + // new paths must be rotated back + if (abs(infill_angle) >= EPSILON) { + for (auto it = polylines_out.begin() + polylines_out_first_idx; it != polylines_out.end(); ++ it) + it->rotate(infill_angle); + } } } diff --git a/src/libslic3r/Fill/FillGyroid.hpp b/src/libslic3r/Fill/FillGyroid.hpp index 72af52c56..874ebf8ae 100644 --- a/src/libslic3r/Fill/FillGyroid.hpp +++ b/src/libslic3r/Fill/FillGyroid.hpp @@ -24,6 +24,17 @@ public: static constexpr double PatternTolerance = 0.2; + // Correction applied to regular infill angle to maximize printing + // speed in default configuration (degrees) + static constexpr float CorrectionAngle = -45.; + + // Density adjustment to have a good %of weight. + static constexpr double DensityAdjust = 2.44; + + // Gyroid upper resolution tolerance (mm^-2) + static constexpr double PatternTolerance = 0.2; + + protected: virtual void _fill_surface_single( const FillParams ¶ms, diff --git a/src/libslic3r/Fill/FillHoneycomb.cpp b/src/libslic3r/Fill/FillHoneycomb.cpp index cbfe926f2..948af182b 100644 --- a/src/libslic3r/Fill/FillHoneycomb.cpp +++ b/src/libslic3r/Fill/FillHoneycomb.cpp @@ -1,5 +1,5 @@ #include "../ClipperUtils.hpp" -#include "../PolylineCollection.hpp" +#include "../ShortestPath.hpp" #include "../Surface.hpp" #include "FillHoneycomb.hpp" @@ -93,22 +93,20 @@ void FillHoneycomb::_fill_surface_single( // connect paths if (! paths.empty()) { // prevent calling leftmost_point() on empty collections - Polylines chained = PolylineCollection::chained_path_from( - std::move(paths), - PolylineCollection::leftmost_point(paths), false); + Polylines chained = chain_polylines(std::move(paths)); assert(paths.empty()); paths.clear(); - for (Polylines::iterator it_path = chained.begin(); it_path != chained.end(); ++ it_path) { + for (Polyline &path : chained) { if (! paths.empty()) { // distance between first point of this path and last point of last path - double distance = (it_path->first_point() - paths.back().last_point()).cast().norm(); + double distance = (path.first_point() - paths.back().last_point()).cast().norm(); if (distance <= m.hex_width) { - paths.back().points.insert(paths.back().points.end(), it_path->points.begin(), it_path->points.end()); + paths.back().points.insert(paths.back().points.end(), path.points.begin(), path.points.end()); continue; } } // Don't connect the paths. - paths.push_back(*it_path); + paths.push_back(std::move(path)); } } diff --git a/src/libslic3r/Fill/FillPlanePath.cpp b/src/libslic3r/Fill/FillPlanePath.cpp index 51e790613..76b6b4eaf 100644 --- a/src/libslic3r/Fill/FillPlanePath.cpp +++ b/src/libslic3r/Fill/FillPlanePath.cpp @@ -1,5 +1,4 @@ #include "../ClipperUtils.hpp" -#include "../PolylineCollection.hpp" #include "../Surface.hpp" #include "FillPlanePath.hpp" diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp index cc2d10624..5bb7d0c0b 100644 --- a/src/libslic3r/Fill/FillRectilinear.cpp +++ b/src/libslic3r/Fill/FillRectilinear.cpp @@ -1,6 +1,6 @@ #include "../ClipperUtils.hpp" #include "../ExPolygon.hpp" -#include "../PolylineCollection.hpp" +#include "../ShortestPath.hpp" #include "../Surface.hpp" #include "FillRectilinear.hpp" @@ -92,15 +92,12 @@ void FillRectilinear::_fill_surface_single( std::swap(expolygon_off, expolygons_off.front()); } } - Polylines chained = PolylineCollection::chained_path_from( - std::move(polylines), - PolylineCollection::leftmost_point(polylines), false); // reverse allowed bool first = true; - for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) { + for (Polyline &polyline : chain_polylines(std::move(polylines))) { if (!params.dont_connect && !first) { // Try to connect the lines. Points &pts_end = polylines_out.back().points; - const Point &first_point = it_polyline->points.front(); + const Point &first_point = polyline.points.front(); const Point &last_point = pts_end.back(); // Distance in X, Y. const Vector distance = last_point - first_point; @@ -109,12 +106,12 @@ void FillRectilinear::_fill_surface_single( if (this->_can_connect(std::abs(distance(0)), std::abs(distance(1))) && expolygon_off.contains(Line(last_point, first_point))) { // Append the polyline. - pts_end.insert(pts_end.end(), it_polyline->points.begin(), it_polyline->points.end()); + pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end()); continue; } } // The lines cannot be connected. - polylines_out.emplace_back(std::move(*it_polyline)); + polylines_out.emplace_back(std::move(polyline)); first = false; } } diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 0f89e6193..62e964a7a 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -3,6 +3,9 @@ #include "../Utils.hpp" #include "../GCode.hpp" #include "../Geometry.hpp" +#if ENABLE_THUMBNAIL_GENERATOR +#include "../GCode/ThumbnailData.hpp" +#endif // ENABLE_THUMBNAIL_GENERATOR #include "../I18N.hpp" @@ -31,7 +34,8 @@ namespace pt = boost::property_tree; // VERSION NUMBERS // 0 : .3mf, files saved by older slic3r or other applications. No version definition in them. // 1 : Introduction of 3mf versioning. No other change in data saved into 3mf files. -const unsigned int VERSION_3MF = 1; +// 2 : Meshes saved in their local system; Volumes' matrices and source data added to Metadata/Slic3r_PE_model.config file. +const unsigned int VERSION_3MF = 2; const char* SLIC3RPE_3MF_VERSION = "slic3rpe:Version3mf"; // definition of the metadata name saved into .model file const std::string MODEL_FOLDER = "3D/"; @@ -39,11 +43,15 @@ const std::string MODEL_EXTENSION = ".model"; const std::string MODEL_FILE = "3D/3dmodel.model"; // << this is the only format of the string which works with CURA const std::string CONTENT_TYPES_FILE = "[Content_Types].xml"; const std::string RELATIONSHIPS_FILE = "_rels/.rels"; +#if ENABLE_THUMBNAIL_GENERATOR +const std::string THUMBNAIL_FILE = "Metadata/thumbnail.png"; +#endif // ENABLE_THUMBNAIL_GENERATOR const std::string PRINT_CONFIG_FILE = "Metadata/Slic3r_PE.config"; const std::string MODEL_CONFIG_FILE = "Metadata/Slic3r_PE_model.config"; const std::string LAYER_HEIGHTS_PROFILE_FILE = "Metadata/Slic3r_PE_layer_heights_profile.txt"; const std::string LAYER_CONFIG_RANGES_FILE = "Metadata/Prusa_Slicer_layer_config_ranges.xml"; const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt"; +const std::string CUSTOM_GCODE_PER_HEIGHT_FILE = "Metadata/Prusa_Slicer_custom_gcode_per_height.xml"; const char* MODEL_TAG = "model"; const char* RESOURCES_TAG = "resources"; @@ -87,6 +95,13 @@ const char* VOLUME_TYPE = "volume"; const char* NAME_KEY = "name"; const char* MODIFIER_KEY = "modifier"; const char* VOLUME_TYPE_KEY = "volume_type"; +const char* MATRIX_KEY = "matrix"; +const char* SOURCE_FILE_KEY = "source_file"; +const char* SOURCE_OBJECT_ID_KEY = "source_object_id"; +const char* SOURCE_VOLUME_ID_KEY = "source_volume_id"; +const char* SOURCE_OFFSET_X_KEY = "source_offset_x"; +const char* SOURCE_OFFSET_Y_KEY = "source_offset_y"; +const char* SOURCE_OFFSET_Z_KEY = "source_offset_z"; const unsigned int VALID_OBJECT_TYPES_COUNT = 1; const char* VALID_OBJECT_TYPES[] = @@ -148,11 +163,15 @@ bool get_attribute_value_bool(const char** attributes, unsigned int attributes_s return (text != nullptr) ? (bool)::atoi(text) : true; } -Slic3r::Transform3d get_transform_from_string(const std::string& mat_str) +Slic3r::Transform3d get_transform_from_3mf_specs_string(const std::string& mat_str) { + // check: https://3mf.io/3d-manufacturing-format/ or https://github.com/3MFConsortium/spec_core/blob/master/3MF%20Core%20Specification.md + // to see how matrices are stored inside 3mf according to specifications + Slic3r::Transform3d ret = Slic3r::Transform3d::Identity(); + if (mat_str.empty()) // empty string means default identity matrix - return Slic3r::Transform3d::Identity(); + return ret; std::vector mat_elements_str; boost::split(mat_elements_str, mat_str, boost::is_any_of(" "), boost::token_compress_on); @@ -160,9 +179,8 @@ Slic3r::Transform3d get_transform_from_string(const std::string& mat_str) unsigned int size = (unsigned int)mat_elements_str.size(); if (size != 12) // invalid data, return identity matrix - return Slic3r::Transform3d::Identity(); + return ret; - Slic3r::Transform3d ret = Slic3r::Transform3d::Identity(); unsigned int i = 0; // matrices are stored into 3mf files as 4x3 // we need to transpose them @@ -400,6 +418,8 @@ namespace Slic3r { 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_custom_gcode_per_height_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); + void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, const std::string& archive_filename); bool _extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model); @@ -609,6 +629,11 @@ namespace Slic3r { // extract slic3r print config file _extract_print_config_from_archive(archive, stat, config, filename); } + if (boost::algorithm::iequals(name, CUSTOM_GCODE_PER_HEIGHT_FILE)) + { + // extract slic3r layer config ranges file + _extract_custom_gcode_per_height_from_archive(archive, stat); + } else if (boost::algorithm::iequals(name, MODEL_CONFIG_FILE)) { // extract slic3r model config file @@ -1039,6 +1064,43 @@ namespace Slic3r { return true; } + void _3MF_Importer::_extract_custom_gcode_per_height_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 custom Gcodes per height data to buffer"); + return; + } + + std::istringstream iss(buffer); // wrap returned xml to istringstream + pt::ptree main_tree; + pt::read_xml(iss, main_tree); + + if (main_tree.front().first != "custom_gcodes_per_height") + return; + pt::ptree code_tree = main_tree.front().second; + + if (!m_model->custom_gcode_per_height.empty()) + m_model->custom_gcode_per_height.clear(); + + for (const auto& code : code_tree) + { + if (code.first != "code") + continue; + pt::ptree tree = code.second; + double height = tree.get(".height"); + std::string gcode = tree.get(".gcode"); + int extruder = tree.get(".extruder"); + std::string color = tree.get(".color"); + + m_model->custom_gcode_per_height.push_back(Model::CustomGCode(height, gcode, extruder, color)) ; + } + } + } + void _3MF_Importer::_handle_start_model_xml_element(const char* name, const char** attributes) { if (m_xml_parser == nullptr) @@ -1375,7 +1437,7 @@ namespace Slic3r { bool _3MF_Importer::_handle_start_component(const char** attributes, unsigned int num_attributes) { int object_id = get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR); - Transform3d transform = get_transform_from_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR)); + Transform3d transform = get_transform_from_3mf_specs_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR)); IdToModelObjectMap::iterator object_item = m_objects.find(object_id); if (object_item == m_objects.end()) @@ -1421,7 +1483,7 @@ namespace Slic3r { // see specifications int object_id = get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR); - Transform3d transform = get_transform_from_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR)); + Transform3d transform = get_transform_from_3mf_specs_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR)); int printable = get_attribute_value_bool(attributes, num_attributes, PRINTABLE_ATTR); return _create_object_instance(object_id, transform, printable, 1); @@ -1634,6 +1696,21 @@ namespace Slic3r { return false; } + Slic3r::Geometry::Transformation transform; + if (m_version > 1) + { + // extract the volume transformation from the volume's metadata, if present + for (const Metadata& metadata : volume_data.metadata) + { + if (metadata.key == MATRIX_KEY) + { + transform.set_from_string(metadata.value); + break; + } + } + } + Transform3d inv_matrix = transform.get_matrix().inverse(); + // splits volume out of imported geometry TriangleMesh triangle_mesh; stl_file &stl = triangle_mesh.stl; @@ -1651,7 +1728,12 @@ namespace Slic3r { stl_facet& facet = stl.facet_start[i]; for (unsigned int v = 0; v < 3; ++v) { - ::memcpy(facet.vertex[v].data(), (const void*)&geometry.vertices[geometry.triangles[src_start_id + ii + v] * 3], 3 * sizeof(float)); + unsigned int tri_id = geometry.triangles[src_start_id + ii + v] * 3; + Vec3f vertex(geometry.vertices[tri_id + 0], geometry.vertices[tri_id + 1], geometry.vertices[tri_id + 2]); + if (m_version > 1) + // revert the vertices to the original mesh reference system + vertex = (inv_matrix * vertex.cast()).cast(); + ::memcpy(facet.vertex[v].data(), (const void*)vertex.data(), 3 * sizeof(float)); } } @@ -1659,10 +1741,12 @@ namespace Slic3r { triangle_mesh.repair(); ModelVolume* volume = object.add_volume(std::move(triangle_mesh)); - volume->center_geometry_after_creation(); + // apply the volume matrix taken from the metadata, if present + if (m_version > 1) + volume->set_transformation(transform); volume->calculate_convex_hull(); - // apply volume's name and config data + // apply the remaining volume's metadata for (const Metadata& metadata : volume_data.metadata) { if (metadata.key == NAME_KEY) @@ -1671,6 +1755,18 @@ namespace Slic3r { volume->set_type(ModelVolumeType::PARAMETER_MODIFIER); else if (metadata.key == VOLUME_TYPE_KEY) volume->set_type(ModelVolume::type_from_string(metadata.value)); + else if (metadata.key == SOURCE_FILE_KEY) + volume->source.input_file = metadata.value; + else if (metadata.key == SOURCE_OBJECT_ID_KEY) + volume->source.object_idx = ::atoi(metadata.value.c_str()); + else if (metadata.key == SOURCE_VOLUME_ID_KEY) + volume->source.volume_idx = ::atoi(metadata.value.c_str()); + else if (metadata.key == SOURCE_OFFSET_X_KEY) + volume->source.mesh_offset(0) = ::atof(metadata.value.c_str()); + else if (metadata.key == SOURCE_OFFSET_Y_KEY) + volume->source.mesh_offset(1) = ::atof(metadata.value.c_str()); + else if (metadata.key == SOURCE_OFFSET_Z_KEY) + volume->source.mesh_offset(2) = ::atof(metadata.value.c_str()); else volume->config.set_deserialize(metadata.key, metadata.value); } @@ -1761,11 +1857,22 @@ namespace Slic3r { typedef std::map IdToObjectDataMap; public: +#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 private: +#if ENABLE_THUMBNAIL_GENERATOR + bool _save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data); +#else bool _save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config); +#endif // ENABLE_THUMBNAIL_GENERATOR bool _add_content_types_file_to_archive(mz_zip_archive& archive); +#if ENABLE_THUMBNAIL_GENERATOR + bool _add_thumbnail_file_to_archive(mz_zip_archive& archive, const ThumbnailData& thumbnail_data); +#endif // ENABLE_THUMBNAIL_GENERATOR bool _add_relationships_file_to_archive(mz_zip_archive& archive); bool _add_model_file_to_archive(mz_zip_archive& archive, const Model& model, IdToObjectDataMap &objects_data); bool _add_object_to_model_stream(std::stringstream& stream, unsigned int& object_id, ModelObject& object, BuildItemsList& build_items, VolumeToOffsetsMap& volumes_offsets); @@ -1776,15 +1883,28 @@ namespace Slic3r { bool _add_sla_support_points_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_height_file_to_archive(mz_zip_archive& archive, Model& model); }; +#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 +#if ENABLE_THUMBNAIL_GENERATOR + bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data) +#else bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config) +#endif // ENABLE_THUMBNAIL_GENERATOR { mz_zip_archive archive; mz_zip_zero_struct(&archive); @@ -1803,6 +1923,19 @@ namespace Slic3r { return false; } +#if ENABLE_THUMBNAIL_GENERATOR + if ((thumbnail_data != nullptr) && thumbnail_data->is_valid()) + { + // Adds the file Metadata/thumbnail.png. + if (!_add_thumbnail_file_to_archive(archive, *thumbnail_data)) + { + close_zip_writer(&archive); + boost::filesystem::remove(filename); + return false; + } + } +#endif // ENABLE_THUMBNAIL_GENERATOR + // Adds relationships file ("_rels/.rels"). // The content of this file is the same for each PrusaSlicer 3mf. // The relationshis file contains a reference to the geometry file "3D/3dmodel.model", the name was chosen to be compatible with CURA. @@ -1853,6 +1986,15 @@ namespace Slic3r { return false; } + // Adds custom gcode per height file ("Metadata/Prusa_Slicer_custom_gcode_per_height.xml"). + // All custom gcode per height of whole Model are stored here + if (!_add_custom_gcode_per_height_file_to_archive(archive, model)) + { + close_zip_writer(&archive); + boost::filesystem::remove(filename); + return false; + } + // Adds slic3r print config file ("Metadata/Slic3r_PE.config"). // This file contains the content of FullPrintConfing / SLAFullPrintConfig. if (config != nullptr) @@ -1896,6 +2038,9 @@ namespace Slic3r { stream << "\n"; stream << " \n"; stream << " \n"; +#if ENABLE_THUMBNAIL_GENERATOR + stream << " \n"; +#endif // ENABLE_THUMBNAIL_GENERATOR stream << ""; std::string out = stream.str(); @@ -1909,12 +2054,35 @@ namespace Slic3r { return true; } +#if ENABLE_THUMBNAIL_GENERATOR + bool _3MF_Exporter::_add_thumbnail_file_to_archive(mz_zip_archive& archive, const ThumbnailData& thumbnail_data) + { + bool res = false; + + size_t png_size = 0; + void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)thumbnail_data.pixels.data(), thumbnail_data.width, thumbnail_data.height, 4, &png_size, MZ_DEFAULT_LEVEL, 1); + if (png_data != nullptr) + { + res = mz_zip_writer_add_mem(&archive, THUMBNAIL_FILE.c_str(), (const void*)png_data, png_size, MZ_DEFAULT_COMPRESSION); + mz_free(png_data); + } + + if (!res) + add_error("Unable to add thumbnail file to archive"); + + return res; + } +#endif // ENABLE_THUMBNAIL_GENERATOR + bool _3MF_Exporter::_add_relationships_file_to_archive(mz_zip_archive& archive) { std::stringstream stream; stream << "\n"; stream << "\n"; stream << " \n"; +#if ENABLE_THUMBNAIL_GENERATOR + stream << " \n"; +#endif // ENABLE_THUMBNAIL_GENERATOR stream << ""; std::string out = stream.str(); @@ -2116,7 +2284,7 @@ namespace Slic3r { for (const BuildItem& item : build_items) { - stream << " <" << ITEM_TAG << " objectid=\"" << item.id << "\" transform =\""; + stream << " <" << ITEM_TAG << " " << OBJECTID_ATTR << "=\"" << item.id << "\" " << TRANSFORM_ATTR << "=\""; for (unsigned c = 0; c < 4; ++c) { for (unsigned r = 0; r < 3; ++r) @@ -2126,7 +2294,7 @@ namespace Slic3r { stream << " "; } } - stream << "\" printable =\"" << item.printable << "\" />\n"; + stream << "\" " << PRINTABLE_ATTR << "=\"" << item.printable << "\" />\n"; } stream << " \n"; @@ -2211,7 +2379,7 @@ namespace Slic3r { if (!tree.empty()) { std::ostringstream oss; - boost::property_tree::write_xml(oss, tree); + pt::write_xml(oss, tree); out = oss.str(); // Post processing("beautification") of the output string for a better preview @@ -2344,6 +2512,31 @@ namespace Slic3r { stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << VOLUME_TYPE_KEY << "\" " << VALUE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\"/>\n"; + // stores volume's local matrix + stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MATRIX_KEY << "\" " << VALUE_ATTR << "=\""; + const Transform3d& matrix = volume->get_matrix(); + for (int r = 0; r < 4; ++r) + { + for (int c = 0; c < 4; ++c) + { + stream << matrix(r, c); + if ((r != 3) || (c != 3)) + stream << " "; + } + } + stream << "\"/>\n"; + + // stores volume's source data + if (!volume->source.input_file.empty()) + { + stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->source.input_file) << "\"/>\n"; + stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OBJECT_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.object_idx << "\"/>\n"; + stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_VOLUME_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.volume_idx << "\"/>\n"; + stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_X_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(0) << "\"/>\n"; + stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_Y_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(1) << "\"/>\n"; + stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_Z_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(2) << "\"/>\n"; + } + // stores volume's config data for (const std::string& key : volume->config.keys()) { @@ -2372,7 +2565,49 @@ namespace Slic3r { return true; } - bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model, bool check_version) +bool _3MF_Exporter::_add_custom_gcode_per_height_file_to_archive( mz_zip_archive& archive, Model& model) +{ + std::string out = ""; + + if (!model.custom_gcode_per_height.empty()) + { + pt::ptree tree; + pt::ptree& main_tree = tree.add("custom_gcodes_per_height", ""); + + for (const Model::CustomGCode& code : model.custom_gcode_per_height) + { + pt::ptree& code_tree = main_tree.add("code", ""); + // store minX and maxZ + code_tree.put(".height" , code.height ); + code_tree.put(".gcode" , code.gcode ); + code_tree.put(".extruder" , code.extruder ); + code_tree.put(".color" , code.color ); + } + + if (!tree.empty()) + { + std::ostringstream oss; + boost::property_tree::write_xml(oss, tree); + out = oss.str(); + + // Post processing("beautification") of the output string + boost::replace_all(out, "><", ">\n<"); + } + } + + if (!out.empty()) + { + if (!mz_zip_writer_add_mem(&archive, CUSTOM_GCODE_PER_HEIGHT_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) + { + add_error("Unable to add custom Gcodes per height file to archive"); + return false; + } + } + + return true; +} + +bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model, bool check_version) { if ((path == nullptr) || (config == nullptr) || (model == nullptr)) return false; @@ -2383,13 +2618,21 @@ namespace Slic3r { return res; } +#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 { if ((path == nullptr) || (model == nullptr)) return false; _3MF_Exporter exporter; +#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 if (!res) exporter.log_errors(); diff --git a/src/libslic3r/Format/3mf.hpp b/src/libslic3r/Format/3mf.hpp index f387192ab..2e85b7f59 100644 --- a/src/libslic3r/Format/3mf.hpp +++ b/src/libslic3r/Format/3mf.hpp @@ -22,13 +22,20 @@ namespace Slic3r { class Model; class DynamicPrintConfig; +#if ENABLE_THUMBNAIL_GENERATOR + struct ThumbnailData; +#endif // ENABLE_THUMBNAIL_GENERATOR // Load the content of a 3mf file into the given model and preset bundle. extern bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model, bool check_version); // 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_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 }; // namespace Slic3r diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index f07c92da4..b185dc794 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -12,9 +12,14 @@ #include "../PrintConfig.hpp" #include "../Utils.hpp" #include "../I18N.hpp" +#include "../Geometry.hpp" #include "AMF.hpp" +#include +#include +namespace pt = boost::property_tree; + #include #include #include @@ -36,7 +41,8 @@ // Added x and y components of rotation // Added x, y and z components of scale // Added x, y and z components of mirror -const unsigned int VERSION_AMF = 2; +// 3 : Meshes saved in their local system; Added volumes' matrices and source data +const unsigned int VERSION_AMF = 3; const char* SLIC3RPE_AMF_VERSION = "slic3rpe_amf_version"; const char* SLIC3R_CONFIG_TYPE = "slic3rpe_config"; @@ -145,6 +151,8 @@ struct AMFParserContext NODE_TYPE_MIRRORY, // amf/constellation/instance/mirrory NODE_TYPE_MIRRORZ, // amf/constellation/instance/mirrorz 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_METADATA, // anywhere under amf/*/metadata }; @@ -225,7 +233,7 @@ struct AMFParserContext // Current instance allocated for an amf/constellation/instance subtree. Instance *m_instance; // Generic string buffer for vertices, face indices, metadata etc. - std::string m_value[3]; + std::string m_value[4]; // Pointer to config to update if config data are stored inside the amf file DynamicPrintConfig *m_config; @@ -266,6 +274,8 @@ void AMFParserContext::startElement(const char *name, const char **atts) } } else if (strcmp(name, "constellation") == 0) { node_type_new = NODE_TYPE_CONSTELLATION; + } else if (strcmp(name, "custom_gcodes_per_height") == 0) { + node_type_new = NODE_TYPE_CUSTOM_GCODE; } break; case 2: @@ -292,6 +302,13 @@ 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"); } break; case 3: @@ -560,15 +577,38 @@ void AMFParserContext::endElement(const char * /* name */) stl.stats.number_of_facets = int(m_volume_facets.size() / 3); stl.stats.original_num_facets = stl.stats.number_of_facets; stl_allocate(&stl); + + Slic3r::Geometry::Transformation transform; + if (m_version > 2) + transform = m_volume->get_transformation(); + + Transform3d inv_matrix = transform.get_matrix().inverse(); + for (size_t i = 0; i < m_volume_facets.size();) { stl_facet &facet = stl.facet_start[i/3]; - for (unsigned int v = 0; v < 3; ++ v) - memcpy(facet.vertex[v].data(), &m_object_vertices[m_volume_facets[i ++] * 3], 3 * sizeof(float)); + for (unsigned int v = 0; v < 3; ++v) + { + unsigned int tri_id = m_volume_facets[i++] * 3; + Vec3f vertex(m_object_vertices[tri_id + 0], m_object_vertices[tri_id + 1], m_object_vertices[tri_id + 2]); + if (m_version > 2) + // revert the vertices to the original mesh reference system + vertex = (inv_matrix * vertex.cast()).cast(); + ::memcpy((void*)facet.vertex[v].data(), (const void*)vertex.data(), 3 * sizeof(float)); + } } stl_get_size(&stl); mesh.repair(); m_volume->set_mesh(std::move(mesh)); - m_volume->center_geometry_after_creation(); + if (m_volume->source.input_file.empty() && (m_volume->type() == ModelVolumeType::MODEL_PART)) + { + m_volume->source.object_idx = (int)m_model.objects.size() - 1; + m_volume->source.volume_idx = (int)m_model.objects.back()->volumes.size() - 1; + m_volume->center_geometry_after_creation(); + } + else + // pass false if the mesh offset has been already taken from the data + m_volume->center_geometry_after_creation(m_volume->source.input_file.empty()); + m_volume->calculate_convex_hull(); m_volume_facets.clear(); m_volume = nullptr; @@ -591,6 +631,19 @@ void AMFParserContext::endElement(const char * /* name */) m_instance = nullptr; break; + case NODE_TYPE_GCODE_PER_HEIGHT: { + double height = 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_height.push_back(Model::CustomGCode(height, gcode, extruder, color)); + + 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()); @@ -664,6 +717,29 @@ void AMFParserContext::endElement(const char * /* name */) } else if (strcmp(opt_key, "volume_type") == 0) { m_volume->set_type(ModelVolume::type_from_string(m_value[1])); } + else if (strcmp(opt_key, "matrix") == 0) { + Geometry::Transformation transform; + transform.set_from_string(m_value[1]); + m_volume->set_transformation(transform); + } + else if (strcmp(opt_key, "source_file") == 0) { + m_volume->source.input_file = m_value[1]; + } + else if (strcmp(opt_key, "source_object_id") == 0) { + m_volume->source.object_idx = ::atoi(m_value[1].c_str()); + } + else if (strcmp(opt_key, "source_volume_id") == 0) { + m_volume->source.volume_idx = ::atoi(m_value[1].c_str()); + } + else if (strcmp(opt_key, "source_offset_x") == 0) { + m_volume->source.mesh_offset(0) = ::atof(m_value[1].c_str()); + } + else if (strcmp(opt_key, "source_offset_y") == 0) { + m_volume->source.mesh_offset(1) = ::atof(m_value[1].c_str()); + } + else if (strcmp(opt_key, "source_offset_z") == 0) { + m_volume->source.mesh_offset(2) = ::atof(m_value[1].c_str()); + } } } else if (m_path.size() == 3) { if (m_path[1] == NODE_TYPE_MATERIAL) { @@ -759,6 +835,15 @@ bool load_amf_file(const char *path, DynamicPrintConfig *config, Model *model) if (result) ctx.endDocument(); + for (ModelObject* o : model->objects) + { + for (ModelVolume* v : o->volumes) + { + if (v->source.input_file.empty() && (v->type() == ModelVolumeType::MODEL_PART)) + v->source.input_file = path; + } + } + return result; } @@ -978,7 +1063,7 @@ bool store_amf(std::string &path, Model *model, const DynamicPrintConfig *config stream << layer_height_profile.front(); for (size_t i = 1; i < layer_height_profile.size(); ++i) stream << ";" << layer_height_profile[i]; - stream << "\n \n"; + stream << "\n \n"; } // Export layer height ranges including the layer range specific config overrides. @@ -1056,7 +1141,28 @@ bool store_amf(std::string &path, Model *model, const DynamicPrintConfig *config if (volume->is_modifier()) stream << " 1\n"; stream << " " << ModelVolume::type_to_string(volume->type()) << "\n"; - const indexed_triangle_set &its = volume->mesh().its; + stream << " "; + const Transform3d& matrix = volume->get_matrix(); + for (int r = 0; r < 4; ++r) + { + for (int c = 0; c < 4; ++c) + { + stream << matrix(r, c); + if ((r != 3) || (c != 3)) + stream << " "; + } + } + stream << "\n"; + if (!volume->source.input_file.empty()) + { + stream << " " << xml_escape(volume->source.input_file) << "\n"; + stream << " " << volume->source.object_idx << "\n"; + stream << " " << volume->source.volume_idx << "\n"; + stream << " " << volume->source.mesh_offset(0) << "\n"; + stream << " " << volume->source.mesh_offset(1) << "\n"; + stream << " " << volume->source.mesh_offset(2) << "\n"; + } + const indexed_triangle_set &its = volume->mesh().its; for (size_t i = 0; i < its.indices.size(); ++i) { stream << " \n"; for (int j = 0; j < 3; ++j) @@ -1111,6 +1217,42 @@ bool store_amf(std::string &path, Model *model, const DynamicPrintConfig *config stream << instances; stream << " \n"; } + + if (!model->custom_gcode_per_height.empty()) + { + std::string out = ""; + pt::ptree tree; + + pt::ptree& main_tree = tree.add("custom_gcodes_per_height", ""); + + for (const Model::CustomGCode& code : model->custom_gcode_per_height) + { + pt::ptree& code_tree = main_tree.add("code", ""); + // store minX and maxZ + code_tree.put(".height", code.height); + code_tree.put(".gcode", code.gcode); + code_tree.put(".extruder", code.extruder); + code_tree.put(".color", code.color); + } + + if (!tree.empty()) + { + std::ostringstream oss; + pt::write_xml(oss, tree); + out = oss.str(); + + int del_header_pos = out.find("\n <", ">\n<"); + + stream << out << "\n"; + } + } + stream << "\n"; std::string internal_amf_filename = boost::ireplace_last_copy(boost::filesystem::path(path).filename().string(), ".zip.amf", ".amf"); diff --git a/src/libslic3r/Format/OBJ.cpp b/src/libslic3r/Format/OBJ.cpp index 85ca73551..aa389781a 100644 --- a/src/libslic3r/Format/OBJ.cpp +++ b/src/libslic3r/Format/OBJ.cpp @@ -15,39 +15,41 @@ namespace Slic3r { -bool load_obj(const char *path, Model *model, const char *object_name_in) +bool load_obj(const char *path, TriangleMesh *meshptr) { + if(meshptr == nullptr) return false; + // Parse the OBJ file. ObjParser::ObjData data; if (! ObjParser::objparse(path, data)) { -// die "Failed to parse $file\n" if !-e $path; + // die "Failed to parse $file\n" if !-e $path; return false; } - + // Count the faces and verify, that all faces are triangular. size_t num_faces = 0; - size_t num_quads = 0; + size_t num_quads = 0; for (size_t i = 0; i < data.vertices.size(); ) { size_t j = i; for (; j < data.vertices.size() && data.vertices[j].coordIdx != -1; ++ j) ; if (i == j) continue; - size_t face_vertices = j - i; - if (face_vertices != 3 && face_vertices != 4) { + size_t face_vertices = j - i; + if (face_vertices != 3 && face_vertices != 4) { // Non-triangular and non-quad faces are not supported as of now. return false; } - if (face_vertices == 4) - ++ num_quads; - ++ num_faces; + if (face_vertices == 4) + ++ num_quads; + ++ num_faces; i = j + 1; } - + // Convert ObjData into STL. - TriangleMesh mesh; + TriangleMesh &mesh = *meshptr; stl_file &stl = mesh.stl; stl.stats.type = inmemory; - stl.stats.number_of_facets = int(num_faces + num_quads); + stl.stats.number_of_facets = uint32_t(num_faces + num_quads); stl.stats.original_num_facets = int(num_faces + num_quads); // stl_allocate clears all the allocated data to zero, all normals are set to zeros as well. stl_allocate(&stl); @@ -68,14 +70,14 @@ bool load_obj(const char *path, Model *model, const char *object_name_in) ++ num_normals; } } - if (data.vertices[i].coordIdx != -1) { - // This is a quad. Produce the other triangle. - stl_facet &facet2 = stl.facet_start[i_face++]; + if (data.vertices[i].coordIdx != -1) { + // This is a quad. Produce the other triangle. + stl_facet &facet2 = stl.facet_start[i_face++]; facet2.vertex[0] = facet.vertex[0]; facet2.vertex[1] = facet.vertex[2]; - const ObjParser::ObjVertex &vertex = data.vertices[i++]; - memcpy(facet2.vertex[2].data(), &data.coordinates[vertex.coordIdx * 4], 3 * sizeof(float)); - if (vertex.normalIdx != -1) { + const ObjParser::ObjVertex &vertex = data.vertices[i++]; + memcpy(facet2.vertex[2].data(), &data.coordinates[vertex.coordIdx * 4], 3 * sizeof(float)); + if (vertex.normalIdx != -1) { normal(0) += data.normals[vertex.normalIdx*3]; normal(1) += data.normals[vertex.normalIdx*3+1]; normal(2) += data.normals[vertex.normalIdx*3+2]; @@ -96,25 +98,37 @@ bool load_obj(const char *path, Model *model, const char *object_name_in) if (len > EPSILON) facet.normal = normal / len; } - } + } stl_get_size(&stl); mesh.repair(); if (mesh.facets_count() == 0) { - // die "This STL file couldn't be read because it's empty.\n" + // die "This OBJ file couldn't be read because it's empty.\n" return false; } - - std::string object_name; - if (object_name_in == nullptr) { - const char *last_slash = strrchr(path, DIR_SEPARATOR); - object_name.assign((last_slash == nullptr) ? path : last_slash + 1); - } else - object_name.assign(object_name_in); - - model->add_object(object_name.c_str(), path, std::move(mesh)); + return true; } +bool load_obj(const char *path, Model *model, const char *object_name_in) +{ + TriangleMesh mesh; + + bool ret = load_obj(path, &mesh); + + if (ret) { + std::string object_name; + if (object_name_in == nullptr) { + const char *last_slash = strrchr(path, DIR_SEPARATOR); + object_name.assign((last_slash == nullptr) ? path : last_slash + 1); + } else + object_name.assign(object_name_in); + + model->add_object(object_name.c_str(), path, std::move(mesh)); + } + + return ret; +} + bool store_obj(const char *path, TriangleMesh *mesh) { //FIXME returning false even if write failed. diff --git a/src/libslic3r/Format/OBJ.hpp b/src/libslic3r/Format/OBJ.hpp index 36aa17951..3eb8b4139 100644 --- a/src/libslic3r/Format/OBJ.hpp +++ b/src/libslic3r/Format/OBJ.hpp @@ -5,8 +5,10 @@ namespace Slic3r { class TriangleMesh; class Model; +class ModelObject; // Load an OBJ file into a provided model. +extern bool load_obj(const char *path, TriangleMesh *mesh); extern bool load_obj(const char *path, Model *model, const char *object_name = nullptr); extern bool store_obj(const char *path, TriangleMesh *mesh); diff --git a/src/libslic3r/Format/PRUS.cpp b/src/libslic3r/Format/PRUS.cpp index 03ea71a83..d6f87197d 100644 --- a/src/libslic3r/Format/PRUS.cpp +++ b/src/libslic3r/Format/PRUS.cpp @@ -246,7 +246,7 @@ static void extract_model_from_archive( sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) { // Normal was mangled. Maybe denormals or "not a number" were stored? // Just reset the normal and silently ignore it. - memset(&facet.normal, 0, sizeof(facet.normal)); + facet.normal = stl_normal::Zero(); } facets.emplace_back(facet); } @@ -278,7 +278,7 @@ static void extract_model_from_archive( instance->set_rotation(instance_rotation); instance->set_scaling_factor(instance_scaling_factor); instance->set_offset(instance_offset); - if (group_id != (size_t)-1) + if (group_id != (unsigned int)(-1)) group_to_model_object[group_id] = model_object; } else { // This is not the 1st mesh of a group. Add it to the ModelObject. diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 9b06cda08..5d69d16c9 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -6,6 +6,7 @@ #include "Geometry.hpp" #include "GCode/PrintExtents.hpp" #include "GCode/WipeTower.hpp" +#include "ShortestPath.hpp" #include "Utils.hpp" #include @@ -17,6 +18,9 @@ #include #include #include +#if ENABLE_THUMBNAIL_GENERATOR +#include +#endif // ENABLE_THUMBNAIL_GENERATOR #include #include @@ -28,6 +32,8 @@ #include +#include "miniz_extension.hpp" + #if 0 // Enable debugging and asserts, even in the release build. #define DEBUG @@ -116,11 +122,11 @@ Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectP const Layer* layer1 = object->layers()[i * 2]; const Layer* layer2 = object->layers()[i * 2 + 1]; Polygons polys; - polys.reserve(layer1->slices.expolygons.size() + layer2->slices.expolygons.size()); - for (const ExPolygon &expoly : layer1->slices.expolygons) + polys.reserve(layer1->slices.size() + layer2->slices.size()); + for (const ExPolygon &expoly : layer1->slices) //FIXME no holes? polys.emplace_back(expoly.contour); - for (const ExPolygon &expoly : layer2->slices.expolygons) + for (const ExPolygon &expoly : layer2->slices) //FIXME no holes? polys.emplace_back(expoly.contour); polygons_per_layer[i] = union_(polys); @@ -129,8 +135,8 @@ Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectP if (object->layers().size() & 1) { const Layer *layer = object->layers().back(); Polygons polys; - polys.reserve(layer->slices.expolygons.size()); - for (const ExPolygon &expoly : layer->slices.expolygons) + polys.reserve(layer->slices.size()); + for (const ExPolygon &expoly : layer->slices) //FIXME no holes? polys.emplace_back(expoly.contour); polygons_per_layer.back() = union_(polys); @@ -274,7 +280,7 @@ static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const Vec2 return Point(scale_(wipe_tower_pt.x() - gcodegen.origin()(0)), scale_(wipe_tower_pt.y() - gcodegen.origin()(1))); } -std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const +std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z) const { if (new_extruder_id != -1 && new_extruder_id != tcr.new_tool) throw std::invalid_argument("Error: WipeTowerIntegration::append_tcr was asked to do a toolchange it didn't expect."); @@ -310,6 +316,15 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T gcode += gcodegen.unretract(); } + double current_z = gcodegen.writer().get_position().z(); + if (z == -1.) // in case no specific z was provided, print at current_z pos + z = current_z; + if (! is_approx(z, current_z)) { + gcode += gcodegen.writer().retract(); + gcode += gcodegen.writer().travel_to_z(z, "Travel down to the last wipe tower layer."); + gcode += gcodegen.writer().unretract(); + } + // Process the end filament gcode. std::string end_filament_gcode_str; @@ -376,7 +391,13 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T // A phony move to the end position at the wipe tower. gcodegen.writer().travel_to_xy(end_pos.cast()); gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos)); + if (! is_approx(z, current_z)) { + gcode += gcodegen.writer().retract(); + gcode += gcodegen.writer().travel_to_z(current_z, "Travel back up to the topmost object layer."); + gcode += gcodegen.writer().unretract(); + } + else { // Prepare a future wipe. gcodegen.m_wipe.path.points.clear(); if (new_extruder_id >= 0) { @@ -387,6 +408,7 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T Vec2f((std::abs(m_left - end_pos.x()) < std::abs(m_right - end_pos.x())) ? m_right : m_left, end_pos.y()))); } + } // Let the planner know we are traveling between objects. gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true; @@ -405,39 +427,44 @@ std::string WipeTowerIntegration::post_process_wipe_tower_moves(const WipeTower: Vec2f pos = tcr.start_pos; Vec2f transformed_pos = pos; Vec2f old_pos(-1000.1f, -1000.1f); + std::string never_skip_tag = WipeTower::never_skip_tag(); while (gcode_str) { std::getline(gcode_str, line); // we read the gcode line by line - // All G1 commands should be translated and rotated + // All G1 commands should be translated and rotated. X and Y coords are + // only pushed to the output when they differ from last time. + // WT generator can override this by appending the never_skip_tag if (line.find("G1 ") == 0) { + bool never_skip = false; + auto it = line.find(never_skip_tag); + if (it != std::string::npos) { + // remove the tag and remember we saw it + never_skip = true; + line.erase(it, it+never_skip_tag.size()); + } std::ostringstream line_out; std::istringstream line_str(line); line_str >> std::noskipws; // don't skip whitespace char ch = 0; while (line_str >> ch) { - if (ch == 'X') - line_str >> pos.x(); + if (ch == 'X' || ch =='Y') + line_str >> (ch == 'X' ? pos.x() : pos.y()); else - if (ch == 'Y') - line_str >> pos.y(); - else line_out << ch; } - transformed_pos = pos; - transformed_pos = Eigen::Rotation2Df(angle) * transformed_pos; - transformed_pos += translation; + transformed_pos = Eigen::Rotation2Df(angle) * pos + translation; - if (transformed_pos != old_pos) { + if (transformed_pos != old_pos || never_skip) { line = line_out.str(); std::ostringstream oss; oss << std::fixed << std::setprecision(3) << "G1 "; - if (transformed_pos.x() != old_pos.x()) + if (transformed_pos.x() != old_pos.x() || never_skip) oss << " X" << transformed_pos.x() - extruder_offset.x(); - if (transformed_pos.y() != old_pos.y()) + if (transformed_pos.y() != old_pos.y() || never_skip) oss << " Y" << transformed_pos.y() - extruder_offset.y(); - + oss << " "; line.replace(line.find("G1 "), 3, oss.str()); old_pos = transformed_pos; } @@ -505,12 +532,28 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen) std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, bool finish_layer) { std::string gcode; - assert(m_layer_idx >= 0 && size_t(m_layer_idx) <= m_tool_changes.size()); + assert(m_layer_idx >= 0); if (! m_brim_done || gcodegen.writer().need_toolchange(extruder_id) || finish_layer) { if (m_layer_idx < (int)m_tool_changes.size()) { if (! (size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size())) throw std::runtime_error("Wipe tower generation failed, possibly due to empty first layer."); - gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id); + + + // Calculate where the wipe tower layer will be printed. -1 means that print z will not change, + // resulting in a wipe tower with sparse layers. + double wipe_tower_z = -1; + bool ignore_sparse = false; + if (gcodegen.config().wipe_tower_no_sparse_layers.value) { + wipe_tower_z = m_last_wipe_tower_print_z; + ignore_sparse = (m_brim_done && m_tool_changes[m_layer_idx].size() == 1 && m_tool_changes[m_layer_idx].front().initial_tool == m_tool_changes[m_layer_idx].front().new_tool); + if (m_tool_change_idx == 0 && ! ignore_sparse) + wipe_tower_z = m_last_wipe_tower_print_z + m_tool_changes[m_layer_idx].front().layer_height; + } + + if (! ignore_sparse) { + gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id, wipe_tower_z); + m_last_wipe_tower_print_z = wipe_tower_z; + } } m_brim_done = true; } @@ -541,7 +584,7 @@ std::vector GCode::collect_layers_to_print(const PrintObjec //FIXME should we use the printing extruders instead? double gap_over_supports = object.config().support_material_contact_distance_top; // FIXME should we test object.config().support_material_synchronize_layers ? IN prusa code, the support layers are synchronized with object layers iff soluble supports. - //assert(gap_over_supports != 0. || object.config().support_material_synchronize_layers); + assert(! object.config().support_material || gap_over_supports != 0. || object.config().support_material_synchronize_layers); if (gap_over_supports != 0.) { gap_over_supports = std::max(0., gap_over_supports); // Not a soluble support, @@ -650,7 +693,11 @@ std::vector>> GCode::collec return layers_to_print; } +#if ENABLE_THUMBNAIL_GENERATOR +void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb) +#else void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_data) +#endif // ENABLE_THUMBNAIL_GENERATOR { PROFILE_CLEAR(); @@ -676,7 +723,11 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ try { m_placeholder_parser_failed_templates.clear(); +#if ENABLE_THUMBNAIL_GENERATOR + this->_do_export(*print, file, thumbnail_cb); +#else this->_do_export(*print, file); +#endif // ENABLE_THUMBNAIL_GENERATOR fflush(file); if (ferror(file)) { fclose(file); @@ -741,7 +792,11 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ PROFILE_OUTPUT(debug_out_path("gcode-export-profile.txt").c_str()); } +#if ENABLE_THUMBNAIL_GENERATOR +void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb) +#else void GCode::_do_export(Print &print, FILE *file) +#endif // ENABLE_THUMBNAIL_GENERATOR { PROFILE_FUNC(); @@ -778,22 +833,26 @@ void GCode::_do_export(Print &print, FILE *file) { m_silent_time_estimator.reset(); m_silent_time_estimator.set_dialect(print.config().gcode_flavor); - m_silent_time_estimator.set_max_acceleration((float)print.config().machine_max_acceleration_extruding.values[1]); - m_silent_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values[1]); - m_silent_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values[1]); - m_silent_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values[1]); - m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values[1]); - m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values[1]); - m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values[1]); - m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values[1]); - m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values[1]); - m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values[1]); - m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values[1]); - m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values[1]); - m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values[1]); - m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values[1]); - m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values[1]); - m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values[1]); + /* "Stealth mode" values can be just a copy of "normal mode" values + * (when they aren't input for a printer preset). + * Thus, use back value from values, instead of second one, which could be absent + */ + m_silent_time_estimator.set_max_acceleration((float)print.config().machine_max_acceleration_extruding.values.back()); + m_silent_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values.back()); + m_silent_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values.back()); + m_silent_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values.back()); + m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values.back()); + m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values.back()); + m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values.back()); + m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values.back()); + m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values.back()); + m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values.back()); + m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values.back()); + m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values.back()); + m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values.back()); + m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values.back()); + m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values.back()); + m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values.back()); if (print.config().single_extruder_multi_material) { // As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they // are considered to be active for the single extruder multi-material printers only. @@ -851,7 +910,8 @@ void GCode::_do_export(Print &print, FILE *file) std::sort(zs.begin(), zs.end()); m_layer_count += (unsigned int)(object->copies().size() * (std::unique(zs.begin(), zs.end()) - zs.begin())); } - } else { + } + else { // Print all objects with the same print_z together. std::vector zs; for (auto object : print.objects()) { @@ -870,8 +930,9 @@ void GCode::_do_export(Print &print, FILE *file) this->apply_print_config(print.config()); this->set_extruders(print.extruders()); - // Initialize colorprint. - m_colorprint_heights = cast(print.config().colorprint_heights.values); + // Initialize custom gcode + Model* model = print.get_object(0)->model_object()->get_model(); + m_custom_g_code_heights = model->custom_gcode_per_height; // Initialize autospeed. { @@ -930,6 +991,49 @@ void GCode::_do_export(Print &print, FILE *file) // Write information on the generator. _write_format(file, "; %s\n\n", Slic3r::header_slic3r_generated().c_str()); + +#if ENABLE_THUMBNAIL_GENERATOR + // Write thumbnails using base64 encoding + if (thumbnail_cb != nullptr) + { + const size_t max_row_length = 78; + ThumbnailsList thumbnails; + thumbnail_cb(thumbnails, print.full_print_config().option("thumbnails")->values, true, true, true, true); + for (const ThumbnailData& data : thumbnails) + { + if (data.is_valid()) + { + size_t png_size = 0; + void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)data.pixels.data(), data.width, data.height, 4, &png_size, MZ_DEFAULT_LEVEL, 1); + if (png_data != nullptr) + { + std::string encoded; + encoded.resize(boost::beast::detail::base64::encoded_size(png_size)); + encoded.resize(boost::beast::detail::base64::encode((void*)&encoded[0], (const void*)png_data, png_size)); + + _write_format(file, "\n;\n; thumbnail begin %dx%d %d\n", data.width, data.height, encoded.size()); + + unsigned int row_count = 0; + while (encoded.size() > max_row_length) + { + _write_format(file, "; %s\n", encoded.substr(0, max_row_length).c_str()); + encoded = encoded.substr(max_row_length); + ++row_count; + } + + if (encoded.size() > 0) + _write_format(file, "; %s\n", encoded.c_str()); + + _write(file, "; thumbnail end\n;\n"); + + mz_free(png_data); + } + } + print.throw_if_canceled(); + } + } +#endif // ENABLE_THUMBNAIL_GENERATOR + // Write notes (content of the Print Settings tab -> Notes) { std::list lines; @@ -992,6 +1096,9 @@ void GCode::_do_export(Print &print, FILE *file) _writeln(file, GCodeTimeEstimator::Silent_First_M73_Output_Placeholder_Tag); } + // Hold total number of print toolchanges. Check for negative toolchanges (single extruder mode) and set to 0 (no tool change). + int total_toolchanges = std::max(0, print.wipe_tower_data().number_of_toolchanges); + // Prepare the helper object for replacing placeholders in custom G-code and output filename. m_placeholder_parser = print.placeholder_parser(); m_placeholder_parser.update_timestamp(); @@ -1034,6 +1141,47 @@ void GCode::_do_export(Print &print, FILE *file) } print.throw_if_canceled(); + /* To avoid change filament for non-used extruder for Multi-material, + * check model->custom_gcode_per_height using tool_ordering values + * */ + if (!m_custom_g_code_heights. empty()) + { + bool delete_executed = false; + auto it = m_custom_g_code_heights.end(); + while (it != m_custom_g_code_heights.begin()) + { + --it; + if (it->gcode != ColorChangeCode) + continue; + + auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), LayerTools(it->height)); + + bool used_extruder = false; + for (; it_layer_tools != tool_ordering.end(); it_layer_tools++) + { + const std::vector& extruders = it_layer_tools->extruders; + if (std::find(extruders.begin(), extruders.end(), (unsigned)(it->extruder-1)) != extruders.end()) + { + used_extruder = true; + break; + } + } + if (used_extruder) + continue; + + /* If we are there, current extruder wouldn't be used, + * so this color change is a redundant move. + * Delete this item from m_custom_g_code_heights + * */ + it = m_custom_g_code_heights.erase(it); + delete_executed = true; + } + + if (delete_executed) + model->custom_gcode_per_height = m_custom_g_code_heights; + } + + m_cooling_buffer->set_current_extruder(initial_extruder_id); // Emit machine envelope limits for the Marlin firmware. @@ -1054,6 +1202,7 @@ void GCode::_do_export(Print &print, FILE *file) // For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided. m_placeholder_parser.set("has_wipe_tower", has_wipe_tower); m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming); + m_placeholder_parser.set("total_toolchanges", total_toolchanges); std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config().start_gcode.value, initial_extruder_id); // Set bed temperature if the start G-code does not contain any bed temp control G-codes. this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true); @@ -1182,7 +1331,7 @@ void GCode::_do_export(Print &print, FILE *file) 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()), © - object.copies().data()); + 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 @@ -1196,12 +1345,8 @@ void GCode::_do_export(Print &print, FILE *file) } } } else { - // Order objects using a nearest neighbor search. - std::vector object_indices; - Points object_reference_points; - for (PrintObject *object : print.objects()) - object_reference_points.push_back(object->copies().front()); - Slic3r::Geometry::chained_path(object_reference_points, object_indices); + // 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); @@ -1240,7 +1385,7 @@ void GCode::_do_export(Print &print, FILE *file) const LayerTools &layer_tools = tool_ordering.tools_for_layer(layer.first); if (m_wipe_tower && layer_tools.has_wipe_tower) m_wipe_tower->next_layer(); - this->process_layer(file, print, layer.second, layer_tools, size_t(-1)); + this->process_layer(file, print, layer.second, layer_tools, &print_object_instances_ordering, size_t(-1)); print.throw_if_canceled(); } #ifdef HAS_PRESSURE_EQUALIZER @@ -1308,7 +1453,7 @@ void GCode::_do_export(Print &print, FILE *file) print.m_print_statistics.estimated_normal_color_print_times = m_normal_time_estimator.get_color_times_dhms(true); if (m_silent_time_estimator_enabled) print.m_print_statistics.estimated_silent_color_print_times = m_silent_time_estimator.get_color_times_dhms(true); - + print.m_print_statistics.total_toolchanges = total_toolchanges; std::vector extruders = m_writer.extruders(); if (! extruders.empty()) { std::pair out_filament_used_mm ("; filament used [mm] = ", 0); @@ -1358,6 +1503,8 @@ void GCode::_do_export(Print &print, FILE *file) } _write_format(file, "; total filament used [g] = %.1lf\n", print.m_print_statistics.total_weight); _write_format(file, "; total filament cost = %.1lf\n", print.m_print_statistics.total_cost); + if (print.m_print_statistics.total_toolchanges > 0) + _write_format(file, "; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges); _write_format(file, "; estimated printing time (normal mode) = %s\n", m_normal_time_estimator.get_time_dhms().c_str()); if (m_silent_time_estimator_enabled) _write_format(file, "; estimated printing time (silent mode) = %s\n", m_silent_time_estimator.get_time_dhms().c_str()); @@ -1551,8 +1698,54 @@ inline std::vector& object_islands_by_extruder( return islands; } +std::vector GCode::sort_print_object_instances( + std::vector &objects_by_extruder, + const std::vector &layers, + // Ordering must be defined for normal (non-sequential print). + 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) +{ + std::vector out; + + if (ordering == nullptr) { + // Sequential print, single object is being printed. + for (ObjectByExtruder &object_by_extruder : objects_by_extruder) { + const size_t layer_id = &object_by_extruder - objects_by_extruder.data(); + const PrintObject *print_object = layers[layer_id].object(); + if (print_object) + out.emplace_back(object_by_extruder, layer_id, *print_object, single_object_instance_idx); + } + } else { + // Create mapping from PrintObject* to ObjectByExtruder*. + std::vector> sorted; + sorted.reserve(objects_by_extruder.size()); + for (ObjectByExtruder &object_by_extruder : objects_by_extruder) { + const size_t layer_id = &object_by_extruder - objects_by_extruder.data(); + const PrintObject *print_object = layers[layer_id].object(); + if (print_object) + sorted.emplace_back(print_object, &object_by_extruder); + } + std::sort(sorted.begin(), sorted.end()); + + 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]; + 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); + } + } + } + return out; +} + // In sequential mode, process_layer is called once per each object and its copy, -// therefore layers will contain a single entry and single_object_idx will point to the copy of the object. +// therefore layers will contain a single entry and single_object_instance_idx will point to the copy of the object. // In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated. // For multi-material prints, this routine minimizes extruder switches by gathering extruder specific extrusion paths // and performing the extruder specific extrusions together. @@ -1563,14 +1756,16 @@ void GCode::process_layer( // Set of object & print layers of the same PrintObject and with the same print_z. const std::vector &layers, const LayerTools &layer_tools, + // Pairs of PrintObject index and its instance index. + 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) + const size_t single_object_instance_idx) { assert(! layers.empty()); // assert(! layer_tools.extruders.empty()); // Either printing all copies of all objects, or just a single copy of a single object. - assert(single_object_idx == size_t(-1) || layers.size() == 1); + assert(single_object_instance_idx == size_t(-1) || layers.size() == 1); if (layer_tools.extruders.empty()) // Nothing to extrude. @@ -1653,19 +1848,66 @@ void GCode::process_layer( // In case there are more toolchange requests that weren't done yet and should happen simultaneously, erase them all. // (Layers can be close to each other, model could have been resliced with bigger layer height, ...). bool colorprint_change = false; - while (!m_colorprint_heights.empty() && m_colorprint_heights.front()-EPSILON < layer.print_z) { - m_colorprint_heights.erase(m_colorprint_heights.begin()); + + std::string custom_code = ""; + std::string pause_print_msg = ""; + int m600_before_extruder = -1; + while (!m_custom_g_code_heights.empty() && m_custom_g_code_heights.front().height-EPSILON < layer.print_z) { + custom_code = m_custom_g_code_heights.front().gcode; + + if (custom_code == ColorChangeCode && m_custom_g_code_heights.front().extruder > 0) + m600_before_extruder = m_custom_g_code_heights.front().extruder - 1; + if (custom_code == PausePrintCode) + pause_print_msg = m_custom_g_code_heights.front().color; + + m_custom_g_code_heights.erase(m_custom_g_code_heights.begin()); colorprint_change = true; } // we should add or not colorprint_change in respect to nozzle_diameter count instead of really used extruders count - if (colorprint_change && print./*extruders()*/config().nozzle_diameter.size()==1) + + // don't save "tool_change"(ExtruderChangeCode) code to GCode + if (colorprint_change && custom_code != ExtruderChangeCode) { + const bool single_material_print = print.config().nozzle_diameter.size() == 1; + + if (custom_code == ColorChangeCode) // color change { // add tag for analyzer - gcode += "; " + GCodeAnalyzer::Color_Change_Tag + "\n"; + gcode += "; " + GCodeAnalyzer::Color_Change_Tag + ",T" + std::to_string(m600_before_extruder) + "\n"; // add tag for time estimator gcode += "; " + GCodeTimeEstimator::Color_Change_Tag + "\n"; - gcode += "M600\n"; + + if (!single_material_print && m600_before_extruder >= 0 && first_extruder_id != m600_before_extruder + // && !MMU1 + ) { + //! FIXME_in_fw show message during print pause + gcode += "M601\n"; // pause print + gcode += "M117 Change filament for Extruder " + std::to_string(m600_before_extruder) + "\n"; + } + else + gcode += custom_code + "\n"; + } + else + { + if (custom_code == PausePrintCode) // Pause print + { + // add tag for analyzer + gcode += "; " + GCodeAnalyzer::Pause_Print_Tag + "\n"; + //! FIXME_in_fw show message during print pause + if (!pause_print_msg.empty()) + gcode += "M117 " + pause_print_msg + "\n"; + // add tag for time estimator + //gcode += "; " + GCodeTimeEstimator::Pause_Print_Tag + "\n"; + } + else // custom Gcode + { + // add tag for analyzer + gcode += "; " + GCodeAnalyzer::Custom_Code_Tag + "\n"; + // add tag for time estimator + //gcode += "; " + GCodeTimeEstimator::Custom_Code_Tag + "\n"; + } + gcode += custom_code + "\n"; + } } @@ -1779,16 +2021,24 @@ void GCode::process_layer( // - for each island, we extrude perimeters first, unless user set the infill_first // option // (Still, we have to keep track of regions because we need to apply their config) - size_t n_slices = layer.slices.expolygons.size(); - std::vector layer_surface_bboxes; - layer_surface_bboxes.reserve(n_slices); - for (const ExPolygon &expoly : layer.slices.expolygons) - layer_surface_bboxes.push_back(get_extents(expoly.contour)); + size_t n_slices = layer.slices.size(); + const std::vector &layer_surface_bboxes = layer.slices_bboxes; + // Traverse the slices in an increasing order of bounding box size, so that the islands inside another islands are tested first, + // so we can just test a point inside ExPolygon::contour and we may skip testing the holes. + std::vector slices_test_order; + slices_test_order.reserve(n_slices); + for (size_t i = 0; i < n_slices; ++ i) + slices_test_order.emplace_back(i); + std::sort(slices_test_order.begin(), slices_test_order.end(), [&layer_surface_bboxes](int i, int j) { + const Vec2d s1 = layer_surface_bboxes[i].size().cast(); + const Vec2d s2 = layer_surface_bboxes[j].size().cast(); + return s1.x() * s1.y() < s2.x() * s2.y(); + }); auto point_inside_surface = [&layer, &layer_surface_bboxes](const size_t i, const Point &point) { const BoundingBox &bbox = layer_surface_bboxes[i]; return point(0) >= bbox.min(0) && point(0) < bbox.max(0) && point(1) >= bbox.min(1) && point(1) < bbox.max(1) && - layer.slices.expolygons[i].contour.contains(point); + layer.slices[i].contour.contains(point); }; for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) { @@ -1831,16 +2081,16 @@ void GCode::process_layer( extruder, &layer_to_print - layers.data(), layers.size(), n_slices+1); - for (size_t i = 0; i <= n_slices; ++i) { + for (size_t i = 0; i <= n_slices; ++ i) { + bool last = i == n_slices; + size_t island_idx = last ? n_slices : slices_test_order[i]; if (// fill->first_point does not fit inside any slice - i == n_slices || + last || // fill->first_point fits inside ith slice - point_inside_surface(i, fill->first_point())) { - if (islands[i].by_region.empty()) { - islands[i].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region()); - } - //don't do fill->entities because it will discard no_sort - islands[i].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->copies().size()); + point_inside_surface(island_idx, fill->first_point())) { + if (islands[island_idx].by_region.empty()) + islands[island_idx].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region()); + islands[island_idx].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->copies().size()); break; } } @@ -1904,20 +2154,20 @@ void GCode::process_layer( m_avoid_crossing_perimeters.disable_once = true; } //extrude object-only skirt - if (single_object_idx != size_t(-1) && !layers.front().object()->skirt().empty() + if (single_object_instance_idx != size_t(-1) && !layers.front().object()->skirt().empty() && extruder_id == layer_tools.extruders.front()) { const PrintObject *print_object = layers.front().object(); - this->set_origin(unscale(print_object->copies()[single_object_idx])); + this->set_origin(unscale(print_object->copies()[single_object_instance_idx])); if (this->m_layer != nullptr && this->m_layer->id() < m_config.skirt_height) { for (const ExtrusionEntity *ee : print_object->skirt().entities) gcode += this->extrude_entity(*ee, "skirt", m_config.support_material_speed.value); } } //extrude object-only brim - if (single_object_idx != size_t(-1) && !layers.front().object()->brim().empty() + if (single_object_instance_idx != size_t(-1) && !layers.front().object()->brim().empty() && extruder_id == layer_tools.extruders.front()) { const PrintObject *print_object = layers.front().object(); - this->set_origin(unscale(print_object->copies()[single_object_idx])); + this->set_origin(unscale(print_object->copies()[single_object_instance_idx])); if (this->m_layer != nullptr && this->m_layer->id() == 0) { m_avoid_crossing_perimeters.use_external_mp = true; for (const ExtrusionEntity *ee : print_object->brim().entities) @@ -1932,66 +2182,52 @@ void GCode::process_layer( if (objects_by_extruder_it == by_extruder.end()) continue; + std::vector instances_to_print = sort_print_object_instances(objects_by_extruder_it->second, layers, ordering, single_object_instance_idx); + // We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature): bool is_anything_overridden = const_cast(layer_tools).wiping_extrusions().is_anything_overridden(); for (int print_wipe_extrusions = is_anything_overridden; print_wipe_extrusions>=0; --print_wipe_extrusions) { if (is_anything_overridden && print_wipe_extrusions == 0) gcode+="; PURGING FINISHED\n"; - for (ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) { - const size_t layer_id = &object_by_extruder - objects_by_extruder_it->second.data(); - const PrintObject *print_object = layers[layer_id].object(); - if (print_object == nullptr) - // This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z. - continue; - - m_config.apply(print_object->config(), true); - m_layer = layers[layer_id].layer(); + for (InstanceToPrint &instance_to_print : instances_to_print) { + m_config.apply(instance_to_print.print_object.config(), true); + m_layer = layers[instance_to_print.layer_id].layer(); if (m_config.avoid_crossing_perimeters) m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true)); - Points copies; - if (single_object_idx == size_t(-1)) - copies = print_object->copies(); - else - copies.push_back(print_object->copies()[single_object_idx]); - //FIXME: Sort the copies by the closest point starting with the current print position. - // be careful to set a good copy_idx (used to identify the copy). - unsigned int copy_id = 0; - for (const Point © : copies) { if (this->config().gcode_label_objects) - gcode += std::string("; printing object ") + print_object->model_object()->name - + " id:" + std::to_string(std::find(this->m_ordered_objects.begin(), this->m_ordered_objects.end(), print_object) - this->m_ordered_objects.begin()) - + " copy " + std::to_string(copy_id) + "\n"; + gcode += std::string("; printing object ") + instance_to_print.print_object.model_object()->name + + " id:" + std::to_string(std::find(this->m_ordered_objects.begin(), this->m_ordered_objects.end(), &instance_to_print.print_object) - this->m_ordered_objects.begin()) + + " 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. - std::pair this_object_copy(print_object, copy); + const Point &offset = instance_to_print.print_object.copies()[instance_to_print.instance_id]; + 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; m_last_obj_copy = this_object_copy; - this->set_origin(unscale(copy)); - if (object_by_extruder.support != nullptr && !print_wipe_extrusions) { - m_layer = layers[layer_id].support_layer; + this->set_origin(unscale(offset)); + if (instance_to_print.object_by_extruder.support != nullptr && !print_wipe_extrusions) { + m_layer = layers[instance_to_print.layer_id].support_layer; gcode += this->extrude_support( // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths. - object_by_extruder.support->chained_path_from(m_last_pos, false, object_by_extruder.support_extrusion_role)); - m_layer = layers[layer_id].layer(); + instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, instance_to_print.object_by_extruder.support_extrusion_role)); + m_layer = layers[instance_to_print.layer_id].layer(); } - for (ObjectByExtruder::Island &island : object_by_extruder.islands) { - const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(copy_id, extruder_id, print_wipe_extrusions) : island.by_region; + for (ObjectByExtruder::Island &island : instance_to_print.object_by_extruder.islands) { + const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(instance_to_print.instance_id, extruder_id, print_wipe_extrusions) : island.by_region; gcode += this->extrude_infill(print, by_region_specific, true); - gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]); + gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[instance_to_print.layer_id]); gcode += this->extrude_infill(print, by_region_specific, false); } if (this->config().gcode_label_objects) - gcode += std::string("; stop printing object ") + print_object->model_object()->name - + " id:" + std::to_string((std::find(this->m_ordered_objects.begin(), this->m_ordered_objects.end(), print_object) - this->m_ordered_objects.begin())) - + " copy " + std::to_string(copy_id) + "\n"; - ++ copy_id; + gcode += std::string("; stop printing object ") + instance_to_print.print_object.model_object()->name + + " id:" + std::to_string((std::find(this->m_ordered_objects.begin(), this->m_ordered_objects.end(), &instance_to_print.print_object) - this->m_ordered_objects.begin())) + + " copy " + std::to_string(instance_to_print.instance_id) + "\n"; } } } - } // Apply spiral vase post-processing if this layer contains suitable geometry // (we must feed all the G-code into the post-processor, including the first @@ -2005,6 +2241,12 @@ void GCode::process_layer( if (m_cooling_buffer) gcode = m_cooling_buffer->process_layer(gcode, layer.id()); + // add tag for analyzer + if (gcode.find(GCodeAnalyzer::Pause_Print_Tag) != gcode.npos) + gcode += "\n; " + GCodeAnalyzer::End_Pause_Print_Or_Custom_Code_Tag + "\n"; + else if (gcode.find(GCodeAnalyzer::Custom_Code_Tag) != gcode.npos) + gcode += "\n; " + GCodeAnalyzer::End_Pause_Print_Or_Custom_Code_Tag + "\n"; + #ifdef HAS_PRESSURE_EQUALIZER // Apply pressure equalization if enabled; // printf("G-code before filter:\n%s\n", gcode.c_str()); @@ -2600,6 +2842,28 @@ void GCode::split_at_seam_pos(ExtrusionLoop &loop, std::unique_ptrobject()] = polygon.points[idx_min]; } + // Export the contour into a SVG file. + #if 0 + { + static int iRun = 0; + SVG svg(debug_out_path("GCode_extrude_loop-%d.svg", iRun ++)); + if (m_layer->lower_layer != NULL) + svg.draw(m_layer->lower_layer->slices); + for (size_t i = 0; i < loop.paths.size(); ++ i) + svg.draw(loop.paths[i].as_polyline(), "red"); + Polylines polylines; + for (size_t i = 0; i < loop.paths.size(); ++ i) + polylines.push_back(loop.paths[i].as_polyline()); + Slic3r::Polygons polygons; + coordf_t nozzle_dmr = EXTRUDER_CONFIG(nozzle_diameter); + coord_t delta = scale_(0.5*nozzle_dmr); + Slic3r::offset(polylines, &polygons, delta); +// for (size_t i = 0; i < polygons.size(); ++ i) svg.draw((Polyline)polygons[i], "blue"); + svg.draw(last_pos, "green", 3); + svg.draw(polygon.points[idx_min], "yellow", 3); + svg.Close(); + } + #endif // Split the loop at the point with a minium penalty. if (!loop.split_at_vertex(polygon.points[idx_min])) @@ -2819,7 +3083,7 @@ std::string GCode::extrude_entity(const ExtrusionEntity &entity, const std::stri void GCode::use(const ExtrusionEntityCollection &collection) { ExtrusionEntityCollection chained; if (collection.no_sort) chained = collection; - else chained = collection.chained_path_from(m_last_pos, false); + else chained = collection.chained_path_from(m_last_pos); for (const ExtrusionEntity *next_entity : chained.entities) { next_entity->visit(*this); } @@ -2892,7 +3156,7 @@ std::string GCode::extrude_infill(const Print &print, const std::vectorconfig().infill_first == is_infill_first) { m_config.apply(print.regions()[®ion - &by_region.front()]->config()); - ExtrusionEntityCollection chained = region.infills.chained_path_from(m_last_pos, false); + ExtrusionEntityCollection chained = region.infills.chained_path_from(m_last_pos); gcode += extrude_entity(chained, "infill"); } } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 706ef9db1..a19484b28 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -17,6 +17,9 @@ #include "GCodeTimeEstimator.hpp" #include "EdgeGrid.hpp" #include "GCode/Analyzer.hpp" +#if ENABLE_THUMBNAIL_GENERATOR +#include "GCode/ThumbnailData.hpp" +#endif // ENABLE_THUMBNAIL_GENERATOR #include #include @@ -111,7 +114,7 @@ public: private: WipeTowerIntegration& operator=(const WipeTowerIntegration&); - std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const; + std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z = -1.) const; // Postprocesses gcode: rotates and moves G1 extrusions and returns result std::string post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const; @@ -132,6 +135,7 @@ private: int m_tool_change_idx; bool m_brim_done; bool i_have_brim = false; + double m_last_wipe_tower_print_z = 0.f; }; class GCode : ExtrusionVisitorConst { @@ -163,7 +167,11 @@ public: // throws std::runtime_exception on error, // throws CanceledException through print->throw_if_canceled(). +#if ENABLE_THUMBNAIL_GENERATOR + void do_export(Print* print, const char* path, GCodePreviewData* preview_data = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); +#else void do_export(Print *print, const char *path, GCodePreviewData *preview_data = nullptr); +#endif // ENABLE_THUMBNAIL_GENERATOR // Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests. const Vec2d& origin() const { return m_origin; } @@ -193,7 +201,11 @@ public: static void append_full_config(const Print& print, std::string& str); protected: +#if ENABLE_THUMBNAIL_GENERATOR + void _do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb); +#else void _do_export(Print &print, FILE *file); +#endif //ENABLE_THUMBNAIL_GENERATOR // Object and support extrusions of the same PrintObject at the same print_z. struct LayerToPrint @@ -205,7 +217,7 @@ protected: const PrintObject* object() const { return (this->layer() != nullptr) ? this->layer()->object() : nullptr; } coordf_t print_z() const { return (object_layer != nullptr && support_layer != nullptr) ? 0.5 * (object_layer->print_z + support_layer->print_z) : this->layer()->print_z; } }; - static std::vector collect_layers_to_print(const PrintObject &object); + static std::vector collect_layers_to_print(const PrintObject &object); static std::vector>> collect_layers_to_print(const Print &print); void process_layer( // Write into the output file. @@ -213,7 +225,9 @@ protected: 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 LayerTools &layer_tools, + // Pairs of PrintObject index and its instance index. + 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)); @@ -275,6 +289,25 @@ protected: std::vector islands; }; + struct InstanceToPrint + { + InstanceToPrint(ObjectByExtruder &object_by_extruder, size_t layer_id, const PrintObject &print_object, size_t instance_id) : + object_by_extruder(object_by_extruder), layer_id(layer_id), print_object(print_object), instance_id(instance_id) {} + + ObjectByExtruder &object_by_extruder; + const size_t layer_id; + const PrintObject &print_object; + // Instance idx of the copy of a print object. + const size_t instance_id; + }; + + std::vector sort_print_object_instances( + std::vector &objects_by_extruder, + const std::vector &layers, + // Ordering must be defined for normal (non-sequential print). + 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); std::string extrude_perimeters(const Print &print, const std::vector &by_region, std::unique_ptr &lower_layer_edge_grid); std::string extrude_infill(const Print &print, const std::vector &by_region, bool is_infill_first); @@ -346,9 +379,12 @@ protected: bool m_second_layer_things_done; // Index of a last object copy extruded. std::pair m_last_obj_copy; - // Layer heights for colorprint - updated before the export and erased during the process - // so no toolchange occurs twice. - std::vector m_colorprint_heights; + /* Extensions for colorprint - now it's not a just color_print_heights, + * there can be some custom gcode. + * Updated before the export and erased during the process, + * so no toolchange occurs twice. + * */ + std::vector m_custom_g_code_heights; // ordered list of object, to give them a unique id. std::vector m_ordered_objects; diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp index 6f11b8c36..27bb2fa55 100644 --- a/src/libslic3r/GCode/Analyzer.cpp +++ b/src/libslic3r/GCode/Analyzer.cpp @@ -20,6 +20,7 @@ static const unsigned int DEFAULT_EXTRUDER_ID = 0; static const unsigned int DEFAULT_COLOR_PRINT_ID = 0; static const Slic3r::Vec3d DEFAULT_START_POSITION = Slic3r::Vec3d(0.0f, 0.0f, 0.0f); static const float DEFAULT_START_EXTRUSION = 0.0f; +static const float DEFAULT_FAN_SPEED = 0.0f; namespace Slic3r { @@ -28,6 +29,9 @@ const std::string GCodeAnalyzer::Mm3_Per_Mm_Tag = "_ANALYZER_MM3_PER_MM:"; const std::string GCodeAnalyzer::Width_Tag = "_ANALYZER_WIDTH:"; const std::string GCodeAnalyzer::Height_Tag = "_ANALYZER_HEIGHT:"; const std::string GCodeAnalyzer::Color_Change_Tag = "_ANALYZER_COLOR_CHANGE"; +const std::string GCodeAnalyzer::Pause_Print_Tag = "_ANALYZER_PAUSE_PRINT"; +const std::string GCodeAnalyzer::Custom_Code_Tag = "_ANALYZER_CUSTOM_CODE"; +const std::string GCodeAnalyzer::End_Pause_Print_Or_Custom_Code_Tag = "_ANALYZER_END_PAUSE_PRINT_OR_CUSTOM_CODE"; const double GCodeAnalyzer::Default_mm3_per_mm = 0.0; const float GCodeAnalyzer::Default_Width = 0.0f; @@ -36,21 +40,23 @@ const float GCodeAnalyzer::Default_Height = 0.0f; GCodeAnalyzer::Metadata::Metadata() : extrusion_role(erNone) , extruder_id(DEFAULT_EXTRUDER_ID) - , cp_color_id(DEFAULT_COLOR_PRINT_ID) , mm3_per_mm(GCodeAnalyzer::Default_mm3_per_mm) , width(GCodeAnalyzer::Default_Width) , height(GCodeAnalyzer::Default_Height) , feedrate(DEFAULT_FEEDRATE) + , fan_speed(DEFAULT_FAN_SPEED) + , cp_color_id(DEFAULT_COLOR_PRINT_ID) { } -GCodeAnalyzer::Metadata::Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, unsigned int cp_color_id/* = 0*/) +GCodeAnalyzer::Metadata::Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, float fan_speed, unsigned int cp_color_id/* = 0*/) : extrusion_role(extrusion_role) , extruder_id(extruder_id) , mm3_per_mm(mm3_per_mm) , width(width) , height(height) , feedrate(feedrate) + , fan_speed(fan_speed) , cp_color_id(cp_color_id) { } @@ -75,15 +81,18 @@ bool GCodeAnalyzer::Metadata::operator != (const GCodeAnalyzer::Metadata& other) if (feedrate != other.feedrate) return true; + if (fan_speed != other.fan_speed) + return true; + if (cp_color_id != other.cp_color_id) return true; return false; } -GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, unsigned int cp_color_id/* = 0*/) +GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, float fan_speed, unsigned int cp_color_id/* = 0*/) : type(type) - , data(extrusion_role, extruder_id, mm3_per_mm, width, height, feedrate, cp_color_id) + , data(extrusion_role, extruder_id, mm3_per_mm, width, height, feedrate, fan_speed, cp_color_id) , start_position(start_position) , end_position(end_position) , delta_extruder(delta_extruder) @@ -112,6 +121,8 @@ void GCodeAnalyzer::set_extruder_offsets(const GCodeAnalyzer::ExtruderOffsetsMap void GCodeAnalyzer::set_extruders_count(unsigned int count) { m_extruders_count = count; + for (unsigned int i=0; i float + { + float current_absolute_position = _get_axis_position(axis); + float current_origin = _get_axis_origin(axis); + float lengthsScaleFactor = (_get_units() == GCodeAnalyzer::Inches) ? INCHES_TO_MM : 1.0f; + + bool is_relative = (_get_global_positioning_type() == Relative); + if (axis == E) + is_relative |= (_get_e_local_positioning_type() == Relative); + + if (lineG1.has(Slic3r::Axis(axis))) + { + float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor; + return is_relative ? current_absolute_position + ret : ret + current_origin; + } + else + return current_absolute_position; + }; + // updates axes positions from line - EUnits units = _get_units(); float new_pos[Num_Axis]; for (unsigned char a = X; a < Num_Axis; ++a) { - bool is_relative = (_get_global_positioning_type() == Relative); - if (a == E) - is_relative |= (_get_e_local_positioning_type() == Relative); - - new_pos[a] = axis_absolute_position_from_G1_line((EAxis)a, line, units, is_relative, _get_axis_position((EAxis)a)); + new_pos[a] = axis_absolute_position((EAxis)a, line); } // updates feedrate from line, if present @@ -407,25 +437,25 @@ void GCodeAnalyzer::_processG92(const GCodeReader::GCodeLine& line) if (line.has_x()) { - _set_axis_position(X, line.x() * lengthsScaleFactor); + _set_axis_origin(X, _get_axis_position(X) - line.x() * lengthsScaleFactor); anyFound = true; } if (line.has_y()) { - _set_axis_position(Y, line.y() * lengthsScaleFactor); + _set_axis_origin(Y, _get_axis_position(Y) - line.y() * lengthsScaleFactor); anyFound = true; } if (line.has_z()) { - _set_axis_position(Z, line.z() * lengthsScaleFactor); + _set_axis_origin(Z, _get_axis_position(Z) - line.z() * lengthsScaleFactor); anyFound = true; } if (line.has_e()) { - _set_axis_position(E, line.e() * lengthsScaleFactor); + _set_axis_origin(E, _get_axis_position(E) - line.e() * lengthsScaleFactor); anyFound = true; } @@ -433,7 +463,7 @@ void GCodeAnalyzer::_processG92(const GCodeReader::GCodeLine& line) { for (unsigned char a = X; a < Num_Axis; ++a) { - _set_axis_position((EAxis)a, 0.0f); + _set_axis_origin((EAxis)a, _get_axis_position((EAxis)a)); } } } @@ -448,6 +478,24 @@ void GCodeAnalyzer::_processM83(const GCodeReader::GCodeLine& line) _set_e_local_positioning_type(Relative); } +void GCodeAnalyzer::_processM106(const GCodeReader::GCodeLine& line) +{ + if (!line.has('P')) + { + // The absence of P means the print cooling fan, so ignore anything else. + float new_fan_speed; + if (line.has_value('S', new_fan_speed)) + _set_fan_speed((100.0f / 256.0f) * new_fan_speed); + else + _set_fan_speed(100.0f); + } +} + +void GCodeAnalyzer::_processM107(const GCodeReader::GCodeLine& line) +{ + _set_fan_speed(0.0f); +} + void GCodeAnalyzer::_processM108orM135(const GCodeReader::GCodeLine& line) { // These M-codes are used by MakerWare and Sailfish to change active tool. @@ -467,6 +515,25 @@ void GCodeAnalyzer::_processM108orM135(const GCodeReader::GCodeLine& line) } } +void GCodeAnalyzer::_processM132(const GCodeReader::GCodeLine& line) +{ + // This command is used by Makerbot to load the current home position from EEPROM + // see: https://github.com/makerbot/s3g/blob/master/doc/GCodeProtocol.md + // Using this command to reset the axis origin to zero helps in fixing: https://github.com/prusa3d/PrusaSlicer/issues/3082 + + if (line.has_x()) + _set_axis_origin(X, 0.0f); + + if (line.has_y()) + _set_axis_origin(Y, 0.0f); + + if (line.has_z()) + _set_axis_origin(Z, 0.0f); + + if (line.has_e()) + _set_axis_origin(E, 0.0f); +} + void GCodeAnalyzer::_processM401(const GCodeReader::GCodeLine& line) { if (m_gcode_flavor != gcfRepetier) @@ -534,7 +601,11 @@ void GCodeAnalyzer::_processT(const std::string& cmd) BOOST_LOG_TRIVIAL(error) << "GCodeAnalyzer encountered an invalid toolchange, maybe from a custom gcode."; } else + { _set_extruder_id(id); + if (_get_cp_color_id() != INT_MAX) + _set_cp_color_id(m_extruder_color[id]); + } // stores tool change move _store_move(GCodeMove::Tool_change); @@ -587,7 +658,33 @@ bool GCodeAnalyzer::_process_tags(const GCodeReader::GCodeLine& line) pos = comment.find(Color_Change_Tag); if (pos != comment.npos) { - _process_color_change_tag(); + pos = comment.find_last_of(",T"); + int extruder = pos == comment.npos ? 0 : std::atoi(comment.substr(pos + 1, comment.npos).c_str()); + _process_color_change_tag(extruder); + return true; + } + + // color change tag + pos = comment.find(Pause_Print_Tag); + if (pos != comment.npos) + { + _process_pause_print_or_custom_code_tag(); + return true; + } + + // color change tag + pos = comment.find(Custom_Code_Tag); + if (pos != comment.npos) + { + _process_pause_print_or_custom_code_tag(); + return true; + } + + // color change tag + pos = comment.find(End_Pause_Print_Or_Custom_Code_Tag); + if (pos != comment.npos) + { + _process_end_pause_print_or_custom_code_tag(); return true; } @@ -620,10 +717,24 @@ void GCodeAnalyzer::_process_height_tag(const std::string& comment, size_t pos) _set_height((float)::strtod(comment.substr(pos + Height_Tag.length()).c_str(), nullptr)); } -void GCodeAnalyzer::_process_color_change_tag() +void GCodeAnalyzer::_process_color_change_tag(int extruder) { - m_state.cur_cp_color_id++; - _set_cp_color_id(m_state.cur_cp_color_id); + m_extruder_color[extruder] = m_extruders_count + m_state.cp_color_counter; // color_change position in list of color for preview + m_state.cp_color_counter++; + + if (_get_extruder_id() == extruder) + _set_cp_color_id(m_extruder_color[extruder]); +} + +void GCodeAnalyzer::_process_pause_print_or_custom_code_tag() +{ + _set_cp_color_id(INT_MAX); +} + +void GCodeAnalyzer::_process_end_pause_print_or_custom_code_tag() +{ + if (_get_cp_color_id() == INT_MAX) + _set_cp_color_id(m_extruder_color[_get_extruder_id()]); } void GCodeAnalyzer::_set_units(GCodeAnalyzer::EUnits units) @@ -726,6 +837,16 @@ float GCodeAnalyzer::_get_feedrate() const return m_state.data.feedrate; } +void GCodeAnalyzer::_set_fan_speed(float fan_speed_percentage) +{ + m_state.data.fan_speed = fan_speed_percentage; +} + +float GCodeAnalyzer::_get_fan_speed() const +{ + return m_state.data.fan_speed; +} + void GCodeAnalyzer::_set_axis_position(EAxis axis, float position) { m_state.position[axis] = position; @@ -736,11 +857,26 @@ float GCodeAnalyzer::_get_axis_position(EAxis axis) const return m_state.position[axis]; } +void GCodeAnalyzer::_set_axis_origin(EAxis axis, float position) +{ + m_state.origin[axis] = position; +} + +float GCodeAnalyzer::_get_axis_origin(EAxis axis) const +{ + return m_state.origin[axis]; +} + void GCodeAnalyzer::_reset_axes_position() { ::memset((void*)m_state.position, 0, Num_Axis * sizeof(float)); } +void GCodeAnalyzer::_reset_axes_origin() +{ + ::memset((void*)m_state.origin, 0, Num_Axis * sizeof(float)); +} + void GCodeAnalyzer::_set_start_position(const Vec3d& position) { m_state.start_position = position; @@ -798,7 +934,7 @@ void GCodeAnalyzer::_store_move(GCodeAnalyzer::GCodeMove::EType type) Vec3d start_position = _get_start_position() + extruder_offset; Vec3d end_position = _get_end_position() + extruder_offset; - it->second.emplace_back(type, _get_extrusion_role(), extruder_id, _get_mm3_per_mm(), _get_width(), _get_height(), _get_feedrate(), start_position, end_position, _get_delta_extrusion(), _get_cp_color_id()); + it->second.emplace_back(type, _get_extrusion_role(), extruder_id, _get_mm3_per_mm(), _get_width(), _get_height(), _get_feedrate(), start_position, end_position, _get_delta_extrusion(), _get_fan_speed(), _get_cp_color_id()); } bool GCodeAnalyzer::_is_valid_extrusion_role(int value) const @@ -821,7 +957,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ } // if layer not found, create and return it - layers.emplace_back(z, ExtrusionPaths()); + layers.emplace_back(z, GCodePreviewData::Extrusion::Paths()); return layers.back(); } @@ -830,13 +966,18 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ // if the polyline is valid, create the extrusion path from it and store it if (polyline.is_valid()) { - ExtrusionPath path(data.extrusion_role, data.mm3_per_mm, data.width, data.height); + auto& paths = get_layer_at_z(preview_data.extrusion.layers, z).paths; + paths.emplace_back(GCodePreviewData::Extrusion::Path()); + GCodePreviewData::Extrusion::Path &path = paths.back(); path.polyline = polyline; + path.extrusion_role = data.extrusion_role; + path.mm3_per_mm = data.mm3_per_mm; + path.width = data.width; + path.height = data.height; path.feedrate = data.feedrate; path.extruder_id = data.extruder_id; path.cp_color_id = data.cp_color_id; - - get_layer_at_z(preview_data.extrusion.layers, z).paths.push_back(path); + path.fan_speed = data.fan_speed; } } }; @@ -854,6 +995,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ GCodePreviewData::Range width_range; GCodePreviewData::Range feedrate_range; GCodePreviewData::Range volumetric_rate_range; + GCodePreviewData::Range fan_speed_range; // to avoid to call the callback too often unsigned int cancel_callback_threshold = (unsigned int)std::max((int)extrude_moves->second.size() / 25, 1); @@ -888,6 +1030,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ width_range.update_from(move.data.width); feedrate_range.update_from(move.data.feedrate); volumetric_rate_range.update_from(volumetric_rate); + fan_speed_range.update_from(move.data.fan_speed); } else // append end vertex of the move to current polyline @@ -906,6 +1049,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ preview_data.ranges.width.update_from(width_range); preview_data.ranges.feedrate.update_from(feedrate_range); preview_data.ranges.volumetric_rate.update_from(volumetric_rate_range); + preview_data.ranges.fan_speed.update_from(fan_speed_range); // we need to sort the layers by their z as they can be shuffled in case of sequential prints std::sort(preview_data.extrusion.layers.begin(), preview_data.extrusion.layers.end(), [](const GCodePreviewData::Extrusion::Layer& l1, const GCodePreviewData::Extrusion::Layer& l2)->bool { return l1.z < l2.z; }); diff --git a/src/libslic3r/GCode/Analyzer.hpp b/src/libslic3r/GCode/Analyzer.hpp index 61b783f3b..0777623de 100644 --- a/src/libslic3r/GCode/Analyzer.hpp +++ b/src/libslic3r/GCode/Analyzer.hpp @@ -21,6 +21,9 @@ public: static const std::string Width_Tag; static const std::string Height_Tag; static const std::string Color_Change_Tag; + static const std::string Pause_Print_Tag; + static const std::string Custom_Code_Tag; + static const std::string End_Pause_Print_Or_Custom_Code_Tag; static const double Default_mm3_per_mm; static const float Default_Width; @@ -55,10 +58,11 @@ public: float width; // mm float height; // mm float feedrate; // mm/s + float fan_speed; // percentage unsigned int cp_color_id; Metadata(); - Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, unsigned int cp_color_id = 0); + Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, float fan_speed, unsigned int cp_color_id = 0); bool operator != (const Metadata& other) const; }; @@ -82,13 +86,14 @@ public: Vec3d end_position; float delta_extruder; - GCodeMove(EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, unsigned int cp_color_id = 0); + GCodeMove(EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, float fan_speed, unsigned int cp_color_id = 0); GCodeMove(EType type, const Metadata& data, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder); }; typedef std::vector GCodeMovesList; typedef std::map TypeToMovesMap; typedef std::map ExtruderOffsetsMap; + typedef std::map ExtruderToColorMap; private: struct State @@ -101,7 +106,8 @@ private: float cached_position[5]; float start_extrusion; float position[Num_Axis]; - unsigned int cur_cp_color_id = 0; + float origin[Num_Axis]; + unsigned int cp_color_counter = 0; }; private: @@ -112,6 +118,8 @@ private: unsigned int m_extruders_count; GCodeFlavor m_gcode_flavor; + ExtruderToColorMap m_extruder_color; + // The output of process_layer() std::string m_process_output; @@ -172,9 +180,18 @@ private: // Set extruder to relative mode void _processM83(const GCodeReader::GCodeLine& line); + // Set fan speed + void _processM106(const GCodeReader::GCodeLine& line); + + // Disable fan + void _processM107(const GCodeReader::GCodeLine& line); + // Set tool (MakerWare and Sailfish flavor) void _processM108orM135(const GCodeReader::GCodeLine& line); + // Recall stored home offsets + void _processM132(const GCodeReader::GCodeLine& line); + // Repetier: Store x, y and z position void _processM401(const GCodeReader::GCodeLine& line); @@ -202,7 +219,13 @@ private: void _process_height_tag(const std::string& comment, size_t pos); // Processes color change tag - void _process_color_change_tag(); + void _process_color_change_tag(int extruder); + + // Processes pause print and custom gcode tag + void _process_pause_print_or_custom_code_tag(); + + // Processes new layer tag + void _process_end_pause_print_or_custom_code_tag(); void _set_units(EUnits units); EUnits _get_units() const; @@ -234,11 +257,19 @@ private: void _set_feedrate(float feedrate_mm_sec); float _get_feedrate() const; + void _set_fan_speed(float fan_speed_percentage); + float _get_fan_speed() const; + void _set_axis_position(EAxis axis, float position); float _get_axis_position(EAxis axis) const; + void _set_axis_origin(EAxis axis, float position); + float _get_axis_origin(EAxis axis) const; + // Sets axes position to zero void _reset_axes_position(); + // Sets origin position to zero + void _reset_axes_origin(); void _set_start_position(const Vec3d& position); const Vec3d& _get_start_position() const; diff --git a/src/libslic3r/GCode/CoolingBuffer.hpp b/src/libslic3r/GCode/CoolingBuffer.hpp index 511089ad0..b0c35ecc5 100644 --- a/src/libslic3r/GCode/CoolingBuffer.hpp +++ b/src/libslic3r/GCode/CoolingBuffer.hpp @@ -1,7 +1,7 @@ #ifndef slic3r_CoolingBuffer_hpp_ #define slic3r_CoolingBuffer_hpp_ -#include "libslic3r.h" +#include "../libslic3r.h" #include #include diff --git a/src/libslic3r/GCode/PreviewData.cpp b/src/libslic3r/GCode/PreviewData.cpp index 3db8db6af..a6b84c188 100644 --- a/src/libslic3r/GCode/PreviewData.cpp +++ b/src/libslic3r/GCode/PreviewData.cpp @@ -23,7 +23,7 @@ std::vector GCodePreviewData::Color::as_bytes() const return ret; } -GCodePreviewData::Extrusion::Layer::Layer(float z, const ExtrusionPaths& paths) +GCodePreviewData::Extrusion::Layer::Layer(float z, const Paths& paths) : z(z) , paths(paths) { @@ -171,8 +171,8 @@ size_t GCodePreviewData::Extrusion::memory_used() const size_t out = sizeof(*this); out += SLIC3R_STDVEC_MEMSIZE(this->layers, Layer); for (const Layer &layer : this->layers) { - out += SLIC3R_STDVEC_MEMSIZE(layer.paths, ExtrusionPath); - for (const ExtrusionPath &path : layer.paths) + out += SLIC3R_STDVEC_MEMSIZE(layer.paths, Path); + for (const Path &path : layer.paths) out += SLIC3R_STDVEC_MEMSIZE(path.polyline.points, Point); } return out; @@ -241,6 +241,7 @@ void GCodePreviewData::set_default() ::memcpy((void*)ranges.height.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); ::memcpy((void*)ranges.width.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); ::memcpy((void*)ranges.feedrate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); + ::memcpy((void*)ranges.fan_speed.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); ::memcpy((void*)ranges.volumetric_rate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); extrusion.set_default(); @@ -287,6 +288,11 @@ GCodePreviewData::Color GCodePreviewData::get_feedrate_color(float feedrate) con return ranges.feedrate.get_color_at(feedrate); } +GCodePreviewData::Color GCodePreviewData::get_fan_speed_color(float fan_speed) const +{ + return ranges.fan_speed.get_color_at(fan_speed); +} + GCodePreviewData::Color GCodePreviewData::get_volumetric_rate_color(float rate) const { return ranges.volumetric_rate.get_color_at(rate); @@ -358,8 +364,10 @@ std::string GCodePreviewData::get_legend_title() const return L("Width (mm)"); case Extrusion::Feedrate: return L("Speed (mm/s)"); + case Extrusion::FanSpeed: + return L("Fan Speed (%)"); case Extrusion::VolumetricRate: - return L("Volumetric flow rate (mm3/s)"); + return L("Volumetric flow rate (mm³/s)"); case Extrusion::Tool: return L("Tool"); case Extrusion::Filament: @@ -373,7 +381,8 @@ std::string GCodePreviewData::get_legend_title() const return ""; } -GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::vector& tool_colors, const std::vector>& cp_values) const +GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::vector& tool_colors, + const std::vector& cp_items) const { struct Helper { @@ -423,6 +432,11 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std:: Helper::FillListFromRange(items, ranges.feedrate, 1, 1.0f); break; } + case Extrusion::FanSpeed: + { + Helper::FillListFromRange(items, ranges.fan_speed, 0, 1.0f); + break; + } case Extrusion::VolumetricRate: { Helper::FillListFromRange(items, ranges.volumetric_rate, 3, 1.0f); @@ -445,31 +459,25 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std:: case Extrusion::ColorPrint: { const int color_cnt = (int)tool_colors.size()/4; - - const auto color_print_cnt = (int)cp_values.size(); - for (int i = color_print_cnt; i >= 0 ; --i) + const auto color_print_cnt = (int)cp_items.size(); + if (color_print_cnt == 1) // means "Default print color" { - GCodePreviewData::Color color; - ::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + (i % color_cnt) * 4), 4 * sizeof(float)); + Color color; + ::memcpy((void*)color.rgba, (const void*)(tool_colors.data()), 4 * sizeof(float)); + + items.emplace_back(cp_items[0], color); + break; + } + + if (color_cnt != color_print_cnt) + break; + + for (int i = 0 ; i < color_print_cnt; ++i) + { + Color color; + ::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + i * 4), 4 * sizeof(float)); - if (color_print_cnt == 0) { - items.emplace_back(Slic3r::I18N::translate(L("Default print color")), color); - break; - } - - std::string id_str = std::to_string(i + 1) + ": "; - - if (i == 0) { - items.emplace_back(id_str + (boost::format(Slic3r::I18N::translate(L("up to %.2f mm"))) % cp_values[0].first).str(), color); - break; - } - if (i == color_print_cnt) { - items.emplace_back(id_str + (boost::format(Slic3r::I18N::translate(L("above %.2f mm"))) % cp_values[i - 1].second).str(), color); - continue; - } - -// items.emplace_back((boost::format(Slic3r::I18N::translate(L("%.2f - %.2f mm"))) % cp_values[i-1] % cp_values[i]).str(), color); - items.emplace_back(id_str + (boost::format(Slic3r::I18N::translate(L("%.2f - %.2f mm"))) % cp_values[i - 1].second% cp_values[i].first).str(), color); + items.emplace_back(cp_items[i], color); } break; } diff --git a/src/libslic3r/GCode/PreviewData.hpp b/src/libslic3r/GCode/PreviewData.hpp index 5362186fa..6f66ebf5a 100644 --- a/src/libslic3r/GCode/PreviewData.hpp +++ b/src/libslic3r/GCode/PreviewData.hpp @@ -52,6 +52,8 @@ public: Range width; // Color mapping by feedrate. Range feedrate; + // Color mapping by fan speed. + Range fan_speed; // Color mapping by volumetric extrusion rate. Range volumetric_rate; }; @@ -74,6 +76,7 @@ public: Height, Width, Feedrate, + FanSpeed, VolumetricRate, Tool, Filament, @@ -85,12 +88,34 @@ public: static const std::string Default_Extrusion_Role_Names[erCount]; static const EViewType Default_View_Type; + class Path + { + public: + Polyline polyline; + ExtrusionRole extrusion_role; + // Volumetric velocity. mm^3 of plastic per mm of linear head motion. Used by the G-code generator. + float mm3_per_mm; + // Width of the extrusion, used for visualization purposes. + float width; + // Height of the extrusion, used for visualization purposes. + float height; + // Feedrate of the extrusion, used for visualization purposes. + float feedrate; + // Id of the extruder, used for visualization purposes. + uint32_t extruder_id; + // Id of the color, used for visualization purposes in the color printing case. + uint32_t cp_color_id; + // Fan speed for the extrusion, used for visualization purposes. + float fan_speed; + }; + using Paths = std::vector; + struct Layer { float z; - ExtrusionPaths paths; + Paths paths; - Layer(float z, const ExtrusionPaths& paths); + Layer(float z, const Paths& paths); }; typedef std::vector LayersList; @@ -206,13 +231,14 @@ public: Color get_height_color(float height) const; Color get_width_color(float width) const; Color get_feedrate_color(float feedrate) const; + Color get_fan_speed_color(float fan_speed) const; Color get_volumetric_rate_color(float rate) const; void set_extrusion_role_color(const std::string& role_name, float red, float green, float blue, float alpha); void set_extrusion_paths_colors(const std::vector& colors); std::string get_legend_title() const; - LegendItemsList get_legend_items(const std::vector& tool_colors, const std::vector>& cp_values) const; + LegendItemsList get_legend_items(const std::vector& tool_colors, const std::vector& cp_items) const; // Return an estimate of the memory consumed by the time estimator. size_t memory_used() const; diff --git a/src/libslic3r/GCode/PrintExtents.cpp b/src/libslic3r/GCode/PrintExtents.cpp index 07a71a0ea..d44ef1aad 100644 --- a/src/libslic3r/GCode/PrintExtents.cpp +++ b/src/libslic3r/GCode/PrintExtents.cpp @@ -138,7 +138,7 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_ // We need to get position and angle of the wipe tower to transform them to actual position. Transform2d trafo = Eigen::Translation2d(print.config().wipe_tower_x.value, print.config().wipe_tower_y.value) * - Eigen::Rotation2Dd(print.config().wipe_tower_rotation_angle.value); + Eigen::Rotation2Dd(Geometry::deg2rad(print.config().wipe_tower_rotation_angle.value)); BoundingBoxf bbox; for (const std::vector &tool_changes : print.wipe_tower_data().tool_changes) { diff --git a/src/libslic3r/GCode/SpiralVase.hpp b/src/libslic3r/GCode/SpiralVase.hpp index 7872b1d3c..e35ca640c 100644 --- a/src/libslic3r/GCode/SpiralVase.hpp +++ b/src/libslic3r/GCode/SpiralVase.hpp @@ -1,8 +1,8 @@ #ifndef slic3r_SpiralVase_hpp_ #define slic3r_SpiralVase_hpp_ -#include "libslic3r.h" -#include "GCodeReader.hpp" +#include "../libslic3r.h" +#include "../GCodeReader.hpp" namespace Slic3r { diff --git a/src/libslic3r/GCode/ThumbnailData.cpp b/src/libslic3r/GCode/ThumbnailData.cpp new file mode 100644 index 000000000..80165916b --- /dev/null +++ b/src/libslic3r/GCode/ThumbnailData.cpp @@ -0,0 +1,36 @@ +#include "libslic3r/libslic3r.h" +#include "ThumbnailData.hpp" + +#if ENABLE_THUMBNAIL_GENERATOR + +namespace Slic3r { + +void ThumbnailData::set(unsigned int w, unsigned int h) +{ + if ((w == 0) || (h == 0)) + return; + + if ((width != w) || (height != h)) + { + width = w; + height = h; + // defaults to white texture + pixels = std::vector(width * height * 4, 255); + } +} + +void ThumbnailData::reset() +{ + width = 0; + height = 0; + pixels.clear(); +} + +bool ThumbnailData::is_valid() const +{ + return (width != 0) && (height != 0) && ((unsigned int)pixels.size() == 4 * width * height); +} + +} // namespace Slic3r + +#endif // ENABLE_THUMBNAIL_GENERATOR \ No newline at end of file diff --git a/src/libslic3r/GCode/ThumbnailData.hpp b/src/libslic3r/GCode/ThumbnailData.hpp new file mode 100644 index 000000000..4acfe4374 --- /dev/null +++ b/src/libslic3r/GCode/ThumbnailData.hpp @@ -0,0 +1,31 @@ +#ifndef slic3r_ThumbnailData_hpp_ +#define slic3r_ThumbnailData_hpp_ + +#if ENABLE_THUMBNAIL_GENERATOR + +#include +#include "libslic3r/Point.hpp" + +namespace Slic3r { + +struct ThumbnailData +{ + unsigned int width; + unsigned int height; + std::vector pixels; + + ThumbnailData() { reset(); } + void set(unsigned int w, unsigned int h); + void reset(); + + bool is_valid() const; +}; + +typedef std::vector ThumbnailsList; +typedef std::function ThumbnailsGeneratorCallback; + +} // namespace Slic3r + +#endif // ENABLE_THUMBNAIL_GENERATOR + +#endif // slic3r_ThumbnailData_hpp_ \ No newline at end of file diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 7b3e1d037..f8740eac1 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -308,7 +308,7 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_ LayerTools lt_new(0.5f * (lt.print_z + lt_object.print_z)); // Find the 1st layer above lt_new. for (j = i + 1; j < m_layer_tools.size() && m_layer_tools[j].print_z < lt_new.print_z - EPSILON; ++ j); - if (std::abs(m_layer_tools[j].print_z - lt_new.print_z) < EPSILON) { + if (std::abs(m_layer_tools[j].print_z - lt_new.print_z) < EPSILON) { m_layer_tools[j].has_wipe_tower = true; } else { LayerTools <_extra = *m_layer_tools.insert(m_layer_tools.begin() + j, lt_new); diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 3ae99d45c..8bf223660 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -383,15 +383,18 @@ public: // Let the firmware back up the active speed override value. WipeTowerWriter& speed_override_backup() - { - m_gcode += "M220 B\n"; + { + // This is only supported by Prusa at this point (https://github.com/prusa3d/PrusaSlicer/issues/3114) + if (m_gcode_flavor == gcfMarlin) + m_gcode += "M220 B\n"; return *this; } // Let the firmware restore the active speed override value. WipeTowerWriter& speed_override_restore() { - m_gcode += "M220 R\n"; + if (m_gcode_flavor == gcfMarlin) + m_gcode += "M220 R\n"; return *this; } @@ -523,6 +526,7 @@ WipeTower::WipeTower(const PrintConfig& config, const std::vector " + m_filpar[tool].material + "\n").c_str()) .append(";--------------------\n"); - writer.speed_override_backup(); + writer.speed_override_backup(); writer.speed_override(100); Vec2f initial_position = cleaning_box.ld + Vec2f(0.f, m_depth_traversed); @@ -813,7 +817,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool last_in_lay if (m_current_tool < m_used_filament_length.size()) m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); - ToolChangeResult result; + ToolChangeResult result; result.priming = false; result.initial_tool = int(old_tool); result.new_tool = int(m_current_tool); @@ -852,8 +856,10 @@ WipeTower::ToolChangeResult WipeTower::toolchange_Brim(bool sideOnly, float y_of // The tool is supposed to be active and primed at the time when the wipe tower brim is extruded. // Extrude 4 rounds of a brim around the future wipe tower. box_coordinates box(wipeTower_box); + // the brim shall have 'normal' spacing with no extra void space + float spacing = m_brim_width - m_layer_height*float(1.-M_PI_4); for (size_t i = 0; i < 4; ++ i) { - box.expand(m_brim_width - m_layer_height*(1.f-M_PI_4)); // the brim shall have 'normal' spacing with no extra void space + box.expand(spacing); writer.travel (box.ld, 7000) .extrude(box.lu, 2100).extrude(box.ru) .extrude(box.rd ).extrude(box.ld); @@ -865,13 +871,17 @@ WipeTower::ToolChangeResult WipeTower::toolchange_Brim(bool sideOnly, float y_of writer.append("; CP WIPE TOWER FIRST LAYER BRIM END\n" ";-----------------------------------\n"); + // Save actual brim width to be later passed to the Print object, which will use it + // for skirt calculation and pass it to GLCanvas for precise preview box + m_wipe_tower_brim_width = wipeTower_box.ld.x() - box.ld.x() + spacing/2.f; + m_print_brim = false; // Mark the brim as extruded // Ask our writer about how much material was consumed: if (m_current_tool < m_used_filament_length.size()) m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); - ToolChangeResult result; + ToolChangeResult result; result.priming = false; result.initial_tool = int(old_tool); result.new_tool = int(m_current_tool); @@ -1129,9 +1139,10 @@ void WipeTower::toolchange_Change( writer.append("[toolchange_gcode]\n"); // Travel to where we assume we are. Custom toolchange or some special T code handling (parking extruder etc) - // gcode could have left the extruder somewhere, we cannot just start extruding. - Vec2f current_pos = writer.pos_rotated(); - writer.append(std::string("G1 X") + std::to_string(current_pos.x()) + " Y" + std::to_string(current_pos.y()) + "\n"); + // gcode could have left the extruder somewhere, we cannot just start extruding. We should also inform the + // postprocessor that we absolutely want to have this in the gcode, even if it thought it is the same as before. + Vec2f current_pos = writer.pos_rotated(); + writer.append(std::string("G1 X") + std::to_string(current_pos.x()) + " Y" + std::to_string(current_pos.y()) + never_skip_tag() + "\n"); // The toolchange Tn command will be inserted later, only in case that the user does // not provide a custom toolchange gcode. @@ -1271,9 +1282,10 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() writer.set_initial_position((m_left_to_right ? fill_box.ru : fill_box.lu), // so there is never a diagonal travel m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation); + bool toolchanges_on_layer = m_layer_info->toolchanges_depth() > WT_EPSILON; box_coordinates box = fill_box; for (int i=0;i<2;++i) { - if (m_layer_info->toolchanges_depth() < WT_EPSILON) { // there were no toolchanges on this layer + if (! toolchanges_on_layer) { if (i==0) box.expand(m_perimeter_width); else box.expand(-m_perimeter_width); } @@ -1298,7 +1310,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() writer.extrude(box.rd.x() - m_perimeter_width / 2.f, writer.y() + 0.5f * step); writer.extrude(box.ld.x() + m_perimeter_width / 2.f, writer.y()); } - writer.travel(box.rd.x()-m_perimeter_width/2.f,writer.y()); // wipe the nozzle + writer.travel(box.rd.x()-m_perimeter_width/2.f,writer.y()); // wipe the nozzle } else { // Extrude a sparse infill to support the material to be printed above. const float dy = (fill_box.lu.y() - fill_box.ld.y() - m_perimeter_width); @@ -1327,11 +1339,14 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() m_depth_traversed = m_wipe_tower_depth-m_perimeter_width; - // Ask our writer about how much material was consumed: - if (m_current_tool < m_used_filament_length.size()) - m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); - ToolChangeResult result; + // Ask our writer about how much material was consumed. + // Skip this in case the layer is sparse and config option to not print sparse layers is enabled. + if (! m_no_sparse_layers || toolchanges_on_layer) + if (m_current_tool < m_used_filament_length.size()) + m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); + + ToolChangeResult result; result.priming = false; result.initial_tool = int(old_tool); result.new_tool = int(m_current_tool); diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp index 99fe3ac25..6454d2bd9 100644 --- a/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -17,9 +17,12 @@ class PrintConfig; enum GCodeFlavor : unsigned char; + class WipeTower { public: + static char const* never_skip_tag() { return "_GCODE_WIPE_TOWER_NEVER_SKIP_TAG"; } + struct Extrusion { Extrusion(const Vec2f &pos, float width, unsigned int tool) : pos(pos), width(width), tool(tool) {} @@ -91,6 +94,9 @@ public: void generate(std::vector> &result); float get_depth() const { return m_wipe_tower_depth; } + float get_brim_width() const { return m_wipe_tower_brim_width; } + + @@ -215,6 +221,7 @@ private: Vec2f m_wipe_tower_pos; // Left front corner of the wipe tower in mm. float m_wipe_tower_width; // Width of the wipe tower. float m_wipe_tower_depth = 0.f; // Depth of the wipe tower + float m_wipe_tower_brim_width = 0.f; // Width of brim (mm) float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis) float m_internal_rotation = 0.f; float m_y_shift = 0.f; // y shift passed to writer @@ -230,6 +237,7 @@ private: float m_parking_pos_retraction = 0.f; float m_extra_loading_move = 0.f; float m_bridging = 0.f; + bool m_no_sparse_layers = false; bool m_set_extruder_trimpot = false; bool m_adhesion = true; GCodeFlavor m_gcode_flavor; diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp index f64605a9c..24f979267 100644 --- a/src/libslic3r/GCodeReader.hpp +++ b/src/libslic3r/GCodeReader.hpp @@ -29,6 +29,8 @@ public: float value(Axis axis) const { return m_axis[axis]; } bool has(char axis) const; bool has_value(char axis, float &value) const; + float new_X(const GCodeReader &reader) const { return this->has(X) ? this->x() : reader.x(); } + float new_Y(const GCodeReader &reader) const { return this->has(Y) ? this->y() : reader.y(); } float new_Z(const GCodeReader &reader) const { return this->has(Z) ? this->z() : reader.z(); } float new_E(const GCodeReader &reader) const { return this->has(E) ? this->e() : reader.e(); } float new_F(const GCodeReader &reader) const { return this->has(F) ? this->f() : reader.f(); } diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp index 4693ba9e6..c624c0fce 100644 --- a/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -318,12 +318,15 @@ namespace Slic3r { assert((g1_line_id >= (int)data->g1_line_ids.size()) || (data->g1_line_ids[g1_line_id].first >= g1_lines_count)); const Block* block = nullptr; - const G1LineIdToBlockId& map_item = data->g1_line_ids[g1_line_id]; - if ((g1_line_id < (int)data->g1_line_ids.size()) && (map_item.first == g1_lines_count)) + if (g1_line_id < (int)data->g1_line_ids.size()) { - if (line.has_e() && (map_item.second < (unsigned int)data->blocks.size())) - block = &data->blocks[map_item.second]; - ++g1_line_id; + const G1LineIdToBlockId& map_item = data->g1_line_ids[g1_line_id]; + if (map_item.first == g1_lines_count) + { + if (line.has_e() && (map_item.second < (unsigned int)data->blocks.size())) + block = &data->blocks[map_item.second]; + ++g1_line_id; + } } if ((block != nullptr) && (block->elapsed_time != -1.0f)) @@ -412,6 +415,11 @@ namespace Slic3r { m_state.axis[axis].position = position; } + void GCodeTimeEstimator::set_axis_origin(EAxis axis, float position) + { + m_state.axis[axis].origin = position; + } + void GCodeTimeEstimator::set_axis_max_feedrate(EAxis axis, float feedrate_mm_sec) { m_state.axis[axis].max_feedrate = feedrate_mm_sec; @@ -432,6 +440,11 @@ namespace Slic3r { return m_state.axis[axis].position; } + float GCodeTimeEstimator::get_axis_origin(EAxis axis) const + { + return m_state.axis[axis].origin; + } + float GCodeTimeEstimator::get_axis_max_feedrate(EAxis axis) const { return m_state.axis[axis].max_feedrate; @@ -758,6 +771,10 @@ namespace Slic3r { set_axis_position(X, 0.0f); set_axis_position(Y, 0.0f); set_axis_position(Z, 0.0f); + set_axis_origin(X, 0.0f); + set_axis_origin(Y, 0.0f); + set_axis_origin(Z, 0.0f); + if (get_e_local_positioning_type() == Absolute) set_axis_position(E, 0.0f); @@ -954,34 +971,35 @@ namespace Slic3r { } } - // Returns the new absolute position on the given axis in dependence of the given parameters - float axis_absolute_position_from_G1_line(GCodeTimeEstimator::EAxis axis, const GCodeReader::GCodeLine& lineG1, GCodeTimeEstimator::EUnits units, bool is_relative, float current_absolute_position) - { - float lengthsScaleFactor = (units == GCodeTimeEstimator::Inches) ? INCHES_TO_MM : 1.0f; - if (lineG1.has(Slic3r::Axis(axis))) - { - float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor; - return is_relative ? current_absolute_position + ret : ret; - } - else - return current_absolute_position; - } - void GCodeTimeEstimator::_processG1(const GCodeReader::GCodeLine& line) { + auto axis_absolute_position = [this](GCodeTimeEstimator::EAxis axis, const GCodeReader::GCodeLine& lineG1) -> float + { + float current_absolute_position = get_axis_position(axis); + float current_origin = get_axis_origin(axis); + float lengthsScaleFactor = (get_units() == GCodeTimeEstimator::Inches) ? INCHES_TO_MM : 1.0f; + + bool is_relative = (get_global_positioning_type() == Relative); + if (axis == E) + is_relative |= (get_e_local_positioning_type() == Relative); + + if (lineG1.has(Slic3r::Axis(axis))) + { + float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor; + return is_relative ? current_absolute_position + ret : ret + current_origin; + } + else + return current_absolute_position; + }; + PROFILE_FUNC(); increment_g1_line_id(); // updates axes positions from line - EUnits units = get_units(); float new_pos[Num_Axis]; for (unsigned char a = X; a < Num_Axis; ++a) { - bool is_relative = (get_global_positioning_type() == Relative); - if (a == E) - is_relative |= (get_e_local_positioning_type() == Relative); - - new_pos[a] = axis_absolute_position_from_G1_line((EAxis)a, line, units, is_relative, get_axis_position((EAxis)a)); + new_pos[a] = axis_absolute_position((EAxis)a, line); } // updates feedrate from line, if present @@ -1225,25 +1243,25 @@ namespace Slic3r { if (line.has_x()) { - set_axis_position(X, line.x() * lengthsScaleFactor); + set_axis_origin(X, get_axis_position(X) - line.x() * lengthsScaleFactor); anyFound = true; } if (line.has_y()) { - set_axis_position(Y, line.y() * lengthsScaleFactor); + set_axis_origin(Y, get_axis_position(Y) - line.y() * lengthsScaleFactor); anyFound = true; } if (line.has_z()) { - set_axis_position(Z, line.z() * lengthsScaleFactor); + set_axis_origin(Z, get_axis_position(Z) - line.z() * lengthsScaleFactor); anyFound = true; } if (line.has_e()) { - set_axis_position(E, line.e() * lengthsScaleFactor); + set_axis_origin(E, get_axis_position(E) - line.e() * lengthsScaleFactor); anyFound = true; } else @@ -1253,7 +1271,7 @@ namespace Slic3r { { for (unsigned char a = X; a < Num_Axis; ++a) { - set_axis_position((EAxis)a, 0.0f); + set_axis_origin((EAxis)a, get_axis_position((EAxis)a)); } } } diff --git a/src/libslic3r/GCodeTimeEstimator.hpp b/src/libslic3r/GCodeTimeEstimator.hpp index d9f3bc211..0219c87d1 100644 --- a/src/libslic3r/GCodeTimeEstimator.hpp +++ b/src/libslic3r/GCodeTimeEstimator.hpp @@ -55,6 +55,7 @@ namespace Slic3r { struct Axis { float position; // mm + float origin; // mm float max_feedrate; // mm/s float max_acceleration; // mm/s^2 float max_jerk; // mm/s @@ -282,6 +283,8 @@ namespace Slic3r { // Set current position on the given axis with the given value void set_axis_position(EAxis axis, float position); + // Set current origin on the given axis with the given value + void set_axis_origin(EAxis axis, float position); void set_axis_max_feedrate(EAxis axis, float feedrate_mm_sec); void set_axis_max_acceleration(EAxis axis, float acceleration); @@ -289,6 +292,8 @@ namespace Slic3r { // Returns current position on the given axis float get_axis_position(EAxis axis) const; + // Returns current origin on the given axis + float get_axis_origin(EAxis axis) const; float get_axis_max_feedrate(EAxis axis) const; float get_axis_max_acceleration(EAxis axis) const; diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index 63f5964ac..e0b671189 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -269,7 +269,7 @@ std::string GCodeWriter::set_speed(double F, const std::string &comment, const s assert(F > 0.); assert(F < 100000.); std::ostringstream gcode; - gcode << "G1 F" << F; + gcode << "G1 F" << XYZF_NUM(F); COMMENT(comment); gcode << cooling_marker; gcode << "\n"; diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 715ce9c21..bc889ad37 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -3,7 +3,6 @@ #include "ClipperUtils.hpp" #include "ExPolygon.hpp" #include "Line.hpp" -#include "PolylineCollection.hpp" #include "clipper.hpp" #include #include @@ -17,6 +16,10 @@ #include #include +#include +#include +#include + #ifdef SLIC3R_DEBUG #include "SVG.hpp" #endif @@ -311,49 +314,7 @@ convex_hull(const Polygons &polygons) return convex_hull(std::move(pp)); } -/* accepts an arrayref of points and returns a list of indices - according to a nearest-neighbor walk */ -void -chained_path(const Points &points, std::vector &retval, Point start_near) -{ - PointConstPtrs my_points; - std::map indices; - my_points.reserve(points.size()); - for (Points::const_iterator it = points.begin(); it != points.end(); ++it) { - my_points.push_back(&*it); - indices[&*it] = it - points.begin(); - } - - retval.reserve(points.size()); - while (!my_points.empty()) { - Points::size_type idx = start_near.nearest_point_index(my_points); - start_near = *my_points[idx]; - retval.push_back(indices[ my_points[idx] ]); - my_points.erase(my_points.begin() + idx); - } -} - -void -chained_path(const Points &points, std::vector &retval) -{ - if (points.empty()) return; // can't call front() on empty vector - chained_path(points, retval, points.front()); -} - -/* retval and items must be different containers */ -template -void -chained_path_items(Points &points, T &items, T &retval) -{ - std::vector indices; - chained_path(points, indices); - for (std::vector::const_iterator it = indices.begin(); it != indices.end(); ++it) - retval.push_back(items[*it]); -} -template void chained_path_items(Points &points, ClipperLib::PolyNodes &items, ClipperLib::PolyNodes &retval); - -bool -directions_parallel(double angle1, double angle2, double max_diff) +bool directions_parallel(double angle1, double angle2, double max_diff) { double diff = fabs(angle1 - angle2); max_diff += EPSILON; @@ -361,8 +322,7 @@ directions_parallel(double angle1, double angle2, double max_diff) } template -bool -contains(const std::vector &vector, const Point &point) +bool contains(const std::vector &vector, const Point &point) { for (typename std::vector::const_iterator it = vector.begin(); it != vector.end(); ++it) { if (it->contains(point)) return true; @@ -371,16 +331,101 @@ contains(const std::vector &vector, const Point &point) } template bool contains(const ExPolygons &vector, const Point &point); -double -rad2deg_dir(double angle) +double rad2deg_dir(double angle) { angle = (angle < PI) ? (-angle + PI/2.0) : (angle + PI/2.0); if (angle < 0) angle += PI; return rad2deg(angle); } -void -simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval) +Point circle_taubin_newton(const Points::const_iterator& input_begin, const Points::const_iterator& input_end, size_t cycles) +{ + Vec2ds tmp; + tmp.reserve(std::distance(input_begin, input_end)); + std::transform(input_begin, input_end, std::back_inserter(tmp), [] (const Point& in) { return unscale(in); } ); + Vec2d center = circle_taubin_newton(tmp.cbegin(), tmp.end(), cycles); + return Point::new_scale(center.x(), center.y()); +} + +/// Adapted from work in "Circular and Linear Regression: Fitting circles and lines by least squares", pg 126 +/// Returns a point corresponding to the center of a circle for which all of the points from input_begin to input_end +/// lie on. +Vec2d circle_taubin_newton(const Vec2ds::const_iterator& input_begin, const Vec2ds::const_iterator& input_end, size_t cycles) +{ + // calculate the centroid of the data set + const Vec2d sum = std::accumulate(input_begin, input_end, Vec2d(0,0)); + const size_t n = std::distance(input_begin, input_end); + const double n_flt = static_cast(n); + const Vec2d centroid { sum / n_flt }; + + // Compute the normalized moments of the data set. + double Mxx = 0, Myy = 0, Mxy = 0, Mxz = 0, Myz = 0, Mzz = 0; + for (auto it = input_begin; it < input_end; ++it) { + // center/normalize the data. + double Xi {it->x() - centroid.x()}; + double Yi {it->y() - centroid.y()}; + double Zi {Xi*Xi + Yi*Yi}; + Mxy += (Xi*Yi); + Mxx += (Xi*Xi); + Myy += (Yi*Yi); + Mxz += (Xi*Zi); + Myz += (Yi*Zi); + Mzz += (Zi*Zi); + } + + // divide by number of points to get the moments + Mxx /= n_flt; + Myy /= n_flt; + Mxy /= n_flt; + Mxz /= n_flt; + Myz /= n_flt; + Mzz /= n_flt; + + // Compute the coefficients of the characteristic polynomial for the circle + // eq 5.60 + const double Mz {Mxx + Myy}; // xx + yy = z + const double Cov_xy {Mxx*Myy - Mxy*Mxy}; // this shows up a couple times so cache it here. + const double C3 {4.0*Mz}; + const double C2 {-3.0*(Mz*Mz) - Mzz}; + const double C1 {Mz*(Mzz - (Mz*Mz)) + 4.0*Mz*Cov_xy - (Mxz*Mxz) - (Myz*Myz)}; + const double C0 {(Mxz*Mxz)*Myy + (Myz*Myz)*Mxx - 2.0*Mxz*Myz*Mxy - Cov_xy*(Mzz - (Mz*Mz))}; + + const double C22 = {C2 + C2}; + const double C33 = {C3 + C3 + C3}; + + // solve the characteristic polynomial with Newton's method. + double xnew = 0.0; + double ynew = 1e20; + + for (size_t i = 0; i < cycles; ++i) { + const double yold {ynew}; + ynew = C0 + xnew * (C1 + xnew*(C2 + xnew * C3)); + if (std::abs(ynew) > std::abs(yold)) { + BOOST_LOG_TRIVIAL(error) << "Geometry: Fit is going in the wrong direction.\n"; + return Vec2d(std::nan(""), std::nan("")); + } + const double Dy {C1 + xnew*(C22 + xnew*C33)}; + + const double xold {xnew}; + xnew = xold - (ynew / Dy); + + if (std::abs((xnew-xold) / xnew) < 1e-12) i = cycles; // converged, we're done here + + if (xnew < 0) { + // reset, we went negative + xnew = 0.0; + } + } + + // compute the determinant and the circle's parameters now that we've solved. + double DET = xnew*xnew - xnew*Mz + Cov_xy; + + Vec2d center(Mxz * (Myy - xnew) - Myz * Mxy, Myz * (Mxx - xnew) - Mxz*Mxy); + center /= (DET * 2.); + return center + centroid; +} + +void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval) { Polygons pp; for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it) { @@ -393,8 +438,7 @@ simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval) *retval = Slic3r::simplify_polygons(pp); } -double -linint(double value, double oldmin, double oldmax, double newmin, double newmax) +double linint(double value, double oldmin, double oldmax, double newmin, double newmax) { return (value - oldmin) * (newmax - newmin) / (oldmax - oldmin) + newmin; } @@ -620,7 +664,6 @@ namespace Voronoi { namespace Internal { typedef boost::polygon::point_data point_type; typedef boost::polygon::segment_data segment_type; typedef boost::polygon::rectangle_data rect_type; -// typedef voronoi_builder VB; typedef boost::polygon::voronoi_diagram VD; typedef VD::cell_type cell_type; typedef VD::cell_type::source_index_type source_index_type; @@ -667,15 +710,15 @@ namespace Voronoi { namespace Internal { if (cell1.contains_point() && cell2.contains_point()) { point_type p1 = retrieve_point(segments, cell1); point_type p2 = retrieve_point(segments, cell2); - origin.x((p1(0) + p2(0)) * 0.5); - origin.y((p1(1) + p2(1)) * 0.5); - direction.x(p1(1) - p2(1)); - direction.y(p2(0) - p1(0)); + origin.x((p1.x() + p2.x()) * 0.5); + origin.y((p1.y() + p2.y()) * 0.5); + direction.x(p1.y() - p2.y()); + direction.y(p2.x() - p1.x()); } else { origin = cell1.contains_segment() ? retrieve_point(segments, cell2) : retrieve_point(segments, cell1); segment_type segment = cell1.contains_segment() ? segments[cell1.source_index()] : segments[cell2.source_index()]; - coordinate_type dx = high(segment)(0) - low(segment)(0); - coordinate_type dy = high(segment)(1) - low(segment)(1); + coordinate_type dx = high(segment).x() - low(segment).x(); + coordinate_type dy = high(segment).y() - low(segment).y(); if ((low(segment) == origin) ^ cell1.contains_point()) { direction.x(dy); direction.y(-dx); @@ -684,19 +727,19 @@ namespace Voronoi { namespace Internal { direction.y(dx); } } - coordinate_type koef = bbox_max_size / (std::max)(fabs(direction(0)), fabs(direction(1))); + coordinate_type koef = bbox_max_size / (std::max)(fabs(direction.x()), fabs(direction.y())); if (edge.vertex0() == NULL) { clipped_edge->push_back(point_type( - origin(0) - direction(0) * koef, - origin(1) - direction(1) * koef)); + origin.x() - direction.x() * koef, + origin.y() - direction.y() * koef)); } else { clipped_edge->push_back( point_type(edge.vertex0()->x(), edge.vertex0()->y())); } if (edge.vertex1() == NULL) { clipped_edge->push_back(point_type( - origin(0) + direction(0) * koef, - origin(1) + direction(1) * koef)); + origin.x() + direction.x() * koef, + origin.y() + direction.y() * koef)); } else { clipped_edge->push_back( point_type(edge.vertex1()->x(), edge.vertex1()->y())); @@ -716,7 +759,7 @@ namespace Voronoi { namespace Internal { } /* namespace Internal */ } // namespace Voronoi -static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_diagram &vd, const ThickPolylines *polylines, const char *path) +static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ boost::polygon::voronoi_diagram &vd, const ThickPolylines *polylines, const char *path) { const double scale = 0.2; const std::string inputSegmentPointColor = "lightseagreen"; @@ -760,7 +803,7 @@ static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_d Voronoi::Internal::point_type(double(it->b(0)), double(it->b(1))))); // Color exterior edges. - for (voronoi_diagram::const_edge_iterator it = vd.edges().begin(); it != vd.edges().end(); ++it) + for (boost::polygon::voronoi_diagram::const_edge_iterator it = vd.edges().begin(); it != vd.edges().end(); ++it) if (!it->is_finite()) Voronoi::Internal::color_exterior(&(*it)); @@ -775,11 +818,11 @@ static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_d #if 1 // Draw voronoi vertices. - for (voronoi_diagram::const_vertex_iterator it = vd.vertices().begin(); it != vd.vertices().end(); ++it) + for (boost::polygon::voronoi_diagram::const_vertex_iterator it = vd.vertices().begin(); it != vd.vertices().end(); ++it) if (! internalEdgesOnly || it->color() != Voronoi::Internal::EXTERNAL_COLOR) - svg.draw(Point(coord_t((*it)(0)), coord_t((*it)(1))), voronoiPointColor, voronoiPointRadius); + svg.draw(Point(coord_t(it->x()), coord_t(it->y())), voronoiPointColor, voronoiPointRadius); - for (voronoi_diagram::const_edge_iterator it = vd.edges().begin(); it != vd.edges().end(); ++it) { + for (boost::polygon::voronoi_diagram::const_edge_iterator it = vd.edges().begin(); it != vd.edges().end(); ++it) { if (primaryEdgesOnly && !it->is_primary()) continue; if (internalEdgesOnly && (it->color() == Voronoi::Internal::EXTERNAL_COLOR)) @@ -802,7 +845,7 @@ static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_d color = voronoiLineColorSecondary; } for (std::size_t i = 0; i + 1 < samples.size(); ++i) - svg.draw(Line(Point(coord_t(samples[i](0)), coord_t(samples[i](1))), Point(coord_t(samples[i+1](0)), coord_t(samples[i+1](1)))), color, voronoiLineWidth); + svg.draw(Line(Point(coord_t(samples[i].x()), coord_t(samples[i].y())), Point(coord_t(samples[i+1].x()), coord_t(samples[i+1].y()))), color, voronoiLineWidth); } #endif @@ -1111,6 +1154,32 @@ void Transformation::set_from_transform(const Transform3d& transform) // std::cout << "something went wrong in extracting data from matrix" << std::endl; } +void Transformation::set_from_string(const std::string& transform_str) +{ + Transform3d transform = Transform3d::Identity(); + + if (!transform_str.empty()) + { + std::vector mat_elements_str; + boost::split(mat_elements_str, transform_str, boost::is_any_of(" "), boost::token_compress_on); + + unsigned int size = (unsigned int)mat_elements_str.size(); + if (size == 16) + { + unsigned int i = 0; + for (unsigned int r = 0; r < 4; ++r) + { + for (unsigned int c = 0; c < 4; ++c) + { + transform(r, c) = ::atof(mat_elements_str[i++].c_str()); + } + } + } + } + + set_from_transform(transform); +} + void Transformation::reset() { m_offset = Vec3d::Zero(); diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index 277be5b32..961366d8c 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -12,8 +12,11 @@ #include #include "boost/polygon/voronoi.hpp" -using boost::polygon::voronoi_builder; -using boost::polygon::voronoi_diagram; + +namespace ClipperLib { +class PolyNode; +using PolyNodes = std::vector; +} namespace Slic3r { namespace Geometry { @@ -145,13 +148,83 @@ inline bool segments_intersect( segments_could_intersect(jp1, jp2, ip1, ip2) <= 0; } +// Based on Liang-Barsky function by Daniel White @ http://www.skytopia.com/project/articles/compsci/clipping.html +template +bool liang_barsky_line_clipping( + // Start and end points of the source line, result will be stored there as well. + Eigen::Matrix &x0, + Eigen::Matrix &x1, + // Bounding box to clip with. + const BoundingBoxBase> &bbox) +{ + Eigen::Matrix v = x1 - x0; + double t0 = 0.0; + double t1 = 1.0; + + // Traverse through left, right, bottom, top edges. + for (int edge = 0; edge < 4; ++ edge) + { + double p, q; + switch (edge) { + case 0: p = - v.x(); q = - bbox.min.x() + x0.x(); break; + case 1: p = v.x(); q = bbox.max.x() - x0.x(); break; + case 2: p = - v.y(); q = - bbox.min.y() + x0.y(); break; + default: p = v.y(); q = bbox.max.y() - x0.y(); break; + } + + if (p == 0) { + if (q < 0) + // Line parallel to the bounding box edge is fully outside of the bounding box. + return false; + // else don't clip + } else { + double r = q / p; + if (p < 0) { + if (r > t1) + // Fully clipped. + return false; + if (r > t0) + // Partially clipped. + t0 = r; + } else { + assert(p > 0); + if (r < t0) + // Fully clipped. + return false; + if (r < t1) + // Partially clipped. + t1 = r; + } + } + } + + // Clipped successfully. + x1 = x0 + t1 * v; + x0 += t0 * v; + return true; +} + +// Based on Liang-Barsky function by Daniel White @ http://www.skytopia.com/project/articles/compsci/clipping.html +template +bool liang_barsky_line_clipping( + // Start and end points of the source line. + const Eigen::Matrix &x0src, + const Eigen::Matrix &x1src, + // Bounding box to clip with. + const BoundingBoxBase> &bbox, + // Start and end points of the clipped line. + Eigen::Matrix &x0clip, + Eigen::Matrix &x1clip) +{ + x0clip = x0src; + x1clip = x1src; + return liang_barsky_line_clipping(x0clip, x1clip, bbox); +} + Pointf3s convex_hull(Pointf3s points); Polygon convex_hull(Points points); Polygon convex_hull(const Polygons &polygons); -void chained_path(const Points &points, std::vector &retval, Point start_near); -void chained_path(const Points &points, std::vector &retval); -template void chained_path_items(Points &points, T &items, T &retval); bool directions_parallel(double angle1, double angle2, double max_diff = 0); template bool contains(const std::vector &vector, const Point &point); template T rad2deg(T angle) { return T(180.0) * angle / T(PI); } @@ -171,6 +244,7 @@ template T angle_to_0_2PI(T angle) return angle; } + void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval); double linint(double value, double oldmin, double oldmax, double newmin, double newmax); @@ -261,6 +335,7 @@ public: void set_mirror(Axis axis, double mirror); void set_from_transform(const Transform3d& transform); + void set_from_string(const std::string& transform_str); void reset(); diff --git a/src/libslic3r/KDTreeIndirect.hpp b/src/libslic3r/KDTreeIndirect.hpp new file mode 100644 index 000000000..239008559 --- /dev/null +++ b/src/libslic3r/KDTreeIndirect.hpp @@ -0,0 +1,235 @@ +// KD tree built upon external data set, referencing the external data by integer indices. + +#ifndef slic3r_KDTreeIndirect_hpp_ +#define slic3r_KDTreeIndirect_hpp_ + +#include +#include +#include + +#include "Utils.hpp" // for next_highest_power_of_2() + +namespace Slic3r { + +// KD tree for N-dimensional closest point search. +template +class KDTreeIndirect +{ +public: + static constexpr size_t NumDimensions = ANumDimensions; + using CoordinateFn = ACoordinateFn; + using CoordType = ACoordType; + // Following could be static constexpr size_t, but that would not link in C++11 + enum : size_t { + npos = size_t(-1) + }; + + KDTreeIndirect(CoordinateFn coordinate) : coordinate(coordinate) {} + KDTreeIndirect(CoordinateFn coordinate, std::vector indices) : coordinate(coordinate) { this->build(std::move(indices)); } + KDTreeIndirect(CoordinateFn coordinate, std::vector &&indices) : coordinate(coordinate) { this->build(std::move(indices)); } + KDTreeIndirect(CoordinateFn coordinate, size_t num_indices) : coordinate(coordinate) { this->build(num_indices); } + KDTreeIndirect(KDTreeIndirect &&rhs) : m_nodes(std::move(rhs.m_nodes)), coordinate(std::move(rhs.coordinate)) {} + KDTreeIndirect& operator=(KDTreeIndirect &&rhs) { m_nodes = std::move(rhs.m_nodes); coordinate = std::move(rhs.coordinate); return *this; } + void clear() { m_nodes.clear(); } + + void build(size_t num_indices) + { + std::vector indices; + indices.reserve(num_indices); + for (size_t i = 0; i < num_indices; ++ i) + indices.emplace_back(i); + this->build(std::move(indices)); + } + + void build(std::vector &&indices) + { + if (indices.empty()) + clear(); + else { + // Allocate enough memory for a full binary tree. + m_nodes.assign(next_highest_power_of_2(indices.size() + 1), npos); + build_recursive(indices, 0, 0, 0, indices.size() - 1); + } + indices.clear(); + } + + enum class VisitorReturnMask : unsigned int + { + CONTINUE_LEFT = 1, + CONTINUE_RIGHT = 2, + STOP = 4, + }; + template + unsigned int descent_mask(const CoordType &point_coord, const CoordType &search_radius, size_t idx, size_t dimension) const + { + CoordType dist = point_coord - this->coordinate(idx, dimension); + return (dist * dist < search_radius + CoordType(EPSILON)) ? + // The plane intersects a hypersphere centered at point_coord of search_radius. + ((unsigned int)(VisitorReturnMask::CONTINUE_LEFT) | (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT)) : + // The plane does not intersect the hypersphere. + (dist > CoordType(0)) ? (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT) : (unsigned int)(VisitorReturnMask::CONTINUE_LEFT); + } + + // Visitor is supposed to return a bit mask of VisitorReturnMask. + template + void visit(Visitor &visitor) const + { + visit_recursive(0, 0, visitor); + } + + CoordinateFn coordinate; + +private: + // Build a balanced tree by splitting the input sequence by an axis aligned plane at a dimension. + void build_recursive(std::vector &input, size_t node, const size_t dimension, const size_t left, const size_t right) + { + if (left > right) + return; + + assert(node < m_nodes.size()); + + if (left == right) { + // Insert a node into the balanced tree. + m_nodes[node] = input[left]; + return; + } + + // Partition the input to left / right pieces of the same length to produce a balanced tree. + size_t center = (left + right) / 2; + partition_input(input, dimension, left, right, center); + // Insert a node into the tree. + m_nodes[node] = input[center]; + // Build up the left / right subtrees. + size_t next_dimension = dimension; + if (++ next_dimension == NumDimensions) + next_dimension = 0; + if (center > left) + build_recursive(input, node * 2 + 1, next_dimension, left, center - 1); + build_recursive(input, node * 2 + 2, next_dimension, center + 1, right); + } + + // Partition the input m_nodes at "k" and "dimension" using the QuickSelect method: + // https://en.wikipedia.org/wiki/Quickselect + // Items left of the k'th item are lower than the k'th item in the "dimension", + // items right of the k'th item are higher than the k'th item in the "dimension", + void partition_input(std::vector &input, const size_t dimension, size_t left, size_t right, const size_t k) const + { + while (left < right) { + size_t center = (left + right) / 2; + CoordType pivot; + { + // Bubble sort the input[left], input[center], input[right], so that a median of the three values + // will end up in input[center]. + CoordType left_value = this->coordinate(input[left], dimension); + CoordType center_value = this->coordinate(input[center], dimension); + CoordType right_value = this->coordinate(input[right], dimension); + if (left_value > center_value) { + std::swap(input[left], input[center]); + std::swap(left_value, center_value); + } + if (left_value > right_value) { + std::swap(input[left], input[right]); + right_value = left_value; + } + if (center_value > right_value) { + std::swap(input[center], input[right]); + center_value = right_value; + } + pivot = center_value; + } + if (right <= left + 2) + // The interval is already sorted. + break; + size_t i = left; + size_t j = right - 1; + std::swap(input[center], input[j]); + // Partition the set based on the pivot. + for (;;) { + // Skip left points that are already at correct positions. + // Search will certainly stop at position (right - 1), which stores the pivot. + while (this->coordinate(input[++ i], dimension) < pivot) ; + // Skip right points that are already at correct positions. + while (this->coordinate(input[-- j], dimension) > pivot && i < j) ; + if (i >= j) + break; + std::swap(input[i], input[j]); + } + // Restore pivot to the center of the sequence. + std::swap(input[i], input[right - 1]); + // Which side the kth element is in? + if (k < i) + right = i - 1; + else if (k == i) + // Sequence is partitioned, kth element is at its place. + break; + else + left = i + 1; + } + } + + template + void visit_recursive(size_t node, size_t dimension, Visitor &visitor) const + { + assert(! m_nodes.empty()); + if (node >= m_nodes.size() || m_nodes[node] == npos) + return; + + // Left / right child node index. + size_t left = node * 2 + 1; + size_t right = left + 1; + unsigned int mask = visitor(m_nodes[node], dimension); + if ((mask & (unsigned int)VisitorReturnMask::STOP) == 0) { + size_t next_dimension = (++ dimension == NumDimensions) ? 0 : dimension; + if (mask & (unsigned int)VisitorReturnMask::CONTINUE_LEFT) + visit_recursive(left, next_dimension, visitor); + if (mask & (unsigned int)VisitorReturnMask::CONTINUE_RIGHT) + visit_recursive(right, next_dimension, visitor); + } + } + + std::vector m_nodes; +}; + +// Find a closest point using Euclidian metrics. +// Returns npos if not found. +template +size_t find_closest_point(const KDTreeIndirectType &kdtree, const PointType &point, FilterFn filter) +{ + struct Visitor { + using CoordType = typename KDTreeIndirectType::CoordType; + const KDTreeIndirectType &kdtree; + const PointType &point; + const FilterFn filter; + size_t min_idx = KDTreeIndirectType::npos; + CoordType min_dist = std::numeric_limits::max(); + + Visitor(const KDTreeIndirectType &kdtree, const PointType &point, FilterFn filter) : kdtree(kdtree), point(point), filter(filter) {} + unsigned int operator()(size_t idx, size_t dimension) { + if (this->filter(idx)) { + auto dist = CoordType(0); + for (size_t i = 0; i < KDTreeIndirectType::NumDimensions; ++ i) { + CoordType d = point[i] - kdtree.coordinate(idx, i); + dist += d * d; + } + if (dist < min_dist) { + min_dist = dist; + min_idx = idx; + } + } + return kdtree.descent_mask(point[dimension], min_dist, idx, dimension); + } + } visitor(kdtree, point, filter); + + kdtree.visit(visitor); + return visitor.min_idx; +} + +template +size_t find_closest_point(const KDTreeIndirectType& kdtree, const PointType& point) +{ + return find_closest_point(kdtree, point, [](size_t) { return true; }); +} + +} // namespace Slic3r + +#endif /* slic3r_KDTreeIndirect_hpp_ */ diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index 6ea59ddf2..f604007ec 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -1,8 +1,8 @@ #include "Layer.hpp" #include "ClipperUtils.hpp" -#include "Geometry.hpp" #include "Print.hpp" #include "Fill/Fill.hpp" +#include "ShortestPath.hpp" #include "SVG.hpp" #include @@ -47,8 +47,8 @@ void Layer::make_slices() slices = union_ex(slices_p); } - this->slices.expolygons.clear(); - this->slices.expolygons.reserve(slices.size()); + this->slices.clear(); + this->slices.reserve(slices.size()); // prepare ordering points Points ordering_points; @@ -57,12 +57,11 @@ void Layer::make_slices() ordering_points.push_back(ex.contour.first_point()); // sort slices - std::vector order; - Slic3r::Geometry::chained_path(ordering_points, order); + std::vector order = chain_points(ordering_points); // populate slices vector for (size_t i : order) - this->slices.expolygons.push_back(std::move(slices[i])); + this->slices.push_back(std::move(slices[i])); } // Merge typed slices into untyped slices. This method is used to revert the effects of detect_surfaces_type() called for posPrepareInfill. @@ -71,7 +70,7 @@ void Layer::merge_slices() if (m_regions.size() == 1) { // Optimization, also more robust. Don't merge classified pieces of layerm->slices, // but use the non-split islands of a layer. For a single region print, these shall be equal. - m_regions.front()->m_slices.set(this->slices.expolygons, stPosInternal | stDensSparse); + m_regions.front()->m_slices.set(this->slices, stPosInternal | stDensSparse); } else { for (LayerRegion *layerm : m_regions) // without safety offset, artifacts are generated (GH #2494) @@ -81,19 +80,23 @@ void Layer::merge_slices() ExPolygons Layer::merged(float offset_scaled) const { - assert(offset_scaled >= 0.f); + assert(offset_scaled >= 0.f); // If no offset is set, apply EPSILON offset before union, and revert it afterwards. - float offset_scaled2 = 0; - if (offset_scaled == 0.f) { - offset_scaled = float( EPSILON); - offset_scaled2 = float(- EPSILON); + float offset_scaled2 = 0; + if (offset_scaled == 0.f) { + offset_scaled = float( EPSILON); + offset_scaled2 = float(- EPSILON); } Polygons polygons; - for (LayerRegion *layerm : m_regions) - append(polygons, offset(to_expolygons(layerm->slices().surfaces), offset_scaled)); + for (LayerRegion *layerm : m_regions) { + const PrintRegionConfig &config = layerm->region()->config(); + // Our users learned to bend Slic3r to produce empty volumes to act as subtracters. Only add the region if it is non-empty. + if (config.bottom_solid_layers > 0 || config.top_solid_layers > 0 || config.fill_density > 0. || config.perimeters > 0) + append(polygons, offset(to_expolygons(layerm->slices().surfaces), offset_scaled)); + } ExPolygons out = union_ex(polygons); - if (offset_scaled2 != 0.f) - out = offset_ex(out, offset_scaled2); + if (offset_scaled2 != 0.f) + out = offset_ex(out, offset_scaled2); return out; } diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index c6091d3bd..a4720c241 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -6,8 +6,6 @@ #include "SurfaceCollection.hpp" #include "ExtrusionEntityCollection.hpp" #include "ExPolygonCollection.hpp" -#include "PolylineCollection.hpp" - namespace Slic3r { @@ -51,7 +49,7 @@ public: Polygons bridged; // collection of polylines representing the unsupported bridge edges - PolylineCollection unsupported_bridge_edges; + Polylines unsupported_bridge_edges; // ordered collection of extrusion paths/loops to build all perimeters // (this collection contains only ExtrusionEntityCollection objects) @@ -115,7 +113,8 @@ public: // also known as 'islands' (all regions and surface types are merged here) // The slices are chained by the shortest traverse distance and this traversal // order will be recovered by the G-code generator. - ExPolygonCollection slices; + ExPolygons slices; + std::vector slices_bboxes; size_t region_count() const { return m_regions.size(); } const LayerRegion* get_region(size_t idx) const { return m_regions.at(idx); } diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 6e453ba59..b4cb37f39 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -73,7 +73,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec fill_surfaces ); - if (this->layer()->lower_layer != NULL) + if (this->layer()->lower_layer != nullptr) // Cummulative sum of polygons over all the regions. g.lower_slices = &this->layer()->lower_layer->slices; if (this->layer()->upper_layer != NULL) @@ -147,7 +147,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly if (! surface.empty()) bridges.emplace_back(surface); } - if (has_infill || !(surface.has_pos_internal() && surface.has_fill_sparse())) { + if (has_infill || !(surface.has_pos_internal())) { if (!surface.has_pos_external()) // Make a copy as the following line uses the move semantics. internal.push_back(surface); @@ -169,7 +169,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly // Remove voids from fill_boundaries, that are not supported by the layer below. if (lower_layer_covered == nullptr) { lower_layer_covered = &lower_layer_covered_tmp; - lower_layer_covered_tmp = to_polygons(lower_layer->slices.expolygons); + lower_layer_covered_tmp = to_polygons(lower_layer->slices); } if (! lower_layer_covered->empty()) voids = diff(voids, *lower_layer_covered); @@ -305,7 +305,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly bridges[idx_last].bridge_angle = bd.angle; if (this->layer()->object()->config().support_material) { polygons_append(this->bridged, intersection(bd.coverage(), to_polygons(initial))); - this->unsupported_bridge_edges.append(bd.unsupported_edges()); + append(this->unsupported_bridge_edges, bd.unsupported_edges()); } } else if (custom_angle > 0) { // Bridge was not detected (likely it is only supported at one side). Still it is a surface filled in @@ -509,4 +509,4 @@ void LayerRegion::export_region_fill_surfaces_to_svg_debug(const char *name) con } } - \ No newline at end of file + diff --git a/src/libslic3r/Line.cpp b/src/libslic3r/Line.cpp index d6b1352bf..4e4ba1566 100644 --- a/src/libslic3r/Line.cpp +++ b/src/libslic3r/Line.cpp @@ -86,10 +86,7 @@ bool Line::intersection(const Line &l2, Point *intersection) const const Line &l1 = *this; const Vec2d v1 = (l1.b - l1.a).cast(); const Vec2d v2 = (l2.b - l2.a).cast(); - const Vec2d v12 = (l1.a - l2.a).cast(); double denom = cross2(v1, v2); - double nume_a = cross2(v2, v12); - double nume_b = cross2(v1, v12); if (fabs(denom) < EPSILON) #if 0 // Lines are collinear. Return true if they are coincident (overlappign). @@ -97,6 +94,9 @@ bool Line::intersection(const Line &l2, Point *intersection) const #else return false; #endif + const Vec2d v12 = (l1.a - l2.a).cast(); + double nume_a = cross2(v2, v12); + double nume_b = cross2(v1, v12); double t1 = nume_a / denom; double t2 = nume_b / denom; if (t1 >= 0 && t1 <= 1.0f && t2 >= 0 && t2 <= 1.0f) { @@ -107,6 +107,17 @@ bool Line::intersection(const Line &l2, Point *intersection) const return false; // not intersecting } +bool Line::clip_with_bbox(const BoundingBox &bbox) +{ + Vec2d x0clip, x1clip; + bool result = Geometry::liang_barsky_line_clipping(this->a.cast(), this->b.cast(), BoundingBoxf(bbox.min.cast(), bbox.max.cast()), x0clip, x1clip); + if (result) { + this->a = x0clip.cast(); + this->b = x1clip.cast(); + } + return result; +} + Vec3d Linef3::intersect_plane(double z) const { auto v = (this->b - this->a).cast(); diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp index 4e95df478..1f0802837 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -6,6 +6,7 @@ namespace Slic3r { +class BoundingBox; class Line; class Line3; class Linef3; @@ -43,6 +44,8 @@ public: Vector normal() const { return Vector((this->b(1) - this->a(1)), -(this->b(0) - this->a(0))); } bool intersection(const Line& line, Point* intersection) const; double ccw(const Point& point) const { return point.ccw(*this); } + // Clip a line with a bounding box. Returns false if the line is completely outside of the bounding box. + bool clip_with_bbox(const BoundingBox &bbox); static double distance_to_squared(const Point &point, const Point &a, const Point &b); static double distance_to(const Point &point, const Point &a, const Point &b) { return sqrt(distance_to_squared(point, a, b)); } diff --git a/src/libslic3r/MTUtils.hpp b/src/libslic3r/MTUtils.hpp index 212752883..63ff6fb09 100644 --- a/src/libslic3r/MTUtils.hpp +++ b/src/libslic3r/MTUtils.hpp @@ -252,22 +252,15 @@ template struct remove_cvref template using remove_cvref_t = typename remove_cvref::type; -template class C, class T> -class Container : public C> -{ -public: - explicit Container(size_t count, T &&initval) - : C>(count, initval) - {} -}; - template using DefaultContainer = std::vector; /// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html -template class C = DefaultContainer> -inline C> linspace(const T &start, const T &stop, const I &n) +template class Container = DefaultContainer> +inline Container> linspace(const T &start, + const T &stop, + const I &n) { - Container vals(n, T()); + Container> vals(n, T()); T stride = (stop - start) / n; size_t i = 0; @@ -282,10 +275,13 @@ inline C> linspace(const T &start, const T &stop, const I &n) /// 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 C = DefaultContainer> -inline C> grid(const T &start, const T &stop, const T &stride) +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()); + Container> + vals(size_t(std::ceil((stop - start) / stride)), T()); int i = 0; std::generate(vals.begin(), vals.end(), [&i, start, stride] { @@ -387,10 +383,12 @@ unscaled(const Eigen::Matrix &v) noexcept return v.template cast() * SCALING_FACTOR; } -template inline std::vector reserve_vector(size_t capacity) +template // Arbitrary allocator can be used +inline IntegerOnly> reserve_vector(I capacity) { - std::vector ret; - ret.reserve(capacity); + std::vector ret; + if (capacity > I(0)) ret.reserve(size_t(capacity)); + return ret; } diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index f3050eeb5..ec23c7716 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -41,6 +41,9 @@ Model& Model::assign_copy(const Model &rhs) mo->set_model(this); this->objects.emplace_back(mo); } + + // copy custom code per height + this->custom_gcode_per_height = rhs.custom_gcode_per_height; return *this; } @@ -59,6 +62,9 @@ Model& Model::assign_copy(Model &&rhs) for (ModelObject *model_object : this->objects) model_object->set_model(this); rhs.objects.clear(); + + // copy custom code per height + this->custom_gcode_per_height = rhs.custom_gcode_per_height; return *this; } @@ -141,12 +147,12 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig for (ModelObject *o : model.objects) { - if (boost::algorithm::iends_with(input_file, ".zip.amf")) - { - // we remove the .zip part of the extension to avoid it be added to filenames when exporting - o->input_file = boost::ireplace_last_copy(input_file, ".zip.", "."); - } - else +// if (boost::algorithm::iends_with(input_file, ".zip.amf")) +// { +// // we remove the .zip part of the extension to avoid it be added to filenames when exporting +// o->input_file = boost::ireplace_last_copy(input_file, ".zip.", "."); +// } +// else o->input_file = input_file; } @@ -170,6 +176,9 @@ ModelObject* Model::add_object(const char *name, const char *path, const Triangl new_object->input_file = path; ModelVolume *new_volume = new_object->add_volume(mesh); new_volume->name = name; + new_volume->source.input_file = path; + new_volume->source.object_idx = (int)this->objects.size() - 1; + new_volume->source.volume_idx = (int)new_object->volumes.size() - 1; new_object->invalidate_bounding_box(); return new_object; } @@ -182,6 +191,9 @@ ModelObject* Model::add_object(const char *name, const char *path, TriangleMesh new_object->input_file = path; ModelVolume *new_volume = new_object->add_volume(std::move(mesh)); new_volume->name = name; + new_volume->source.input_file = path; + new_volume->source.object_idx = (int)this->objects.size() - 1; + new_volume->source.volume_idx = (int)new_object->volumes.size() - 1; new_object->invalidate_bounding_box(); return new_object; } @@ -580,6 +592,22 @@ std::string Model::propose_export_file_name_and_path(const std::string &new_exte return boost::filesystem::path(this->propose_export_file_name_and_path()).replace_extension(new_extension).string(); } +std::vector> Model::get_custom_tool_changes(double default_layer_height, size_t num_extruders) const +{ + std::vector> custom_tool_changes; + if (!custom_gcode_per_height.empty()) { + for (const CustomGCode& custom_gcode : custom_gcode_per_height) + if (custom_gcode.gcode == ExtruderChangeCode) { + DynamicPrintConfig config; + // If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders + config.set_key_value("extruder", new ConfigOptionInt(custom_gcode.extruder > num_extruders ? 0 : custom_gcode.extruder)); + // For correct extruders(tools) changing, we should decrease custom_gcode.height value by one default layer height + custom_tool_changes.push_back({ custom_gcode.height - default_layer_height, config }); + } + } + return custom_tool_changes; +} + ModelObject::~ModelObject() { this->clear_volumes(); @@ -1462,7 +1490,7 @@ stl_stats ModelObject::get_object_stl_stats() const return this->volumes[0]->mesh().stl.stats; stl_stats full_stats; - memset(&full_stats, 0, sizeof(stl_stats)); + full_stats.volume = 0.f; // fill full_stats from all objet's meshes for (ModelVolume* volume : this->volumes) @@ -1543,7 +1571,7 @@ bool ModelVolume::is_splittable() const return m_is_splittable == 1; } -void ModelVolume::center_geometry_after_creation() +void ModelVolume::center_geometry_after_creation(bool update_source_offset) { Vec3d shift = this->mesh().bounding_box().center(); if (!shift.isApprox(Vec3d::Zero())) @@ -1554,6 +1582,9 @@ void ModelVolume::center_geometry_after_creation() const_cast(m_convex_hull.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2)); translate(shift); } + + if (update_source_offset) + source.mesh_offset = shift; } void ModelVolume::calculate_convex_hull() diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index b3e0d3106..741d88472 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -392,6 +392,18 @@ class ModelVolume final : public ObjectBase { public: std::string name; + // struct used by reload from disk command to recover data from disk + struct Source + { + std::string input_file; + int object_idx{ -1 }; + int volume_idx{ -1 }; + Vec3d mesh_offset{ Vec3d::Zero() }; + + template void serialize(Archive& ar) { ar(input_file, object_idx, volume_idx, mesh_offset); } + }; + Source source; + // The triangular model. const TriangleMesh& mesh() const { return *m_mesh.get(); } void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared(mesh); } @@ -440,7 +452,7 @@ public: // Translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box. // Attention! This method may only be called just after ModelVolume creation! It must not be called once the TriangleMesh of this ModelVolume is shared! - void center_geometry_after_creation(); + void center_geometry_after_creation(bool update_source_offset = true); void calculate_convex_hull(); const TriangleMesh& get_convex_hull() const; @@ -529,7 +541,7 @@ private: // Copying an existing volume, therefore this volume will get a copy of the ID assigned. ModelVolume(ModelObject *object, const ModelVolume &other) : ObjectBase(other), - name(other.name), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) + name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) { assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); assert(this->id() == other.id() && this->config.id() == other.config.id()); @@ -537,7 +549,7 @@ private: } // Providing a new mesh, therefore this volume will get a new unique ID assigned. ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) : - name(other.name), m_mesh(new TriangleMesh(std::move(mesh))), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) + name(other.name), source(other.source), m_mesh(new TriangleMesh(std::move(mesh))), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) { assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); assert(this->id() != other.id() && this->config.id() == other.config.id()); @@ -558,8 +570,8 @@ private: } template void load(Archive &ar) { bool has_convex_hull; - ar(name, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); - cereal::load_by_value(ar, config); + ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); + cereal::load_by_value(ar, config); assert(m_mesh); if (has_convex_hull) { cereal::load_optional(ar, m_convex_hull); @@ -571,8 +583,8 @@ private: } template void save(Archive &ar) const { bool has_convex_hull = m_convex_hull.get() != nullptr; - ar(name, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); - cereal::save_by_value(ar, config); + ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); + cereal::save_by_value(ar, config); if (has_convex_hull) cereal::save_optional(ar, m_convex_hull); } @@ -733,6 +745,37 @@ public: ModelObjectPtrs objects; // Wipe tower object. ModelWipeTower wipe_tower; + + // Extensions for color print + struct CustomGCode + { + CustomGCode(double height, const std::string& code, int extruder, const std::string& color) : + height(height), gcode(code), extruder(extruder), color(color) {} + + bool operator<(const CustomGCode& other) const { return other.height > this->height; } + bool operator==(const CustomGCode& other) const + { + return (other.height == this->height) && + (other.gcode == this->gcode) && + (other.extruder == this->extruder )&& + (other.color == this->color ); + } + bool operator!=(const CustomGCode& other) const + { + return (other.height != this->height) || + (other.gcode != this->gcode) || + (other.extruder != this->extruder )|| + (other.color != this->color ); + } + + double height; + std::string gcode; + int extruder; // 0 - "gcode" will be applied for whole print + // else - "gcode" will be applied only for "extruder" print + std::string color; // if gcode is equal to PausePrintCode, + // this field is used for save a short message shown on Printer display + }; + std::vector custom_gcode_per_height; // Default constructor assigns a new ID to the model. Model() { assert(this->id().valid()); } @@ -798,6 +841,9 @@ public: // Propose an output path, replace extension. The new_extension shall contain the initial dot. std::string propose_export_file_name_and_path(const std::string &new_extension) const; + // from custom_gcode_per_height get just tool_change codes + std::vector> get_custom_tool_changes(double default_layer_height, size_t num_extruders) const; + private: explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); }; void assign_new_unique_ids_recursive(); diff --git a/src/libslic3r/MotionPlanner.cpp b/src/libslic3r/MotionPlanner.cpp index fb1ecfc0f..b3480694b 100644 --- a/src/libslic3r/MotionPlanner.cpp +++ b/src/libslic3r/MotionPlanner.cpp @@ -136,11 +136,11 @@ Polyline MotionPlanner::shortest_path(const Point &from, const Point &to) if (! grown_env.contains(from)) { // delete second point while the line connecting first to third crosses the // boundaries as many times as the current first to second - while (polyline.points.size() > 2 && intersection_ln(Line(from, polyline.points[2]), grown_env).size() == 1) + while (polyline.points.size() > 2 && intersection_ln(Line(from, polyline.points[2]), (Polygons)grown_env).size() == 1) polyline.points.erase(polyline.points.begin() + 1); } if (! grown_env.contains(to)) - while (polyline.points.size() > 2 && intersection_ln(Line(*(polyline.points.end() - 3), to), grown_env).size() == 1) + while (polyline.points.size() > 2 && intersection_ln(Line(*(polyline.points.end() - 3), to), (Polygons)grown_env).size() == 1) polyline.points.erase(polyline.points.end() - 2); } @@ -319,7 +319,7 @@ Polyline MotionPlannerGraph::shortest_path(size_t node_start, size_t node_end) c std::vector map_node_to_queue_id(m_adjacency_list.size(), size_t(-1)); distance[node_start] = 0.; - auto queue = make_mutable_priority_queue( + auto queue = make_mutable_priority_queue( [&map_node_to_queue_id](const node_t node, size_t idx) { map_node_to_queue_id[node] = idx; }, [&distance](const node_t node1, const node_t node2) { return distance[node1] < distance[node2]; }); queue.reserve(m_adjacency_list.size()); diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index 46cf2f633..d7aebde5e 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -3,11 +3,6 @@ namespace Slic3r { -MultiPoint::operator Points() const -{ - return this->points; -} - void MultiPoint::scale(double factor) { for (Point &pt : points) @@ -57,18 +52,7 @@ void MultiPoint::rotate(double angle, const Point ¢er) } } -void MultiPoint::reverse() -{ - std::reverse(this->points.begin(), this->points.end()); -} - -Point MultiPoint::first_point() const -{ - return this->points.front(); -} - -double -MultiPoint::length() const +double MultiPoint::length() const { Lines lines = this->lines(); double len = 0; @@ -78,8 +62,7 @@ MultiPoint::length() const return len; } -int -MultiPoint::find_point(const Point &point) const +int MultiPoint::find_point(const Point &point) const { for (const Point &pt : this->points) if (pt == point) @@ -87,21 +70,18 @@ MultiPoint::find_point(const Point &point) const return -1; // not found } -bool -MultiPoint::has_boundary_point(const Point &point) const +bool MultiPoint::has_boundary_point(const Point &point) const { double dist = (point.projection_onto(*this) - point).cast().norm(); return dist < SCALED_EPSILON; } -BoundingBox -MultiPoint::bounding_box() const +BoundingBox MultiPoint::bounding_box() const { return BoundingBox(this->points); } -bool -MultiPoint::has_duplicate_points() const +bool MultiPoint::has_duplicate_points() const { for (size_t i = 1; i < points.size(); ++i) if (points[i-1] == points[i]) @@ -109,8 +89,7 @@ MultiPoint::has_duplicate_points() const return false; } -bool -MultiPoint::remove_duplicate_points() +bool MultiPoint::remove_duplicate_points() { size_t j = 0; for (size_t i = 1; i < points.size(); ++i) { @@ -129,8 +108,7 @@ MultiPoint::remove_duplicate_points() return false; } -bool -MultiPoint::intersection(const Line& line, Point* intersection) const +bool MultiPoint::intersection(const Line& line, Point* intersection) const { Lines lines = this->lines(); for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) { diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index 2440c3a49..2198a5c15 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -17,7 +17,8 @@ class MultiPoint public: Points points; - operator Points() const; + operator Points() const { return this->points; } + MultiPoint() {} MultiPoint(const MultiPoint &other) : points(other.points) {} MultiPoint(MultiPoint &&other) : points(std::move(other.points)) {} @@ -32,9 +33,10 @@ public: void rotate(double angle) { this->rotate(cos(angle), sin(angle)); } void rotate(double cos_angle, double sin_angle); void rotate(double angle, const Point ¢er); - void reverse(); - Point first_point() const; - virtual Point last_point() const = 0; + void reverse() { std::reverse(this->points.begin(), this->points.end()); } + + const Point& first_point() const { return this->points.front(); } + virtual const Point& last_point() const = 0; virtual Lines lines() const = 0; size_t size() const { return points.size(); } bool empty() const { return points.empty(); } diff --git a/src/libslic3r/MutablePriorityQueue.hpp b/src/libslic3r/MutablePriorityQueue.hpp index 82e992fd6..b20bf60ea 100644 --- a/src/libslic3r/MutablePriorityQueue.hpp +++ b/src/libslic3r/MutablePriorityQueue.hpp @@ -3,7 +3,7 @@ #include -template +template class MutablePriorityQueue { public: @@ -13,21 +13,28 @@ public: {} ~MutablePriorityQueue() { clear(); } - inline void clear() { m_heap.clear(); } - inline void reserve(size_t cnt) { m_heap.reserve(cnt); } - inline void push(const T &item); - inline void push(T &&item); - inline void pop(); - inline T& top() { return m_heap.front(); } - inline void remove(size_t idx); - inline void update(size_t idx) { T item = m_heap[idx]; remove(idx); push(item); } + void clear(); + void reserve(size_t cnt) { m_heap.reserve(cnt); } + void push(const T &item); + void push(T &&item); + void pop(); + T& top() { return m_heap.front(); } + void remove(size_t idx); + void update(size_t idx) { T item = m_heap[idx]; remove(idx); push(item); } - inline size_t size() const { return m_heap.size(); } - inline bool empty() const { return m_heap.empty(); } + size_t size() const { return m_heap.size(); } + bool empty() const { return m_heap.empty(); } + + using iterator = typename std::vector::iterator; + using const_iterator = typename std::vector::const_iterator; + iterator begin() { return m_heap.begin(); } + iterator end() { return m_heap.end(); } + const_iterator cbegin() const { return m_heap.cbegin(); } + const_iterator cend() const { return m_heap.cend(); } protected: - inline void update_heap_up(size_t top, size_t bottom); - inline void update_heap_down(size_t top, size_t bottom); + void update_heap_up(size_t top, size_t bottom); + void update_heap_down(size_t top, size_t bottom); private: std::vector m_heap; @@ -35,15 +42,30 @@ private: LessPredicate m_less_predicate; }; -template -MutablePriorityQueue make_mutable_priority_queue(IndexSetter &&index_setter, LessPredicate &&less_predicate) +template +MutablePriorityQueue make_mutable_priority_queue(IndexSetter &&index_setter, LessPredicate &&less_predicate) { - return MutablePriorityQueue( + return MutablePriorityQueue( std::forward(index_setter), std::forward(less_predicate)); } -template -inline void MutablePriorityQueue::push(const T &item) +template +inline void MutablePriorityQueue::clear() +{ +#ifdef NDEBUG + // Only mark as removed from the queue in release mode, if configured so. + if (ResetIndexWhenRemoved) +#endif /* NDEBUG */ + { + for (size_t idx = 0; idx < m_heap.size(); ++ idx) + // Mark as removed from the queue. + m_index_setter(m_heap[idx], std::numeric_limits::max()); + } + m_heap.clear(); +} + +template +inline void MutablePriorityQueue::push(const T &item) { size_t idx = m_heap.size(); m_heap.emplace_back(item); @@ -51,8 +73,8 @@ inline void MutablePriorityQueue::push(const T &i update_heap_up(0, idx); } -template -inline void MutablePriorityQueue::push(T &&item) +template +inline void MutablePriorityQueue::push(T &&item) { size_t idx = m_heap.size(); m_heap.emplace_back(std::move(item)); @@ -60,10 +82,18 @@ inline void MutablePriorityQueue::push(T &&item) update_heap_up(0, idx); } -template -inline void MutablePriorityQueue::pop() +template +inline void MutablePriorityQueue::pop() { assert(! m_heap.empty()); +#ifdef NDEBUG + // Only mark as removed from the queue in release mode, if configured so. + if (ResetIndexWhenRemoved) +#endif /* NDEBUG */ + { + // Mark as removed from the queue. + m_index_setter(m_heap.front(), std::numeric_limits::max()); + } if (m_heap.size() > 1) { m_heap.front() = m_heap.back(); m_heap.pop_back(); @@ -73,10 +103,18 @@ inline void MutablePriorityQueue::pop() m_heap.clear(); } -template -inline void MutablePriorityQueue::remove(size_t idx) +template +inline void MutablePriorityQueue::remove(size_t idx) { assert(idx < m_heap.size()); +#ifdef NDEBUG + // Only mark as removed from the queue in release mode, if configured so. + if (ResetIndexWhenRemoved) +#endif /* NDEBUG */ + { + // Mark as removed from the queue. + m_index_setter(m_heap[idx], std::numeric_limits::max()); + } if (idx + 1 == m_heap.size()) { m_heap.pop_back(); return; @@ -88,8 +126,8 @@ inline void MutablePriorityQueue::remove(size_t i update_heap_up(0, idx); } -template -inline void MutablePriorityQueue::update_heap_up(size_t top, size_t bottom) +template +inline void MutablePriorityQueue::update_heap_up(size_t top, size_t bottom) { size_t childIdx = bottom; T *child = &m_heap[childIdx]; @@ -112,8 +150,8 @@ inline void MutablePriorityQueue::update_heap_up( } } -template -inline void MutablePriorityQueue::update_heap_down(size_t top, size_t bottom) +template +inline void MutablePriorityQueue::update_heap_down(size_t top, size_t bottom) { size_t parentIdx = top; T *parent = &m_heap[parentIdx]; diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 4e4670672..8fafb46b5 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -740,7 +740,8 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_loops( // reapply the nearest point search for starting point // We allow polyline reversal because Clipper may have randomly // reversed polylines during clipping. - paths = (ExtrusionPaths)ExtrusionEntityCollection(paths).chained_path(); + if (!paths.empty()) + paths = (ExtrusionPaths)ExtrusionEntityCollection(paths).chained_path_from(paths.front().first_point()); } else { ExtrusionPath path(role); path.polyline = loop->polygon.split_at_first_point(); @@ -764,8 +765,7 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_loops( // sort entities into a new collection using a nearest-neighbor search, // preserving the original indices which are useful for detecting thin walls - ExtrusionEntityCollection sorted_coll; - coll.chained_path(&sorted_coll, false, erMixed, &sorted_coll.orig_indices); + ExtrusionEntityCollection sorted_coll = coll.chained_path_from(coll.first_point()); // traverse children and build the final collection ExtrusionEntityCollection entities; @@ -814,7 +814,7 @@ PerimeterGenerator::_get_nearest_point(const PerimeterGeneratorLoops &children, for (size_t idx_child = 0; idx_child < children.size(); idx_child++) { const PerimeterGeneratorLoop &child = children[idx_child]; for (size_t idx_poly = 0; idx_poly < myPolylines.paths.size(); idx_poly++) { - if (myPolylines.paths[idx_poly].extruder_id == (unsigned int)-1) continue; + //if (myPolylines.paths[idx_poly].extruder_id == (unsigned int)-1) continue; if (myPolylines.paths[idx_poly].length() < dist_cut + SCALED_RESOLUTION) continue; if ((myPolylines.paths[idx_poly].role() == erExternalPerimeter || child.is_external() ) @@ -863,7 +863,7 @@ PerimeterGenerator::_get_nearest_point(const PerimeterGeneratorLoops &children, for (size_t idx_child = 0; idx_child < children.size(); idx_child++) { const PerimeterGeneratorLoop &child = children[idx_child]; for (size_t idx_poly = 0; idx_poly < myPolylines.paths.size(); idx_poly++) { - if (myPolylines.paths[idx_poly].extruder_id == (unsigned int)-1) continue; + //if (myPolylines.paths[idx_poly].extruder_id == (unsigned int)-1) continue; if (myPolylines.paths[idx_poly].length() < dist_cut + SCALED_RESOLUTION) continue; //second, try to check from one of my points @@ -893,7 +893,7 @@ PerimeterGenerator::_get_nearest_point(const PerimeterGeneratorLoops &children, for (size_t idx_child = 0; idx_child < children.size(); idx_child++) { const PerimeterGeneratorLoop &child = children[idx_child]; for (size_t idx_poly = 0; idx_poly < myPolylines.paths.size(); idx_poly++) { - if (myPolylines.paths[idx_poly].extruder_id == (unsigned int)-1) continue; + //if (myPolylines.paths[idx_poly].extruder_id == (unsigned int)-1) continue; if (myPolylines.paths[idx_poly].length() < dist_cut + SCALED_RESOLUTION) continue; //lastly, try to check from one of his points @@ -1014,7 +1014,8 @@ PerimeterGenerator::_extrude_and_cut_loop(const PerimeterGeneratorLoop &loop, co // reapply the nearest point search for starting point // We allow polyline reversal because Clipper may have randomly // reversed polylines during clipping. - paths = (ExtrusionPaths)ExtrusionEntityCollection(paths).chained_path(); + if(!paths.empty()) + paths = (ExtrusionPaths)ExtrusionEntityCollection(paths).chained_path_from(paths.front().first_point()); if (direction.length() > 0) { @@ -1277,9 +1278,9 @@ PerimeterGenerator::_traverse_and_join_loops(const PerimeterGeneratorLoop &loop, travel_path_begin.emplace_back(ExtrusionRole::erPerimeter, outer_start->mm3_per_mm, outer_start->width, outer_start->height); travel_path_begin.emplace_back(ExtrusionRole::erNone, 0, outer_start->width, outer_start->height); travel_path_begin.emplace_back(ExtrusionRole::erPerimeter, outer_start->mm3_per_mm, outer_start->width, outer_start->height); - travel_path_begin[0].extruder_id = -1; - travel_path_begin[1].extruder_id = -1; - travel_path_begin[2].extruder_id = -1; + //travel_path_begin[0].extruder_id = -1; + //travel_path_begin[1].extruder_id = -1; + //travel_path_begin[2].extruder_id = -1; Line line(outer_start->polyline.points.back(), inner_start->polyline.points.front()); Point p_dist_cut_extrude = (line.b - line.a); p_dist_cut_extrude.x() = (coord_t)(p_dist_cut_extrude.x() * ((double)max_width_extrusion) / (line.length() * 2)); @@ -1305,7 +1306,7 @@ PerimeterGenerator::_traverse_and_join_loops(const PerimeterGeneratorLoop &loop, flow_mult = max_width_extrusion / dist_travel; } travel_path_begin.emplace_back(ExtrusionRole::erPerimeter, outer_start->mm3_per_mm * flow_mult, (float)(outer_start->width * flow_mult), outer_start->height); - travel_path_begin[0].extruder_id = -1; + //travel_path_begin[0].extruder_id = -1; travel_path_begin[0].polyline.append(outer_start->polyline.points.back()); travel_path_begin[0].polyline.append(inner_start->polyline.points.front()); } @@ -1314,9 +1315,9 @@ PerimeterGenerator::_traverse_and_join_loops(const PerimeterGeneratorLoop &loop, travel_path_end.emplace_back(ExtrusionRole::erPerimeter, outer_end->mm3_per_mm, outer_end->width, outer_end->height); travel_path_end.emplace_back(ExtrusionRole::erNone, 0, outer_end->width, outer_end->height); travel_path_end.emplace_back(ExtrusionRole::erPerimeter, outer_end->mm3_per_mm, outer_end->width, outer_end->height); - travel_path_end[0].extruder_id = -1; - travel_path_end[1].extruder_id = -1; - travel_path_end[2].extruder_id = -1; + //travel_path_end[0].extruder_id = -1; + //travel_path_end[1].extruder_id = -1; + //travel_path_end[2].extruder_id = -1; Line line(inner_end->polyline.points.back(), outer_end->polyline.points.front()); Point p_dist_cut_extrude = (line.b - line.a); p_dist_cut_extrude.x() = (coord_t)(p_dist_cut_extrude.x() * ((double)max_width_extrusion) / (line.length() * 2)); @@ -1342,7 +1343,7 @@ PerimeterGenerator::_traverse_and_join_loops(const PerimeterGeneratorLoop &loop, flow_mult = max_width_extrusion / dist_travel; } travel_path_end.emplace_back(ExtrusionRole::erPerimeter, outer_end->mm3_per_mm * flow_mult, (float)(outer_end->width * flow_mult), outer_end->height); - travel_path_end[0].extruder_id = -1; + //travel_path_end[0].extruder_id = -1; travel_path_end[0].polyline.append(inner_end->polyline.points.back()); travel_path_end[0].polyline.append(outer_end->polyline.points.front()); } diff --git a/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp index 7b7457e8c..85f8d8473 100644 --- a/src/libslic3r/PerimeterGenerator.hpp +++ b/src/libslic3r/PerimeterGenerator.hpp @@ -84,7 +84,7 @@ public: ExtrusionEntityCollection* gap_fill, // Infills without the gap fills SurfaceCollection* fill_surfaces) - : slices(slices), lower_slices(NULL), upper_slices(NULL), layer_height(layer_height), + : slices(slices), lower_slices(nullptr), upper_slices(nullptr), layer_height(layer_height), layer_id(-1), perimeter_flow(flow), ext_perimeter_flow(flow), overhang_flow(flow), solid_infill_flow(flow), config(config), object_config(object_config), print_config(print_config), diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index eebd9e022..dfe168238 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -38,6 +38,7 @@ typedef std::vector PointPtrs; typedef std::vector PointConstPtrs; typedef std::vector Points3; typedef std::vector Pointfs; +typedef std::vector Vec2ds; typedef std::vector Pointf3s; typedef Eigen::Matrix Matrix2f; @@ -90,11 +91,12 @@ class Point : public Vec2crd public: typedef coord_t coord_type; - Point() : Vec2crd() { (*this)(0) = 0; (*this)(1) = 0; } - Point(int32_t x, int32_t y) { (*this)(0) = coord_t(x); (*this)(1) = coord_t(y); } - Point(int64_t x, int64_t y) { (*this)(0) = coord_t(x); (*this)(1) = coord_t(y); } - Point(double x, double y) { (*this)(0) = coord_t(lrintl(x)); (*this)(1) = coord_t(lrintl(y)); } + Point() : Vec2crd(0, 0) {} + Point(int32_t x, int32_t y) : Vec2crd(coord_t(x), coord_t(y)) {} + Point(int64_t x, int64_t y) : Vec2crd(coord_t(x), coord_t(y)) {} // for Clipper + Point(double x, double y) : Vec2crd(coord_t(lrint(x)), coord_t(lrint(y))) {} Point(const Point &rhs) { *this = rhs; } + explicit Point(const Vec2d& rhs) : Vec2crd(coord_t(lrint(rhs.x())), coord_t(lrint(rhs.y()))) {} // This constructor allows you to construct Point from Eigen expressions template Point(const Eigen::MatrixBase &other) : Vec2crd(other) {} @@ -144,6 +146,36 @@ public: } }; +inline bool is_approx(const Point &p1, const Point &p2, coord_t epsilon = coord_t(SCALED_EPSILON)) +{ + Point d = (p2 - p1).cwiseAbs(); + return d.x() < epsilon && d.y() < epsilon; +} + +inline bool is_approx(const Vec2f &p1, const Vec2f &p2, float epsilon = float(EPSILON)) +{ + Vec2f d = (p2 - p1).cwiseAbs(); + return d.x() < epsilon && d.y() < epsilon; +} + +inline bool is_approx(const Vec2d &p1, const Vec2d &p2, double epsilon = EPSILON) +{ + Vec2d d = (p2 - p1).cwiseAbs(); + return d.x() < epsilon && d.y() < epsilon; +} + +inline bool is_approx(const Vec3f &p1, const Vec3f &p2, float epsilon = float(EPSILON)) +{ + Vec3f d = (p2 - p1).cwiseAbs(); + return d.x() < epsilon && d.y() < epsilon && d.z() < epsilon; +} + +inline bool is_approx(const Vec3d &p1, const Vec3d &p2, double epsilon = EPSILON) +{ + Vec3d d = (p2 - p1).cwiseAbs(); + return d.x() < epsilon && d.y() < epsilon && d.z() < epsilon; +} + namespace int128 { // Exact orientation predicate, // returns +1: CCW, 0: collinear, -1: CW. diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index 2fe3c4d18..5833fff75 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -5,48 +5,17 @@ namespace Slic3r { -Polygon::operator Polygons() const -{ - Polygons pp; - pp.push_back(*this); - return pp; -} - -Polygon::operator Polyline() const -{ - return this->split_at_first_point(); -} - -Point& -Polygon::operator[](Points::size_type idx) -{ - return this->points[idx]; -} - -const Point& -Polygon::operator[](Points::size_type idx) const -{ - return this->points[idx]; -} - -Point -Polygon::last_point() const -{ - return this->points.front(); // last point == first point for polygons -} - Lines Polygon::lines() const { return to_lines(*this); } -Polyline -Polygon::split_at_vertex(const Point &point) const +Polyline Polygon::split_at_vertex(const Point &point) const { // find index of point for (const Point &pt : this->points) if (pt == point) - return this->split_at_index(&pt - &this->points.front()); + return this->split_at_index(int(&pt - &this->points.front())); throw std::invalid_argument("Point not found"); return Polyline(); } @@ -64,19 +33,6 @@ Polygon::split_at_index(size_t index) const return polyline; } -// Split a closed polygon into an open polyline, with the split point duplicated at both ends. -Polyline -Polygon::split_at_first_point() const -{ - return this->split_at_index(0); -} - -Points -Polygon::equally_spaced_points(double distance) const -{ - return this->split_at_first_point().equally_spaced_points(distance); -} - /* int64_t Polygon::area2x() const { @@ -107,20 +63,17 @@ double Polygon::area() const return 0.5 * a; } -bool -Polygon::is_counter_clockwise() const +bool Polygon::is_counter_clockwise() const { return ClipperLib::Orientation(Slic3rMultiPoint_to_ClipperPath(*this)); } -bool -Polygon::is_clockwise() const +bool Polygon::is_clockwise() const { return !this->is_counter_clockwise(); } -bool -Polygon::make_counter_clockwise() +bool Polygon::make_counter_clockwise() { if (!this->is_counter_clockwise()) { this->reverse(); @@ -129,8 +82,7 @@ Polygon::make_counter_clockwise() return false; } -bool -Polygon::make_clockwise() +bool Polygon::make_clockwise() { if (this->is_counter_clockwise()) { this->reverse(); @@ -139,16 +91,9 @@ Polygon::make_clockwise() return false; } -bool -Polygon::is_valid() const -{ - return this->points.size() >= 3; -} - // Does an unoriented polygon contain a point? // Tested by counting intersections along a horizontal line. -bool -Polygon::contains(const Point &point) const +bool Polygon::contains(const Point &point) const { // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html bool result = false; @@ -174,8 +119,7 @@ Polygon::contains(const Point &point) const } // this only works on CCW polygons as CW will be ripped out by Clipper's simplify_polygons() -Polygons -Polygon::simplify(double tolerance) const +Polygons Polygon::simplify(double tolerance) const { // repeat first point at the end in order to apply Douglas-Peucker // on the whole polygon @@ -189,8 +133,7 @@ Polygon::simplify(double tolerance) const return simplify_polygons(pp); } -void -Polygon::simplify(double tolerance, Polygons &polygons) const +void Polygon::simplify(double tolerance, Polygons &polygons) const { Polygons pp = this->simplify(tolerance); polygons.reserve(polygons.size() + pp.size()); @@ -198,8 +141,7 @@ Polygon::simplify(double tolerance, Polygons &polygons) const } // Only call this on convex polygons or it will return invalid results -void -Polygon::triangulate_convex(Polygons* polygons) const +void Polygon::triangulate_convex(Polygons* polygons) const { for (Points::const_iterator it = this->points.begin() + 2; it != this->points.end(); ++it) { Polygon p; @@ -214,8 +156,7 @@ Polygon::triangulate_convex(Polygons* polygons) const } // center of mass -Point -Polygon::centroid() const +Point Polygon::centroid() const { double area_temp = this->area(); double x_temp = 0; @@ -232,20 +173,19 @@ Polygon::centroid() const // find all concave vertices (i.e. having an internal angle greater than the supplied angle) // (external = right side, thus we consider ccw orientation) -Points -Polygon::concave_points(double angle) const +Points Polygon::concave_points(double angle) const { Points points; - angle = 2*PI - angle + EPSILON; + angle = 2. * PI - angle + EPSILON; // check whether first point forms a concave angle if (this->points.front().ccw_angle(this->points.back(), *(this->points.begin()+1)) <= angle) points.push_back(this->points.front()); // check whether points 1..(n-1) form concave angles - for (Points::const_iterator p = this->points.begin()+1; p != this->points.end()-1; ++p) { - if (p->ccw_angle(*(p-1), *(p+1)) <= angle) points.push_back(*p); - } + for (Points::const_iterator p = this->points.begin()+1; p != this->points.end()-1; ++ p) + if (p->ccw_angle(*(p-1), *(p+1)) <= angle) + points.push_back(*p); // check whether last point forms a concave angle if (this->points.back().ccw_angle(*(this->points.end()-2), this->points.front()) <= angle) @@ -256,8 +196,7 @@ Polygon::concave_points(double angle) const // find all convex vertices (i.e. having an internal angle smaller than the supplied angle) // (external = right side, thus we consider ccw orientation) -Points -Polygon::convex_points(double angle) const +Points Polygon::convex_points(double angle) const { Points points; angle = 2*PI - angle - EPSILON; @@ -316,6 +255,12 @@ Point Polygon::point_projection(const Point &point) const return proj; } + +BoundingBox get_extents(const Points &points) +{ + return BoundingBox(points); +} + size_t Polygon::remove_colinear_points(coord_t max_offset){ size_t nb_del = 0; if (points.size() < 3) return 0; @@ -344,7 +289,6 @@ size_t Polygon::remove_colinear_points(coord_t max_offset){ return nb_del; } - BoundingBox get_extents(const Polygon &poly) { return poly.bounding_box(); @@ -485,4 +429,10 @@ bool remove_small(Polygons &polys, double min_area) return modified; } +void remove_collinear(Polygons &polys, coord_t max_offset) +{ + for (Polygon &poly : polys) + poly.remove_colinear_points(max_offset); +} + } diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index c1004330d..57be4c917 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -13,15 +13,17 @@ namespace Slic3r { class Polygon; typedef std::vector Polygons; -class Polygon : public MultiPoint { +class Polygon : public MultiPoint +{ public: - operator Polygons() const; - operator Polyline() const; - Point& operator[](Points::size_type idx); - const Point& operator[](Points::size_type idx) const; - + operator Polygons() const { Polygons pp; pp.push_back(*this); return pp; } + operator Polyline() const { return this->split_at_first_point(); } + Point& operator[](Points::size_type idx) { return this->points[idx]; } + const Point& operator[](Points::size_type idx) const { return this->points[idx]; } + Polygon() {} - explicit Polygon(const Points &points): MultiPoint(points) {} + explicit Polygon(const Points &points) : MultiPoint(points) {} + Polygon(std::initializer_list points) : MultiPoint(points) {} Polygon(const Polygon &other) : MultiPoint(other.points) {} Polygon(Polygon &&other) : MultiPoint(std::move(other.points)) {} static Polygon new_scale(const std::vector &points) { @@ -34,20 +36,24 @@ public: Polygon& operator=(const Polygon &other) { points = other.points; return *this; } Polygon& operator=(Polygon &&other) { points = std::move(other.points); return *this; } - Point last_point() const; + // last point == first point for polygons + const Point& last_point() const override { return this->points.front(); } + virtual Lines lines() const; Polyline split_at_vertex(const Point &point) const; // Split a closed polygon into an open polyline, with the split point duplicated at both ends. Polyline split_at_index(size_t index) const; // Split a closed polygon into an open polyline, with the split point duplicated at both ends. - Polyline split_at_first_point() const; - Points equally_spaced_points(double distance) const; + Polyline split_at_first_point() const { return this->split_at_index(0); } + Points equally_spaced_points(double distance) const { return this->split_at_first_point().equally_spaced_points(distance); } + double area() const; bool is_counter_clockwise() const; bool is_clockwise() const; bool make_counter_clockwise(); bool make_clockwise(); - bool is_valid() const; + bool is_valid() const { return this->points.size() >= 3; } + // Does an unoriented polygon contain a point? // Tested by counting intersections along a horizontal line. bool contains(const Point &point) const; @@ -64,6 +70,10 @@ public: size_t remove_colinear_points(coord_t max_offset); }; +inline bool operator==(const Polygon &lhs, const Polygon &rhs) { return lhs.points == rhs.points; } +inline bool operator!=(const Polygon &lhs, const Polygon &rhs) { return lhs.points != rhs.points; } + +extern BoundingBox get_extents(const Points &points); extern BoundingBox get_extents(const Polygon &poly); extern BoundingBox get_extents(const Polygons &polygons); extern BoundingBox get_extents_rotated(const Polygon &poly, double angle); @@ -84,6 +94,8 @@ extern bool remove_sticks(Polygons &polys); // Remove polygons with less than 3 edges. extern bool remove_degenerate(Polygons &polys); extern bool remove_small(Polygons &polys, double min_area); +extern void remove_collinear(Polygon &poly); +extern void remove_collinear(Polygons &polys); // Append a vector of polygons at the end of another vector of polygons. inline void polygons_append(Polygons &dst, const Polygons &src) { dst.insert(dst.end(), src.begin(), src.end()); } @@ -98,6 +110,15 @@ inline void polygons_append(Polygons &dst, Polygons &&src) } } +inline Polygons polygons_simplify(const Polygons &polys, double tolerance) +{ + Polygons out; + out.reserve(polys.size()); + for (const Polygon &p : polys) + polygons_append(out, p.simplify(tolerance)); + return out; +} + inline void polygons_rotate(Polygons &polys, double angle) { const double cos_angle = cos(angle); diff --git a/src/libslic3r/PolygonTrimmer.cpp b/src/libslic3r/PolygonTrimmer.cpp index 3e3c9b498..2c4e06fc5 100644 --- a/src/libslic3r/PolygonTrimmer.cpp +++ b/src/libslic3r/PolygonTrimmer.cpp @@ -12,12 +12,11 @@ TrimmedLoop trim_loop(const Polygon &loop, const EdgeGrid::Grid &grid) TrimmedLoop out; if (loop.size() >= 2) { - size_t cnt = loop.points.size(); struct Visitor { Visitor(const EdgeGrid::Grid &grid, const Slic3r::Point *pt_prev, const Slic3r::Point *pt_this) : grid(grid), pt_prev(pt_prev), pt_this(pt_this) {} - void operator()(coord_t iy, coord_t ix) { + bool operator()(coord_t iy, coord_t ix) { // Called with a row and colum of the grid cell, which is intersected by a line. auto cell_data_range = grid.cell_data_range(iy, ix); for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++ it_contour_and_segment) { @@ -27,6 +26,8 @@ TrimmedLoop trim_loop(const Polygon &loop, const EdgeGrid::Grid &grid) // The two segments intersect. Add them to the output. } } + // Continue traversing the grid along the edge. + return true; } const EdgeGrid::Grid &grid; diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index 960df5ebd..a6e0b1a09 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -24,24 +24,17 @@ Polyline::operator Line() const return Line(this->points.front(), this->points.back()); } -Point -Polyline::last_point() const +const Point& Polyline::leftmost_point() const { - return this->points.back(); -} - -Point -Polyline::leftmost_point() const -{ - Point p = this->points.front(); - for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++it) { - if ((*it)(0) < p(0)) p = *it; + const Point *p = &this->points.front(); + for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++ it) { + if (it->x() < p->x()) + p = &(*it); } - return p; + return *p; } -Lines -Polyline::lines() const +Lines Polyline::lines() const { Lines lines; if (this->points.size() >= 2) { @@ -212,6 +205,20 @@ BoundingBox get_extents(const Polylines &polylines) return bb; } +const Point& leftmost_point(const Polylines &polylines) +{ + if (polylines.empty()) + throw std::invalid_argument("leftmost_point() called on empty PolylineCollection"); + Polylines::const_iterator it = polylines.begin(); + const Point *p = &it->leftmost_point(); + for (++ it; it != polylines.end(); ++it) { + const Point *p2 = &it->leftmost_point(); + if (p2->x() < p->x()) + p = p2; + } + return *p; +} + bool remove_degenerate(Polylines &polylines) { bool modified = false; diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index 1886d565c..8c19feae0 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -62,8 +62,9 @@ public: operator Polylines() const; operator Line() const; - Point last_point() const; - Point leftmost_point() const; + const Point& last_point() const override { return this->points.back(); } + + const Point& leftmost_point() const; virtual Lines lines() const; void clip_end(double distance); void clip_start(double distance); @@ -76,6 +77,15 @@ public: bool is_straight() const; }; +// Don't use this class in production code, it is used exclusively by the Perl binding for unit tests! +#ifdef PERL_UCHAR_MIN +class PolylineCollection +{ +public: + Polylines polylines; +}; +#endif /* PERL_UCHAR_MIN */ + extern BoundingBox get_extents(const Polyline &polyline); extern BoundingBox get_extents(const Polylines &polylines); @@ -128,6 +138,8 @@ inline void polylines_append(Polylines &dst, Polylines &&src) } } +const Point& leftmost_point(const Polylines &polylines); + bool remove_degenerate(Polylines &polylines); diff --git a/src/libslic3r/PolylineCollection.cpp b/src/libslic3r/PolylineCollection.cpp deleted file mode 100644 index 0c66c371a..000000000 --- a/src/libslic3r/PolylineCollection.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "PolylineCollection.hpp" - -namespace Slic3r { - -struct Chaining -{ - Point first; - Point last; - size_t idx; -}; - -template -inline int nearest_point_index(const std::vector &pairs, const Point &start_near, bool no_reverse) -{ - T dmin = std::numeric_limits::max(); - int idx = 0; - for (std::vector::const_iterator it = pairs.begin(); it != pairs.end(); ++it) { - T d = sqr(T(start_near(0) - it->first(0))); - if (d <= dmin) { - d += sqr(T(start_near(1) - it->first(1))); - if (d < dmin) { - idx = (it - pairs.begin()) * 2; - dmin = d; - if (dmin < EPSILON) - break; - } - } - if (! no_reverse) { - d = sqr(T(start_near(0) - it->last(0))); - if (d <= dmin) { - d += sqr(T(start_near(1) - it->last(1))); - if (d < dmin) { - idx = (it - pairs.begin()) * 2 + 1; - dmin = d; - if (dmin < EPSILON) - break; - } - } - } - } - return idx; -} - -Polylines PolylineCollection::_chained_path_from( - const Polylines &src, - Point start_near, - bool no_reverse, - bool move_from_src) -{ - std::vector endpoints; - endpoints.reserve(src.size()); - for (size_t i = 0; i < src.size(); ++ i) { - Chaining c; - c.first = src[i].first_point(); - if (! no_reverse) - c.last = src[i].last_point(); - c.idx = i; - endpoints.push_back(c); - } - Polylines retval; - while (! endpoints.empty()) { - // find nearest point - int endpoint_index = nearest_point_index(endpoints, start_near, no_reverse); - assert(endpoint_index >= 0 && size_t(endpoint_index) < endpoints.size() * 2); - if (move_from_src) { - retval.push_back(std::move(src[endpoints[endpoint_index/2].idx])); - } else { - retval.push_back(src[endpoints[endpoint_index/2].idx]); - } - if (endpoint_index & 1) - retval.back().reverse(); - endpoints.erase(endpoints.begin() + endpoint_index/2); - start_near = retval.back().last_point(); - } - return retval; -} - -Point PolylineCollection::leftmost_point(const Polylines &polylines) -{ - if (polylines.empty()) - throw std::invalid_argument("leftmost_point() called on empty PolylineCollection"); - Polylines::const_iterator it = polylines.begin(); - Point p = it->leftmost_point(); - for (++ it; it != polylines.end(); ++it) { - Point p2 = it->leftmost_point(); - if (p2(0) < p(0)) - p = p2; - } - return p; -} - -} // namespace Slic3r diff --git a/src/libslic3r/PolylineCollection.hpp b/src/libslic3r/PolylineCollection.hpp deleted file mode 100644 index 87fc1985b..000000000 --- a/src/libslic3r/PolylineCollection.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef slic3r_PolylineCollection_hpp_ -#define slic3r_PolylineCollection_hpp_ - -#include "libslic3r.h" -#include "Polyline.hpp" - -namespace Slic3r { - -class PolylineCollection -{ - static Polylines _chained_path_from( - const Polylines &src, - Point start_near, - bool no_reverse, - bool move_from_src); - -public: - Polylines polylines; - void chained_path(PolylineCollection* retval, bool no_reverse = false) const - { retval->polylines = chained_path(this->polylines, no_reverse); } - void chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse = false) const - { retval->polylines = chained_path_from(this->polylines, start_near, no_reverse); } - Point leftmost_point() const - { return leftmost_point(polylines); } - void append(const Polylines &polylines) - { this->polylines.insert(this->polylines.end(), polylines.begin(), polylines.end()); } - - static Point leftmost_point(const Polylines &polylines); - static Polylines chained_path(Polylines &&src, bool no_reverse = false) { - return (src.empty() || src.front().points.empty()) ? - Polylines() : - _chained_path_from(src, src.front().first_point(), no_reverse, true); - } - static Polylines chained_path_from(Polylines &&src, Point start_near, bool no_reverse = false) - { return _chained_path_from(src, start_near, no_reverse, true); } - static Polylines chained_path(const Polylines &src, bool no_reverse = false) { - return (src.empty() || src.front().points.empty()) ? - Polylines() : - _chained_path_from(src, src.front().first_point(), no_reverse, false); - } - static Polylines chained_path_from(const Polylines &src, Point start_near, bool no_reverse = false) - { return _chained_path_from(src, start_near, no_reverse, false); } -}; - -} - -#endif diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index b33c95d30..ccad4a2d6 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -7,6 +7,7 @@ #include "Flow.hpp" #include "Geometry.hpp" #include "I18N.hpp" +#include "ShortestPath.hpp" #include "SupportMaterial.hpp" #include "GCode.hpp" #include "GCode/WipeTower.hpp" @@ -15,7 +16,6 @@ //#include "PrintExport.hpp" #include -#include #include #include @@ -148,10 +148,7 @@ bool Print::invalidate_state_by_config_options(const std::vector steps_ignore; @@ -172,7 +169,10 @@ bool Print::invalidate_state_by_config_options(const std::vectorstate_mutex()); + tbb::mutex::scoped_lock lock(this->state_mutex()); for (const PrintObject *object : m_objects) if (! object->is_step_done_unguarded(step)) return false; @@ -362,17 +364,6 @@ unsigned int Print::num_object_instances() const return instances; } -void Print::_simplify_slices(double distance) -{ - for (PrintObject *object : m_objects) { - for (Layer *layer : object->m_layers) { - layer->slices.simplify(distance); - for (LayerRegion *layerm : layer->regions()) - layerm->m_slices.simplify(distance); - } - } -} - double Print::max_allowed_layer_height() const { double nozzle_diameter_max = 0.; @@ -680,11 +671,59 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ else m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr); } + + // Convert input config ranges into continuous non-overlapping sorted vector of intervals and their configs, + // considering custom_tool_change values + void assign(const t_layer_config_ranges &in, const std::vector> &custom_tool_changes) { + m_ranges.clear(); + m_ranges.reserve(in.size()); + // Input ranges are sorted lexicographically. First range trims the other ranges. + coordf_t last_z = 0; + for (const std::pair &range : in) + if (range.first.second > last_z) { + coordf_t min_z = std::max(range.first.first, 0.); + if (min_z > last_z + EPSILON) { + m_ranges.emplace_back(t_layer_height_range(last_z, min_z), nullptr); + last_z = min_z; + } + if (range.first.second > last_z + EPSILON) { + const DynamicPrintConfig* cfg = &range.second; + m_ranges.emplace_back(t_layer_height_range(last_z, range.first.second), cfg); + last_z = range.first.second; + } + } + + // add ranges for extruder changes from custom_tool_changes + for (size_t i = 0; i < custom_tool_changes.size(); i++) { + const DynamicPrintConfig* cfg = &custom_tool_changes[i].second; + coordf_t cur_Z = custom_tool_changes[i].first; + coordf_t next_Z = i == custom_tool_changes.size()-1 ? DBL_MAX : custom_tool_changes[i+1].first; + if (cur_Z > last_z + EPSILON) { + if (i==0) + m_ranges.emplace_back(t_layer_height_range(last_z, cur_Z), nullptr); + m_ranges.emplace_back(t_layer_height_range(cur_Z, next_Z), cfg); + } + else if (next_Z > last_z + EPSILON) + m_ranges.emplace_back(t_layer_height_range(last_z, next_Z), cfg); + } + + if (m_ranges.empty()) + m_ranges.emplace_back(t_layer_height_range(0, DBL_MAX), nullptr); + else if (m_ranges.back().second == nullptr) + m_ranges.back().first.second = DBL_MAX; + else if (m_ranges.back().first.second != DBL_MAX) + m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr); + } const DynamicPrintConfig* config(const t_layer_height_range &range) const { auto it = std::lower_bound(m_ranges.begin(), m_ranges.end(), std::make_pair< t_layer_height_range, const DynamicPrintConfig*>(t_layer_height_range(range.first - EPSILON, range.second - EPSILON), nullptr)); - assert(it != m_ranges.end()); - assert(it == m_ranges.end() || std::abs(it->first.first - range.first ) < EPSILON); - assert(it == m_ranges.end() || std::abs(it->first.second - range.second) < EPSILON); + // #ys_FIXME_COLOR + // assert(it != m_ranges.end()); + // assert(it == m_ranges.end() || std::abs(it->first.first - range.first ) < EPSILON); + // assert(it == m_ranges.end() || std::abs(it->first.second - range.second) < EPSILON); + if (it == m_ranges.end() || + std::abs(it->first.first - range.first) > EPSILON || + std::abs(it->first.second - range.second) > EPSILON ) + return nullptr; // desired range doesn't found return (it == m_ranges.end()) ? nullptr : it->second; } std::vector>::const_iterator begin() const { return m_ranges.cbegin(); } @@ -732,6 +771,13 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ // The object list did not change. for (const ModelObject *model_object : m_model.objects) model_object_status.emplace(model_object->id(), ModelObjectStatus::Old); + + // But if custom gcode per layer height was changed + if (m_model.custom_gcode_per_height != model.custom_gcode_per_height) { + // we should stop background processing + update_apply_status(this->invalidate_step(psGCodeExport)); + m_model.custom_gcode_per_height = model.custom_gcode_per_height; + } } else if (model_object_list_extended(m_model, model)) { // Add new objects. Their volumes and configs will be synchronized later. update_apply_status(this->invalidate_step(psGCodeExport)); @@ -823,6 +869,9 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ for (PrintObject *print_object : m_objects) print_object_status.emplace(PrintObjectStatus(print_object)); + std::vector> custom_tool_changes = + m_model.get_custom_tool_changes(m_default_object_config.layer_height, num_extruders); + // 3) Synchronize ModelObjects & PrintObjects. for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) { ModelObject &model_object = *m_model.objects[idx_model_object]; @@ -830,7 +879,9 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ assert(it_status != model_object_status.end()); assert(it_status->status != ModelObjectStatus::Deleted); const ModelObject& model_object_new = *model.objects[idx_model_object]; - const_cast(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges); + // ys_FIXME_COLOR + // const_cast(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges); + const_cast(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges, custom_tool_changes); if (it_status->status == ModelObjectStatus::New) // PrintObject instances will be added in the next loop. continue; @@ -998,6 +1049,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ PrintRegionConfig this_region_config; bool this_region_config_set = false; for (PrintObject *print_object : m_objects) { + if(m_force_update_print_regions && !custom_tool_changes.empty()) + goto print_object_end; const LayerRanges *layer_ranges; { auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id())); @@ -1148,6 +1201,9 @@ std::string Print::validate() const if (m_objects.empty()) return L("All objects are outside of the print volume."); + if (extruders().empty()) + return L("The supplied settings will cause an empty print."); + if (m_config.complete_objects) { // Check horizontal clearance. { @@ -1228,6 +1284,8 @@ std::string Print::validate() const return L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1)."); if (m_config.ooze_prevention) return L("Ooze prevention is currently not supported with the wipe tower enabled."); + if (m_config.use_volumetric_e) + return L("The Wipe Tower currently does not support volumetric E (use_volumetric_e=0)."); if (m_objects.size() > 1) { bool has_custom_layering = false; @@ -1306,11 +1364,8 @@ std::string Print::validate() const } } - { - // find the smallest nozzle diameter - std::vector extruders = this->extruders(); - if (extruders.empty()) - return L("The supplied settings will cause an empty print."); + { + std::vector extruders = this->extruders(); // Find the smallest used nozzle diameter and the number of unique nozzle diameters. double min_nozzle_diameter = std::numeric_limits::max(); @@ -1549,6 +1604,14 @@ void Print::process() obj->infill(); for (PrintObject *obj : m_objects) obj->generate_support_material(); + if (this->set_started(psWipeTower)) { + m_wipe_tower_data.clear(); + if (this->has_wipe_tower()) { + //this->set_status(95, L("Generating wipe tower")); + this->_make_wipe_tower(); + } + this->set_done(psWipeTower); + } if (this->set_started(psSkirt)) { m_skirt.clear(); for (PrintObject *obj : m_objects) { @@ -1601,14 +1664,6 @@ void Print::process() } this->set_done(psBrim); } - if (this->set_started(psWipeTower)) { - m_wipe_tower_data.clear(); - if (this->has_wipe_tower()) { - //this->set_status(95, L("Generating wipe tower")); - this->_make_wipe_tower(); - } - this->set_done(psWipeTower); - } BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info(); } @@ -1616,7 +1671,11 @@ void Print::process() // The export_gcode may die for various reasons (fails to process output_filename_format, // write error into the G-code, cannot execute post-processing scripts). // It is up to the caller to show an error message. +#if ENABLE_THUMBNAIL_GENERATOR +std::string Print::export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb) +#else std::string Print::export_gcode(const std::string &path_template, GCodePreviewData *preview_data) +#endif // ENABLE_THUMBNAIL_GENERATOR { // output everything to a G-code file // The following call may die if the output_filename_format template substitution fails. @@ -1633,7 +1692,11 @@ std::string Print::export_gcode(const std::string &path_template, GCodePreviewDa // The following line may die for multiple reasons. GCode gcode; +#if ENABLE_THUMBNAIL_GENERATOR + gcode.do_export(this, path.c_str(), preview_data, thumbnail_cb); +#else gcode.do_export(this, path.c_str(), preview_data); +#endif // ENABLE_THUMBNAIL_GENERATOR return path.c_str(); } @@ -1665,7 +1728,7 @@ void Print::_make_skirt(const PrintObjectPtrs &objects, ExtrusionEntityCollectio for (const Layer *layer : object->m_layers) { if (layer->print_z > skirt_height_z) break; - for (const ExPolygon &expoly : layer->slices.expolygons) + for (const ExPolygon &expoly : layer->slices) // Collect the outer contour points only, ignore holes for the calculation of the convex hull. append(object_points, expoly.contour.points); } @@ -1689,6 +1752,24 @@ void Print::_make_skirt(const PrintObjectPtrs &objects, ExtrusionEntityCollectio } } + // Include the wipe tower. + if (has_wipe_tower() && ! m_wipe_tower_data.tool_changes.empty()) { + double width = m_config.wipe_tower_width + 2*m_wipe_tower_data.brim_width; + double depth = m_wipe_tower_data.depth + 2*m_wipe_tower_data.brim_width; + Vec2d pt = Vec2d(-m_wipe_tower_data.brim_width, -m_wipe_tower_data.brim_width); + + std::vector pts; + pts.push_back(Vec2d(pt.x(), pt.y())); + pts.push_back(Vec2d(pt.x()+width, pt.y())); + pts.push_back(Vec2d(pt.x()+width, pt.y()+depth)); + pts.push_back(Vec2d(pt.x(), pt.y()+depth)); + for (Vec2d& pt : pts) { + pt = Eigen::Rotation2Dd(Geometry::deg2rad(m_config.wipe_tower_rotation_angle.value)) * pt; + pt += Vec2d(m_config.wipe_tower_x.value, m_config.wipe_tower_y.value); + points.push_back(Point(scale_(pt.x()), scale_(pt.y()))); + } + } + if (points.size() < 3) // At least three points required for a convex hull. return; @@ -2022,7 +2103,7 @@ ExPolygons Print::_make_brim_interior(const PrintObjectPtrs &objects, const ExPo ExPolygons islands; for (PrintObject *object : objects) { ExPolygons object_islands; - for (ExPolygon &expoly : object->m_layers.front()->slices.expolygons) + for (ExPolygon &expoly : object->m_layers.front()->slices) object_islands.push_back(expoly); if (!object->support_layers().empty()) { Polygons polys = object->support_layers().front()->support_fills.polygons_covered_by_spacing(float(SCALED_EPSILON)); @@ -2189,6 +2270,22 @@ bool Print::has_wipe_tower() const m_config.nozzle_diameter.values.size() > 1; } +const WipeTowerData& Print::wipe_tower_data(size_t extruders_cnt, double first_layer_height, double nozzle_diameter) const +{ + // If the wipe tower wasn't created yet, make sure the depth and brim_width members are set to default. + if (! is_step_done(psWipeTower) && extruders_cnt !=0) { + + float width = m_config.wipe_tower_width; + float brim_spacing = nozzle_diameter * 1.25f - first_layer_height * (1. - M_PI_4); + + const_cast(this)->m_wipe_tower_data.depth = (900.f/width) * float(extruders_cnt - 1); + const_cast(this)->m_wipe_tower_data.brim_width = 4.5f * brim_spacing; + } + + return m_wipe_tower_data; +} + + void Print::_make_wipe_tower() { m_wipe_tower_data.clear(); @@ -2330,6 +2427,7 @@ void Print::_make_wipe_tower() m_wipe_tower_data.tool_changes.reserve(m_wipe_tower_data.tool_ordering.layer_tools().size()); wipe_tower.generate(m_wipe_tower_data.tool_changes); m_wipe_tower_data.depth = wipe_tower.get_depth(); + m_wipe_tower_data.brim_width = wipe_tower.get_brim_width(); // Unload the current filament over the purge tower. coordf_t layer_height = m_objects.front()->config().layer_height.value; @@ -2383,6 +2481,7 @@ DynamicConfig PrintStatistics::config() const config.set_key_value("used_filament", new ConfigOptionFloat (this->total_used_filament / 1000.)); config.set_key_value("extruded_volume", new ConfigOptionFloat (this->total_extruded_volume)); config.set_key_value("total_cost", new ConfigOptionFloat (this->total_cost)); + config.set_key_value("total_toolchanges", new ConfigOptionInt(this->total_toolchanges)); config.set_key_value("total_weight", new ConfigOptionFloat (this->total_weight)); config.set_key_value("total_wipe_tower_cost", new ConfigOptionFloat (this->total_wipe_tower_cost)); config.set_key_value("total_wipe_tower_filament", new ConfigOptionFloat (this->total_wipe_tower_filament)); @@ -2395,7 +2494,7 @@ DynamicConfig PrintStatistics::placeholders() for (const std::string &key : { "print_time", "normal_print_time", "silent_print_time", "used_filament", "extruded_volume", "total_cost", "total_weight", - "total_wipe_tower_cost", "total_wipe_tower_filament"}) + "total_toolchanges", "total_wipe_tower_cost", "total_wipe_tower_filament"}) config.set_key_value(key, new ConfigOptionString(std::string("{") + key + "}")); return config; } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 123161573..4ca881a12 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -11,6 +11,9 @@ #include "Slicing.hpp" #include "GCode/ToolOrdering.hpp" #include "GCode/WipeTower.hpp" +#if ENABLE_THUMBNAIL_GENERATOR +#include "GCode/ThumbnailData.hpp" +#endif // ENABLE_THUMBNAIL_GENERATOR namespace Slic3r { @@ -96,6 +99,7 @@ public: 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); } // 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)); } @@ -241,6 +245,7 @@ struct WipeTowerData // Depth of the wipe tower to pass to GLCanvas3D for exact bounding box: float depth; + float brim_width; void clear() { tool_ordering.clear(); @@ -250,6 +255,7 @@ struct WipeTowerData used_filament.clear(); number_of_toolchanges = -1; depth = 0.f; + brim_width = 0.f; } }; @@ -263,6 +269,7 @@ struct PrintStatistics double total_used_filament; double total_extruded_volume; double total_cost; + int total_toolchanges; double total_weight; double total_wipe_tower_cost; double total_wipe_tower_filament; @@ -283,6 +290,7 @@ struct PrintStatistics total_used_filament = 0.; total_extruded_volume = 0.; total_cost = 0.; + total_toolchanges = 0; total_weight = 0.; total_wipe_tower_cost = 0.; total_wipe_tower_filament = 0.; @@ -318,7 +326,11 @@ public: void process() override; // Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file. // If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r). +#if ENABLE_THUMBNAIL_GENERATOR + std::string export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); +#else std::string export_gcode(const std::string &path_template, GCodePreviewData *preview_data); +#endif // ENABLE_THUMBNAIL_GENERATOR // methods for handling state bool is_step_done(PrintStep step) const { return Inherited::is_step_done(step); } @@ -329,7 +341,6 @@ public: bool has_infinite_skirt() const; bool has_skirt() const; - float get_wipe_tower_depth() const { return m_wipe_tower_data.depth; } // Returns an empty string if valid, otherwise returns an error message. std::string validate() const override; @@ -368,13 +379,16 @@ public: // Wipe tower support. bool has_wipe_tower() const; - const WipeTowerData& wipe_tower_data() const { return m_wipe_tower_data; } + const WipeTowerData& wipe_tower_data(size_t extruders_cnt = 0, double first_layer_height = 0., double nozzle_diameter = 0.) const; std::string output_filename(const std::string &filename_base = std::string()) const override; // Accessed by SupportMaterial const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; } + // force update of PrintRegions, when custom_tool_change is not empty and (Re)Slicing is started + void set_force_update_print_regions(bool force_update_print_regions) { m_force_update_print_regions = force_update_print_regions; } + //put this in public to be accessible for tests, it was in private before. bool invalidate_state_by_config_options(const std::vector &opt_keys); protected: @@ -400,7 +414,6 @@ private: ExPolygons _make_brim_interior(const PrintObjectPtrs &objects, const ExPolygons &unbrimmable, ExtrusionEntityCollection &out); Polylines _reorder_brim_polyline(Polylines lines, ExtrusionEntityCollection &out); void _make_wipe_tower(); - void _simplify_slices(double distance); // Declared here to have access to Model / ModelObject / ModelInstance static void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src); @@ -421,6 +434,9 @@ private: // Estimated print time, filament consumed. PrintStatistics m_print_statistics; + // flag used + bool m_force_update_print_regions = false; + // To allow GCode to set the Print's GCodeExport step status. friend class GCode; // Allow PrintObject to access m_mutex and m_cancel_callback. diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index aebc87904..05d884cc8 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -268,8 +268,7 @@ public: std::string text; // Bitmap of flags. enum FlagBits { - DEFAULT, - NO_RELOAD_SCENE = 0, + DEFAULT = 0, RELOAD_SCENE = 1 << 1, RELOAD_SLA_SUPPORT_POINTS = 1 << 2, RELOAD_SLA_PREVIEW = 1 << 3, diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 4eee71ef4..46aa4b6be 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -30,7 +30,7 @@ PrintConfigDef::PrintConfigDef() //assign params that are not already allocated to FFF+SLA (default from slic3rPE) assign_printer_technology_to_unknown(this->options, ptFFF | ptSLA); this->init_fff_params(); - this->init_extruder_retract_keys(); + this->init_extruder_option_keys(); assign_printer_technology_to_unknown(this->options, ptFFF); this->init_sla_params(); assign_printer_technology_to_unknown(this->options, ptSLA); @@ -67,6 +67,11 @@ void PrintConfigDef::init_common_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionString("")); + def = this->add("thumbnails", coPoints); + def->label = L("Picture sizes to be stored into a .gcode and .sl1 files"); + def->mode = comExpert; + def->set_default_value(new ConfigOptionPoints()); + def = this->add("layer_height", coFloat); def->label = L("Base Layer height"); def->category = OptionCategory::perimeter; @@ -625,6 +630,7 @@ void PrintConfigDef::init_fff_params() "If left zero, default extrusion width will be used if set, otherwise 1.05 x nozzle diameter will be used. " "If expressed as percentage (for example 112.5%), it will be computed over nozzle diameter."); def->sidetext = L("mm or %"); + def->min = 0; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); @@ -800,6 +806,7 @@ void PrintConfigDef::init_fff_params() "(see the tooltips for perimeter extrusion width, infill extrusion width etc). " "If expressed as percentage (for example: 105%), it will be computed over nozzle diameter."); def->sidetext = L("mm or %"); + def->min = 0; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); @@ -1124,6 +1131,10 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionStrings { "" }); def->cli = ConfigOptionDef::nocli; + def = this->add("filament_vendor", coString); + def->set_default_value(new ConfigOptionString(L("(Unknown)"))); + def->cli = ConfigOptionDef::nocli; + def = this->add("fill_angle", coFloat); def->label = L("Fill"); def->full_label = L("Fill angle"); @@ -1272,6 +1283,7 @@ void PrintConfigDef::init_fff_params() "If set to zero, it will use the default extrusion width."); def->sidetext = L("mm or %"); def->ratio_over = "first_layer_height"; + def->min = 0; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloatOrPercent(140, true)); @@ -1483,6 +1495,7 @@ void PrintConfigDef::init_fff_params() "You may want to use fatter extrudates to speed up the infill and make your parts stronger. " "If expressed as percentage (for example 110%) it will be computed over nozzle diameter."); def->sidetext = L("mm or %"); + def->min = 0; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); @@ -1865,8 +1878,10 @@ void PrintConfigDef::init_fff_params() def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); def->enum_values.push_back("octoprint"); def->enum_values.push_back("duet"); + def->enum_values.push_back("flashair"); def->enum_labels.push_back("OctoPrint"); def->enum_labels.push_back("Duet"); + def->enum_labels.push_back("FlashAir"); def->mode = comAdvanced; def->set_default_value(new ConfigOptionEnum(htOctoPrint)); @@ -2009,6 +2024,7 @@ void PrintConfigDef::init_fff_params() "If expressed as percentage (for example 105%) it will be computed over nozzle diameter."); def->sidetext = L("mm or %"); def->aliases = { "perimeters_extrusion_width" }; + def->min = 0; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); @@ -2452,6 +2468,7 @@ void PrintConfigDef::init_fff_params() "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 110%) it will be computed over nozzle diameter."); def->sidetext = L("mm or %"); + def->min = 0; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); @@ -2557,6 +2574,14 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionBool(true)); + def = this->add("wipe_tower_no_sparse_layers", coBool); + def->label = L("No sparse layers (EXPERIMENTAL)"); + def->tooltip = L("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."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + def = this->add("support_material", coBool); def->label = L("Generate support material"); def->category = OptionCategory::support; @@ -2678,6 +2703,7 @@ void PrintConfigDef::init_fff_params() "If left zero, default extrusion width will be used if set, otherwise nozzle diameter will be used. " "If expressed as percentage (for example 110%) it will be computed over nozzle diameter."); def->sidetext = L("mm or %"); + def->min = 0; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); @@ -2895,6 +2921,7 @@ void PrintConfigDef::init_fff_params() "If left zero, default extrusion width will be used if set, otherwise nozzle diameter will be used. " "If expressed as percentage (for example 110%) it will be computed over nozzle diameter."); def->sidetext = L("mm or %"); + def->min = 0; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); @@ -3174,8 +3201,17 @@ void PrintConfigDef::init_fff_params() } } -void PrintConfigDef::init_extruder_retract_keys() +void PrintConfigDef::init_extruder_option_keys() { + // ConfigOptionFloats, ConfigOptionPercents, ConfigOptionBools, ConfigOptionStrings + m_extruder_option_keys = { + "nozzle_diameter", "min_layer_height", "max_layer_height", "extruder_offset", + "retract_length", "retract_lift", "retract_lift_above", "retract_lift_below", "retract_speed", "deretract_speed", + "retract_before_wipe", "retract_restart_extra", "retract_before_travel", "wipe", + "retract_layer_change", "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour", + "default_filament_profile" + }; + m_extruder_retract_keys = { "deretract_speed", "retract_before_travel", @@ -3300,11 +3336,24 @@ void PrintConfigDef::init_sla_params() "the threshold in the middle. This behaviour eliminates " "antialiasing without losing holes in polygons."); def->min = 0; + def->max = 1; def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(1.0)); // SLA Material settings. + def = this->add("material_type", coString); + def->label = L("SLA material type"); + def->tooltip = L("SLA material type"); + def->gui_type = "f_enum_open"; // TODO: ??? + def->gui_flags = "show_value"; + def->enum_values.push_back("Tough"); + def->enum_values.push_back("Flexible"); + def->enum_values.push_back("Casting"); + def->enum_values.push_back("Dental"); + def->enum_values.push_back("Heat-resistant"); + def->set_default_value(new ConfigOptionString("Tough")); + def = this->add("initial_layer_height", coFloat); def->label = L("Initial layer height"); def->tooltip = L("Initial layer height"); @@ -3312,6 +3361,34 @@ void PrintConfigDef::init_sla_params() def->min = 0; def->set_default_value(new ConfigOptionFloat(0.3)); + def = this->add("bottle_volume", coFloat); + def->label = L("Bottle volume"); + def->tooltip = L("Bottle volume"); + def->sidetext = L("ml"); + def->min = 50; + def->set_default_value(new ConfigOptionFloat(1000.0)); + + def = this->add("bottle_weight", coFloat); + def->label = L("Bottle weight"); + def->tooltip = L("Bottle weight"); + def->sidetext = L("kg"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(1.0)); + + def = this->add("material_density", coFloat); + def->label = L("Density"); + def->tooltip = L("Density"); + def->sidetext = L("g/ml"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(1.0)); + + def = this->add("bottle_cost", coFloat); + def->label = L("Cost"); + def->tooltip = L("Cost"); + def->sidetext = L("money/bottle"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(0.0)); + def = this->add("faded_layers", coInt); def->label = L("Faded layers"); def->tooltip = L("Number of the layers needed for the exposure time fade from initial exposure time to the exposure time"); @@ -3382,6 +3459,10 @@ void PrintConfigDef::init_sla_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionString("")); + def = this->add("material_vendor", coString); + def->set_default_value(new ConfigOptionString(L("(Unknown)"))); + def->cli = ConfigOptionDef::nocli; + def = this->add("default_sla_material_profile", coString); def->label = L("Default SLA material profile"); def->tooltip = L("Default print profile associated with the current printer profile. " @@ -3603,6 +3684,17 @@ void PrintConfigDef::init_sla_params() def->max = 30; def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(0.)); + + def = this->add("pad_brim_size", coFloat); + def->label = L("Pad brim size"); + def->tooltip = L("How far should the pad extend around the contained geometry"); + def->category = L("Pad"); + // def->tooltip = L(""); + def->sidetext = L("mm"); + def->min = 0; + def->max = 30; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1.6)); def = this->add("pad_max_merge_distance", coFloat); def->label = L("Max merge distance"); @@ -3643,6 +3735,13 @@ void PrintConfigDef::init_sla_params() def->tooltip = L("Create pad around object and ignore the support elevation"); def->mode = comSimple; def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("pad_around_object_everywhere", coBool); + def->label = L("Pad around object everywhere"); + def->category = L("Pad"); + def->tooltip = L("Force pad around object everywhere"); + def->mode = comSimple; + def->set_default_value(new ConfigOptionBool(false)); def = this->add("pad_object_gap", coFloat); def->label = L("Pad object gap"); @@ -3760,9 +3859,13 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va const PrintConfigDef print_config_def; -DynamicPrintConfig* DynamicPrintConfig::new_from_defaults() +DynamicPrintConfig DynamicPrintConfig::full_print_config() +{ + return DynamicPrintConfig((const PrintRegionConfig&)FullPrintConfig::defaults()); +} + +DynamicPrintConfig::DynamicPrintConfig(const StaticPrintConfig& rhs) : DynamicConfig(rhs, rhs.keys_ref()) { - return new_from_defaults_keys(FullPrintConfig::defaults().keys()); } DynamicPrintConfig* DynamicPrintConfig::new_from_defaults_keys(const std::vector &keys) @@ -3815,6 +3918,20 @@ void DynamicPrintConfig::normalize() } } +void DynamicPrintConfig::set_num_extruders(unsigned int num_extruders) +{ + const auto &defaults = FullPrintConfig::defaults(); + for (const std::string &key : print_config_def.extruder_option_keys()) { + if (key == "default_filament_profile") + continue; + auto *opt = this->option(key, false); + assert(opt != nullptr); + assert(opt->is_vector()); + if (opt != nullptr && opt->is_vector()) + static_cast(opt)->resize(num_extruders, defaults.option(key)); + } +} + std::string DynamicPrintConfig::validate() { // Full print config is initialized from the defaults. diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 84a6fdbab..5cbfb969b 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -47,7 +47,7 @@ enum GCodeFlavor : unsigned char { }; enum PrintHostType { - htOctoPrint, htDuet + htOctoPrint, htDuet, htFlashAir }; enum InfillPattern { @@ -71,6 +71,13 @@ enum FilamentType { }; */ +enum SLAMaterial { + slamTough, + slamFlex, + slamCasting, + slamDental, + slamHeatResistant, +}; enum DenseInfillAlgo { dfaAutomatic, dfaAutoNotFull, dfaEnlarged, }; @@ -94,6 +101,12 @@ enum SLAPillarConnectionMode { slapcmDynamic }; +// ys_FIXME ! may be, it's not a best place +// Additional Codes which can be set by user using DoubleSlider +static const std::string ColorChangeCode = "M600"; +static const std::string PausePrintCode = "M601"; +static const std::string ExtruderChangeCode = "tool_change"; + template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { static t_config_enum_values keys_map; if (keys_map.empty()) { @@ -136,6 +149,7 @@ template<> inline const t_config_enum_values& ConfigOptionEnum::g if (keys_map.empty()) { keys_map["octoprint"] = htOctoPrint; keys_map["duet"] = htDuet; + keys_map["flashair"] = htFlashAir; } return keys_map; } @@ -275,6 +289,8 @@ public: static void handle_legacy(t_config_option_key &opt_key, std::string &value); + // Array options growing with the number of extruders + const std::vector& extruder_option_keys() const { return m_extruder_option_keys; } // Options defining the extruder retract properties. These keys are sorted lexicographically. // The extruder retract keys could be overidden by the same values defined at the Filament level // (then the key is further prefixed with the "filament_" prefix). @@ -283,9 +299,10 @@ public: private: void init_common_params(); void init_fff_params(); - void init_extruder_retract_keys(); + void init_extruder_option_keys(); void init_sla_params(); + std::vector m_extruder_option_keys; std::vector m_extruder_retract_keys; }; @@ -293,6 +310,8 @@ private: // This definition is constant. extern const PrintConfigDef print_config_def; +class StaticPrintConfig; + // Slic3r dynamic configuration, used to override the configuration // per object, per modification volume or per printing material. // The dynamic configuration is also used to store user modifications of the print global parameters, @@ -303,9 +322,11 @@ class DynamicPrintConfig : public DynamicConfig { public: DynamicPrintConfig() {} - DynamicPrintConfig(const DynamicPrintConfig &other) : DynamicConfig(other) {} + DynamicPrintConfig(const DynamicPrintConfig &rhs) : DynamicConfig(rhs) {} + explicit DynamicPrintConfig(const StaticPrintConfig &rhs); + explicit DynamicPrintConfig(const ConfigBase &rhs) : DynamicConfig(rhs) {} - static DynamicPrintConfig* new_from_defaults(); + static DynamicPrintConfig full_print_config(); static DynamicPrintConfig* new_from_defaults_keys(const std::vector &keys); // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. @@ -313,6 +334,8 @@ public: void normalize(); + void set_num_extruders(unsigned int num_extruders); + // Validate the PrintConfig. Returns an empty string on success, otherwise an error message is returned. std::string validate(); @@ -339,6 +362,8 @@ public: // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. const ConfigDef* def() const override { return &print_config_def; } + // Reference to the cached list of keys. + virtual const t_config_option_keys& keys_ref() const = 0; protected: // Verify whether the opt_key has not been obsoleted or renamed. @@ -427,6 +452,7 @@ public: \ { return s_cache_##CLASS_NAME.optptr(opt_key, this); } \ /* Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store. */ \ t_config_option_keys keys() const override { return s_cache_##CLASS_NAME.keys(); } \ + const t_config_option_keys& keys_ref() const override { return s_cache_##CLASS_NAME.keys(); } \ static const CLASS_NAME& defaults() { initialize_cache(); return s_cache_##CLASS_NAME.defaults(); } \ private: \ static void initialize_cache() \ @@ -830,6 +856,7 @@ public: ConfigOptionString start_gcode; ConfigOptionBool single_extruder_multi_material; ConfigOptionBool single_extruder_multi_material_priming; + ConfigOptionBool wipe_tower_no_sparse_layers; ConfigOptionString toolchange_gcode; ConfigOptionFloat travel_speed; ConfigOptionBool use_firmware_retraction; @@ -920,6 +947,7 @@ protected: OPT_PTR(retract_speed); OPT_PTR(single_extruder_multi_material); OPT_PTR(single_extruder_multi_material_priming); + OPT_PTR(wipe_tower_no_sparse_layers); OPT_PTR(start_gcode); OPT_PTR(start_filament_gcode); OPT_PTR(toolchange_gcode); @@ -1241,6 +1269,9 @@ public: // The height of the pad from the bottom to the top not considering the pit ConfigOptionFloat pad_wall_height /*= 5*/; + // How far should the pad extend around the contained geometry + ConfigOptionFloat pad_brim_size; + // The greatest distance where two individual pads are merged into one. The // distance is measured roughly from the centroids of the pads. ConfigOptionFloat pad_max_merge_distance /*= 50*/; @@ -1262,6 +1293,8 @@ public: // Disable the elevation (ignore its value) and use the zero elevation mode ConfigOptionBool pad_around_object; + ConfigOptionBool pad_around_object_everywhere; + // This is the gap between the object bottom and the generated pad ConfigOptionFloat pad_object_gap; @@ -1300,10 +1333,12 @@ protected: OPT_PTR(pad_enable); OPT_PTR(pad_wall_thickness); OPT_PTR(pad_wall_height); + OPT_PTR(pad_brim_size); OPT_PTR(pad_max_merge_distance); // OPT_PTR(pad_edge_radius); OPT_PTR(pad_wall_slope); OPT_PTR(pad_around_object); + OPT_PTR(pad_around_object_everywhere); OPT_PTR(pad_object_gap); OPT_PTR(pad_object_connector_stride); OPT_PTR(pad_object_connector_width); @@ -1316,6 +1351,10 @@ class SLAMaterialConfig : public StaticPrintConfig STATIC_PRINT_CONFIG_CACHE(SLAMaterialConfig) public: ConfigOptionFloat initial_layer_height; + ConfigOptionFloat bottle_cost; + ConfigOptionFloat bottle_volume; + ConfigOptionFloat bottle_weight; + ConfigOptionFloat material_density; ConfigOptionFloat exposure_time; ConfigOptionFloat initial_exposure_time; ConfigOptionFloats material_correction; @@ -1323,6 +1362,10 @@ protected: void initialize(StaticCacheBase &cache, const char *base_ptr) { OPT_PTR(initial_layer_height); + OPT_PTR(bottle_cost); + OPT_PTR(bottle_volume); + OPT_PTR(bottle_weight); + OPT_PTR(material_density); OPT_PTR(exposure_time); OPT_PTR(initial_exposure_time); OPT_PTR(material_correction); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 8717aaf20..6756f092f 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1,6 +1,7 @@ #include "Print.hpp" #include "BoundingBox.hpp" #include "ClipperUtils.hpp" +#include "ElephantFootCompensation.hpp" #include "Geometry.hpp" #include "I18N.hpp" #include "SupportMaterial.hpp" @@ -12,7 +13,6 @@ #include #include -#include #include #include @@ -75,13 +75,9 @@ PrintBase::ApplyStatus PrintObject::set_copies(const Points &points) { // Order copies with a nearest-neighbor search. std::vector copies; - { - std::vector ordered_copies; - Slic3r::Geometry::chained_path(points, ordered_copies); - copies.reserve(ordered_copies.size()); - for (size_t point_idx : ordered_copies) - copies.emplace_back(points[point_idx] + m_copies_shift); - } + copies.reserve(points.size()); + for (const Point &pt : points) + copies.emplace_back(pt + m_copies_shift); // Invalidate and set copies. PrintBase::ApplyStatus status = PrintBase::APPLY_STATUS_UNCHANGED; if (copies != m_copies) { @@ -122,8 +118,23 @@ void PrintObject::slice() // Simplify slices if required. if (m_print->config().resolution) this->_simplify_slices(scale_(this->print()->config().resolution)); + //create polyholes this->_transform_hole_to_polyholes(); + + // Update bounding boxes + tbb::parallel_for( + tbb::blocked_range(0, m_layers.size()), + [this](const tbb::blocked_range& range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { + m_print->throw_if_canceled(); + Layer &layer = *m_layers[layer_idx]; + layer.slices_bboxes.clear(); + layer.slices_bboxes.reserve(layer.slices.size()); + for (const ExPolygon &expoly : layer.slices) + layer.slices_bboxes.emplace_back(get_extents(expoly)); + } + }); if (m_layers.empty()) throw std::runtime_error("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"); this->set_done(posSlice); @@ -230,7 +241,7 @@ void PrintObject::_transform_hole_to_polyholes() polyhole.make_clockwise(); for (auto &poly_to_replace : entry.second) { //search the clone in layers->slices - for (ExPolygon &explo_slice : m_layers[poly_to_replace.second]->slices.expolygons) { + for (ExPolygon &explo_slice : m_layers[poly_to_replace.second]->slices) { for (Polygon &poly_slice : explo_slice.holes) { if (poly_slice.points == poly_to_replace.first->points) { poly_slice.points = polyhole.points; @@ -1085,7 +1096,7 @@ void PrintObject::detect_surfaces_type() offset2_ex(diff(layerm_slices_surfaces, lower_slices, true), -offset, offset), surface_type_bottom_other); #else - ExPolygons lower_slices = lower_layer->slices.expolygons; + ExPolygons lower_slices = lower_layer->slices; //if we added new surfaces, we can use them as support /*if (layerm->region()->config().no_perimeter_full_bridge) { lower_slices = union_ex(lower_slices, lower_layer->get_region(idx_region)->fill_surfaces); @@ -1253,7 +1264,7 @@ void PrintObject::process_external_surfaces() // Shrink the holes, let the layer above expand slightly inside the unsupported areas. polygons_append(voids, offset(surface.expolygon, unsupported_width)); } - surfaces_covered[layer_idx] = diff(to_polygons(this->m_layers[layer_idx]->slices.expolygons), voids); + surfaces_covered[layer_idx] = diff(to_polygons(this->m_layers[layer_idx]->slices), voids); } } ); @@ -1363,8 +1374,8 @@ void PrintObject::discover_vertical_shells() polygons_append(cache.holes, offset(offset_ex(layer.slices, 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.expolygons)); - svg.draw(layer.slices.expolygons, "blue"); + Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.slices)); + svg.draw(layer.slices, "blue"); svg.draw(union_ex(cache.holes), "red"); svg.draw_outline(union_ex(cache.holes), "black", "blue", scale_(0.05)); svg.Close(); @@ -2116,7 +2127,8 @@ void PrintObject::_slice(const std::vector &layer_height_profile) // Trim volumes in a single layer, one by the other, possibly apply upscaling. { Polygons processed; - for (SlicedVolume &sliced_volume : sliced_volumes) { + for (SlicedVolume &sliced_volume : sliced_volumes) + if (! sliced_volume.expolygons_by_layer.empty()) { ExPolygons slices = std::move(sliced_volume.expolygons_by_layer[layer_id]); if (upscale) slices = offset_ex(std::move(slices), delta); @@ -2134,7 +2146,7 @@ void PrintObject::_slice(const std::vector &layer_height_profile) ExPolygons expolygons; size_t num_volumes = 0; for (SlicedVolume &sliced_volume : sliced_volumes) - if (sliced_volume.region_id == region_id && ! sliced_volume.expolygons_by_layer[layer_id].empty()) { + if (sliced_volume.region_id == region_id && ! sliced_volume.expolygons_by_layer.empty() && ! sliced_volume.expolygons_by_layer[layer_id].empty()) { ++ num_volumes; append(expolygons, std::move(sliced_volume.expolygons_by_layer[layer_id])); } @@ -2213,8 +2225,10 @@ end: // Apply size compensation and perform clipping of multi-part objects. float delta = float(scale_(m_config.xy_size_compensation.value)); float hole_delta = float(scale_(this->config().hole_size_compensation.value)); + //FIXME only apply the compensation if no raft is enabled. float elephant_foot_compensation = 0.f; - if (layer_id == 0) + if (layer_id == 0 && m_config.raft_layers == 0) + // Only enable Elephant foot compensation if printing directly on the print bed. elephant_foot_compensation = float(scale_(m_config.elefant_foot_compensation.value)); if (layer->m_regions.size() == 1) { // Optimized version for a single region layer. @@ -2240,19 +2254,8 @@ end: to_expolygons(std::move(layerm->slices().surfaces)) : offset_ex(to_expolygons(std::move(layerm->slices().surfaces)), delta); // Apply the elephant foot compensation. - if (elephant_foot_compensation != 0) { - float elephant_foot_spacing = float(layerm->flow(frExternalPerimeter).scaled_elephant_foot_spacing()); - float external_perimeter_nozzle = float(scale_(this->print()->config().nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder.value - 1))); - // Apply the elephant foot compensation by steps of 1/10 nozzle diameter. - float steps = std::ceil(elephant_foot_compensation / (0.1f * external_perimeter_nozzle)); - size_t nsteps = size_t(std::abs(steps)); - float step = elephant_foot_compensation / steps; - for (size_t i = 0; i < nsteps; ++ i) { - Polygons tmp = offset(expolygons, - step); - append(tmp, diff(to_polygons(expolygons), offset(offset_ex(expolygons, -elephant_foot_spacing + step), elephant_foot_spacing - step))); - expolygons = union_ex(tmp); - } - } + if (elephant_foot_compensation > 0) + expolygons = union_ex(Slic3r::elephant_foot_compensation(expolygons, layerm->flow(frExternalPerimeter), unscale(elephant_foot_compensation))); layerm->m_slices.set(std::move(expolygons), stPosInternal | stDensSparse); } _offset_holes(hole_delta, layer->regions().front()); @@ -2278,34 +2281,19 @@ end: layerm->m_slices.set(std::move(slices), stPosInternal | stDensSparse); } } - if (delta < 0.f) { + if (delta < 0.f || elephant_foot_compensation > 0.f) { // Apply the negative XY compensation. - Polygons trimming = offset(layer->merged(double(EPSILON)), delta - double(EPSILON)); + Polygons trimming; + static const float eps = float(scale_(m_config.slice_closing_radius.value) * 1.5); + if (elephant_foot_compensation > 0.f) { + trimming = to_polygons(Slic3r::elephant_foot_compensation(offset_ex(layer->merged(eps), std::min(delta, 0.f) - eps), + layer->m_regions.front()->flow(frExternalPerimeter), unscale(elephant_foot_compensation))); + } else + trimming = offset(layer->merged(float(SCALED_EPSILON)), delta - float(SCALED_EPSILON)); for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) layer->m_regions[region_id]->trim_surfaces(trimming); } - if (elephant_foot_compensation != 0.f) { - // Apply the elephant foot compensation. - std::vector elephant_foot_spacing; - elephant_foot_spacing.reserve(layer->m_regions.size()); - float external_perimeter_nozzle = 0.f; - for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) { - LayerRegion *layerm = layer->m_regions[region_id]; - elephant_foot_spacing.emplace_back(float(layerm->flow(frExternalPerimeter).scaled_elephant_foot_spacing())); - external_perimeter_nozzle += float(scale_(this->print()->config().nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder.value - 1))); } - external_perimeter_nozzle /= (float)layer->m_regions.size(); - // Apply the elephant foot compensation by steps of 1/10 nozzle diameter. - float steps = std::ceil(elephant_foot_compensation / (0.1f * external_perimeter_nozzle)); - size_t nsteps = size_t(std::abs(steps)); - float step = elephant_foot_compensation / steps; - for (size_t i = 0; i < nsteps; ++ i) { - Polygons trimming_polygons = offset(layer->merged(float(EPSILON)), double(- step - EPSILON)); - for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) - layer->m_regions[region_id]->elephant_foot_compensation_step(elephant_foot_spacing[region_id] - step, trimming_polygons); - } - } - } // Merge all regions' slices to get islands, chain them by a shortest path. layer->make_slices(); } @@ -2756,7 +2744,7 @@ std::string PrintObject::_fix_slicing_errors() BOOST_LOG_TRIVIAL(debug) << "Slicing objects - fixing slicing errors in parallel - end"; // remove empty layers from bottom - while (! m_layers.empty() && m_layers.front()->slices.expolygons.empty()) { + while (! m_layers.empty() && m_layers.front()->slices.empty()) { delete m_layers.front(); m_layers.erase(m_layers.begin()); m_layers.front()->lower_layer = nullptr; @@ -2783,13 +2771,17 @@ void PrintObject::_simplify_slices(double distance) Layer *layer = m_layers[layer_idx]; for (size_t region_idx = 0; region_idx < layer->m_regions.size(); ++ region_idx) layer->m_regions[region_idx]->m_slices.simplify(distance); - layer->slices.simplify(distance); + { + ExPolygons simplified; + for (const ExPolygon& expoly : layer->slices) + expoly.simplify(distance, &simplified); + layer->slices = std::move(simplified); + } } }); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - siplifying slices in parallel - end"; } - // Only active if config->infill_only_where_needed. This step trims the sparse infill, // so it acts as an internal support. It maintains all other infill types intact. // Here the internal surfaces and perimeters have to be supported by the sparse infill. @@ -2815,7 +2807,7 @@ void PrintObject::clip_fill_surfaces() // Detect things that we need to support. // Cummulative slices. Polygons slices; - polygons_append(slices, layer->slices.expolygons); + polygons_append(slices, layer->slices); // Cummulative fill surfaces. Polygons fill_surfaces; // Solid surfaces to be supported. diff --git a/src/libslic3r/SLA/ConcaveHull.cpp b/src/libslic3r/SLA/ConcaveHull.cpp new file mode 100644 index 000000000..dff061721 --- /dev/null +++ b/src/libslic3r/SLA/ConcaveHull.cpp @@ -0,0 +1,171 @@ +#include "ConcaveHull.hpp" +#include +#include +#include "SLASpatIndex.hpp" +#include + +namespace Slic3r { +namespace sla { + +inline Vec3d to_vec3(const Vec2crd &v2) { return {double(v2(X)), double(v2(Y)), 0.}; } +inline Vec3d to_vec3(const Vec2d &v2) { return {v2(X), v2(Y), 0.}; } +inline Vec2crd to_vec2(const Vec3d &v3) { return {coord_t(v3(X)), coord_t(v3(Y))}; } + +Point ConcaveHull::centroid(const Points &pp) +{ + Point c; + switch(pp.size()) { + case 0: break; + case 1: c = pp.front(); break; + case 2: c = (pp[0] + pp[1]) / 2; break; + default: { + auto MAX = std::numeric_limits::max(); + auto MIN = std::numeric_limits::min(); + Point min = {MAX, MAX}, max = {MIN, MIN}; + + for(auto& p : pp) { + if(p(0) < min(0)) min(0) = p(0); + if(p(1) < min(1)) min(1) = p(1); + if(p(0) > max(0)) max(0) = p(0); + if(p(1) > max(1)) max(1) = p(1); + } + c(0) = min(0) + (max(0) - min(0)) / 2; + c(1) = min(1) + (max(1) - min(1)) / 2; + break; + } + } + + return c; +} + +// 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) +{ + using ClipperLib::ClipperOffset; + using ClipperLib::etClosedPolygon; + using ClipperLib::Paths; + using ClipperLib::Path; + + ClipperOffset offs; + offs.ArcTolerance = scaled(0.01); + + for (auto &p : paths) + // If the input is not at least a triangle, we can not do this algorithm + if(p.size() < 3) { + BOOST_LOG_TRIVIAL(error) << "Invalid geometry for offsetting!"; + return {}; + } + + offs.AddPaths(paths, jointype, etClosedPolygon); + + Paths result; + offs.Execute(result, static_cast(delta)); + + return result; +} + +Points ConcaveHull::calculate_centroids() const +{ + // We get the centroids of all the islands in the 2D slice + 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); }); + + return centroids; +} + +void ConcaveHull::merge_polygons() { m_polys = get_contours(union_ex(m_polys)); } + +void ConcaveHull::add_connector_rectangles(const Points ¢roids, + coord_t max_dist, + ThrowOnCancel thr) +{ + // Centroid of the centroids of islands. This is where the additional + // connector sticks are routed. + Point cc = centroid(centroids); + + PointIndex ctrindex; + unsigned idx = 0; + for(const Point &ct : centroids) ctrindex.insert(to_vec3(ct), idx++); + + m_polys.reserve(m_polys.size() + centroids.size()); + + idx = 0; + for (const Point &c : centroids) { + thr(); + + double dx = c.x() - cc.x(), dy = c.y() - cc.y(); + double l = std::sqrt(dx * dx + dy * dy); + double nx = dx / l, ny = dy / l; + + const Point &ct = centroids[idx]; + + std::vector result = ctrindex.nearest(to_vec3(ct), 2); + + double dist = max_dist; + for (const PointIndexEl &el : result) + if (el.second != idx) { + dist = Line(to_vec2(el.first), ct).length(); + break; + } + + idx++; + + if (dist >= max_dist) return; + + Polygon r; + r.points.reserve(3); + r.points.emplace_back(cc); + + Point n(scaled(nx), scaled(ny)); + r.points.emplace_back(c + Point(n.y(), -n.x())); + r.points.emplace_back(c + Point(-n.y(), n.x())); + offset(r, scaled(1.)); + + m_polys.emplace_back(r); + } +} + +ConcaveHull::ConcaveHull(const Polygons &polys, double mergedist, ThrowOnCancel thr) +{ + if(polys.empty()) return; + + m_polys = polys; + merge_polygons(); + + if(m_polys.size() == 1) return; + + Points centroids = calculate_centroids(); + + add_connector_rectangles(centroids, scaled(mergedist), thr); + + merge_polygons(); +} + +ExPolygons ConcaveHull::to_expolygons() const +{ + auto ret = reserve_vector(m_polys.size()); + for (const Polygon &p : m_polys) ret.emplace_back(ExPolygon(p)); + return ret; +} + +ExPolygons offset_waffle_style_ex(const ConcaveHull &hull, coord_t delta) +{ + ClipperLib::Paths paths = Slic3rMultiPoints_to_ClipperPaths(hull.polygons()); + paths = fast_offset(paths, 2 * delta, ClipperLib::jtRound); + paths = fast_offset(paths, -delta, ClipperLib::jtRound); + ExPolygons ret = ClipperPaths_to_Slic3rExPolygons(paths); + for (ExPolygon &p : ret) p.holes = {}; + return ret; +} + +Polygons offset_waffle_style(const ConcaveHull &hull, coord_t delta) +{ + return to_polygons(offset_waffle_style_ex(hull, delta)); +} + +}} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/ConcaveHull.hpp b/src/libslic3r/SLA/ConcaveHull.hpp new file mode 100644 index 000000000..94e16d77c --- /dev/null +++ b/src/libslic3r/SLA/ConcaveHull.hpp @@ -0,0 +1,53 @@ +#ifndef CONCAVEHULL_HPP +#define CONCAVEHULL_HPP + +#include + +namespace Slic3r { +namespace sla { + +inline Polygons get_contours(const ExPolygons &poly) +{ + Polygons ret; ret.reserve(poly.size()); + for (const ExPolygon &p : poly) ret.emplace_back(p.contour); + + return ret; +} + +using ThrowOnCancel = std::function; + +/// A fake concave hull that is constructed by connecting separate shapes +/// with explicit bridges. Bridges are generated from each shape's centroid +/// to the center of the "scene" which is the centroid calculated from the shape +/// centroids (a star is created...) +class ConcaveHull { + Polygons m_polys; + + static Point centroid(const Points& pp); + + static inline Point centroid(const Polygon &poly) { return poly.centroid(); } + + Points calculate_centroids() const; + + void merge_polygons(); + + void add_connector_rectangles(const Points ¢roids, + coord_t max_dist, + ThrowOnCancel thr); +public: + + ConcaveHull(const ExPolygons& polys, double merge_dist, ThrowOnCancel thr) + : ConcaveHull{to_polygons(polys), merge_dist, thr} {} + + ConcaveHull(const Polygons& polys, double mergedist, ThrowOnCancel thr); + + const Polygons & polygons() const { return m_polys; } + + ExPolygons to_expolygons() const; +}; + +ExPolygons offset_waffle_style_ex(const ConcaveHull &ccvhull, coord_t delta); +Polygons offset_waffle_style(const ConcaveHull &polys, coord_t delta); + +}} // namespace Slic3r::sla +#endif // CONCAVEHULL_HPP diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index 5451c7612..d8292e77d 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -16,6 +16,7 @@ #include namespace Slic3r { +namespace sla { /*float SLAAutoSupports::approximate_geodesic_distance(const Vec3d& p1, const Vec3d& p2, Vec3d& n1, Vec3d& n2) { @@ -48,9 +49,16 @@ float SLAAutoSupports::distance_limit(float angle) const return 1./(2.4*get_required_density(angle)); }*/ -SLAAutoSupports::SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector& slices, const std::vector& heights, - const Config& config, std::function throw_on_cancel, std::function statusfn) -: m_config(config), m_emesh(emesh), m_throw_on_cancel(throw_on_cancel), m_statusfn(statusfn) +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) + : m_config(config) + , m_emesh(emesh) + , m_throw_on_cancel(throw_on_cancel) + , m_statusfn(statusfn) { process(slices, heights); project_onto_mesh(m_output); @@ -505,6 +513,21 @@ void SLAAutoSupports::uniformly_cover(const ExPolygons& islands, Structure& stru } } +void remove_bottom_points(std::vector &pts, double gnd_lvl, double tolerance) +{ + // get iterator to the reorganized vector end + auto endit = + std::remove_if(pts.begin(), pts.end(), + [tolerance, gnd_lvl](const sla::SupportPoint &sp) { + double diff = std::abs(gnd_lvl - + double(sp.pos(Z))); + return diff <= tolerance; + }); + + // erase all elements after the new end + pts.erase(endit, pts.end()); +} + #ifdef SLA_AUTOSUPPORTS_DEBUG void SLAAutoSupports::output_structures(const std::vector& structures) { @@ -533,4 +556,5 @@ void SLAAutoSupports::output_expolygons(const ExPolygons& expolys, const std::st } #endif +} // namespace sla } // namespace Slic3r diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SLAAutoSupports.hpp index a2ac5f804..76bfcd725 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.hpp +++ b/src/libslic3r/SLA/SLAAutoSupports.hpp @@ -11,20 +11,22 @@ // #define SLA_AUTOSUPPORTS_DEBUG namespace Slic3r { +namespace sla { class SLAAutoSupports { public: struct Config { - float density_relative; - float minimal_distance; - float head_diameter; + 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 TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector& slices, + SLAAutoSupports(const sla::EigenMesh3D& emesh, const std::vector& slices, const std::vector& heights, const Config& config, std::function throw_on_cancel, std::function statusfn); + const std::vector& output() { return m_output; } struct MyLayer; @@ -199,7 +201,9 @@ private: std::function m_statusfn; }; +void remove_bottom_points(std::vector &pts, double gnd_lvl, double tolerance); +} // namespace sla } // namespace Slic3r diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp deleted file mode 100644 index c337a5ac5..000000000 --- a/src/libslic3r/SLA/SLABasePool.cpp +++ /dev/null @@ -1,922 +0,0 @@ -#include "SLABasePool.hpp" -#include "SLABoilerPlate.hpp" - -#include "boost/log/trivial.hpp" -#include "SLABoostAdapter.hpp" -#include "ClipperUtils.hpp" -#include "Tesselate.hpp" -#include "MTUtils.hpp" - -// For debugging: -// #include -// #include -// #include "SVG.hpp" - -namespace Slic3r { namespace sla { - -/// This function will return a triangulation of a sheet connecting an upper -/// and a lower plate given as input polygons. It will not triangulate the -/// plates themselves only the sheet. The caller has to specify the lower and -/// upper z levels in world coordinates as well as the offset difference -/// between the sheets. If the lower_z_mm is higher than upper_z_mm or the -/// offset difference is negative, the resulting triangle orientation will be -/// reversed. -/// -/// IMPORTANT: This is not a universal triangulation algorithm. It assumes -/// that the lower and upper polygons are offsetted versions of the same -/// original polygon. In general, it assumes that one of the polygons is -/// completely inside the other. The offset difference is the reference -/// distance from the inner polygon's perimeter to the outer polygon's -/// perimeter. The real distance will be variable as the clipper offset has -/// different strategies (rounding, etc...). This algorithm should have -/// O(2n + 3m) complexity where n is the number of upper vertices and m is the -/// number of lower vertices. -Contour3D walls(const Polygon& lower, const Polygon& upper, - double lower_z_mm, double upper_z_mm, - double offset_difference_mm, ThrowOnCancel thr) -{ - Contour3D ret; - - if(upper.points.size() < 3 || lower.size() < 3) return ret; - - // The concept of the algorithm is relatively simple. It will try to find - // the closest vertices from the upper and the lower polygon and use those - // as starting points. Then it will create the triangles sequentially using - // an edge from the upper polygon and a vertex from the lower or vice versa, - // depending on the resulting triangle's quality. - // The quality is measured by a scalar value. So far it looks like it is - // enough to derive it from the slope of the triangle's two edges connecting - // the upper and the lower part. A reference slope is calculated from the - // height and the offset difference. - - // Offset in the index array for the ceiling - const auto offs = upper.points.size(); - - // Shorthand for the vertex arrays - auto& upoints = upper.points, &lpoints = lower.points; - auto& rpts = ret.points; auto& ind = ret.indices; - - // If the Z levels are flipped, or the offset difference is negative, we - // will interpret that as the triangles normals should be inverted. - bool inverted = upper_z_mm < lower_z_mm || offset_difference_mm < 0; - - // Copy the points into the mesh, convert them from 2D to 3D - rpts.reserve(upoints.size() + lpoints.size()); - ind.reserve(2 * upoints.size() + 2 * lpoints.size()); - for (auto &p : upoints) - rpts.emplace_back(unscaled(p.x()), unscaled(p.y()), upper_z_mm); - for (auto &p : lpoints) - rpts.emplace_back(unscaled(p.x()), unscaled(p.y()), lower_z_mm); - - // Create pointing indices into vertex arrays. u-upper, l-lower - size_t uidx = 0, lidx = offs, unextidx = 1, lnextidx = offs + 1; - - // Simple squared distance calculation. - auto distfn = [](const Vec3d& p1, const Vec3d& p2) { - auto p = p1 - p2; return p.transpose() * p; - }; - - // We need to find the closest point on lower polygon to the first point on - // the upper polygon. These will be our starting points. - double distmin = std::numeric_limits::max(); - for(size_t l = lidx; l < rpts.size(); ++l) { - thr(); - double d = distfn(rpts[l], rpts[uidx]); - if(d < distmin) { lidx = l; distmin = d; } - } - - // Set up lnextidx to be ahead of lidx in cyclic mode - lnextidx = lidx + 1; - if(lnextidx == rpts.size()) lnextidx = offs; - - // This will be the flip switch to toggle between upper and lower triangle - // creation mode - enum class Proceed { - UPPER, // A segment from the upper polygon and one vertex from the lower - LOWER // A segment from the lower polygon and one vertex from the upper - } proceed = Proceed::UPPER; - - // Flags to help evaluating loop termination. - bool ustarted = false, lstarted = false; - - // The variables for the fitness values, one for the actual and one for the - // previous. - double current_fit = 0, prev_fit = 0; - - // Every triangle of the wall has two edges connecting the upper plate with - // the lower plate. From the length of these two edges and the zdiff we - // can calculate the momentary squared offset distance at a particular - // position on the wall. The average of the differences from the reference - // (squared) offset distance will give us the driving fitness value. - const double offsdiff2 = std::pow(offset_difference_mm, 2); - const double zdiff2 = std::pow(upper_z_mm - lower_z_mm, 2); - - // Mark the current vertex iterator positions. If the iterators return to - // the same position, the loop can be terminated. - size_t uendidx = uidx, lendidx = lidx; - - do { thr(); // check throw if canceled - - prev_fit = current_fit; - - switch(proceed) { // proceed depending on the current state - case Proceed::UPPER: - if(!ustarted || uidx != uendidx) { // there are vertices remaining - // Get the 3D vertices in order - const Vec3d& p_up1 = rpts[uidx]; - const Vec3d& p_low = rpts[lidx]; - const Vec3d& p_up2 = rpts[unextidx]; - - // Calculate fitness: the average of the two connecting edges - double a = offsdiff2 - (distfn(p_up1, p_low) - zdiff2); - double b = offsdiff2 - (distfn(p_up2, p_low) - zdiff2); - current_fit = (std::abs(a) + std::abs(b)) / 2; - - if(current_fit > prev_fit) { // fit is worse than previously - proceed = Proceed::LOWER; - } else { // good to go, create the triangle - inverted - ? ind.emplace_back(int(unextidx), int(lidx), int(uidx)) - : ind.emplace_back(int(uidx), int(lidx), int(unextidx)); - - // Increment the iterators, rotate if necessary - ++uidx; ++unextidx; - if(unextidx == offs) unextidx = 0; - if(uidx == offs) uidx = 0; - - ustarted = true; // mark the movement of the iterators - // so that the comparison to uendidx can be made correctly - } - } else proceed = Proceed::LOWER; - - break; - case Proceed::LOWER: - // Mode with lower segment, upper vertex. Same structure: - if(!lstarted || lidx != lendidx) { - const Vec3d& p_low1 = rpts[lidx]; - const Vec3d& p_low2 = rpts[lnextidx]; - const Vec3d& p_up = rpts[uidx]; - - double a = offsdiff2 - (distfn(p_up, p_low1) - zdiff2); - double b = offsdiff2 - (distfn(p_up, p_low2) - zdiff2); - current_fit = (std::abs(a) + std::abs(b)) / 2; - - if(current_fit > prev_fit) { - proceed = Proceed::UPPER; - } else { - inverted - ? ind.emplace_back(int(uidx), int(lnextidx), int(lidx)) - : ind.emplace_back(int(lidx), int(lnextidx), int(uidx)); - - ++lidx; ++lnextidx; - if(lnextidx == rpts.size()) lnextidx = offs; - if(lidx == rpts.size()) lidx = offs; - - lstarted = true; - } - } else proceed = Proceed::UPPER; - - break; - } // end of switch - } while(!ustarted || !lstarted || uidx != uendidx || lidx != lendidx); - - return ret; -} - -/// Offsetting with clipper and smoothing the edges into a curvature. -void offset(ExPolygon& sh, coord_t distance, bool edgerounding = true) { - using ClipperLib::ClipperOffset; - using ClipperLib::jtRound; - using ClipperLib::jtMiter; - using ClipperLib::etClosedPolygon; - using ClipperLib::Paths; - using ClipperLib::Path; - - auto&& ctour = Slic3rMultiPoint_to_ClipperPath(sh.contour); - auto&& holes = Slic3rMultiPoints_to_ClipperPaths(sh.holes); - - // If the input is not at least a triangle, we can not do this algorithm - if(ctour.size() < 3 || - std::any_of(holes.begin(), holes.end(), - [](const Path& p) { return p.size() < 3; }) - ) { - BOOST_LOG_TRIVIAL(error) << "Invalid geometry for offsetting!"; - return; - } - - auto jointype = edgerounding? jtRound : jtMiter; - - ClipperOffset offs; - offs.ArcTolerance = scaled(0.01); - Paths result; - offs.AddPath(ctour, jointype, etClosedPolygon); - offs.AddPaths(holes, jointype, etClosedPolygon); - offs.Execute(result, static_cast(distance)); - - // Offsetting reverts the orientation and also removes the last vertex - // so boost will not have a closed polygon. - - bool found_the_contour = false; - sh.holes.clear(); - for(auto& r : result) { - if(ClipperLib::Orientation(r)) { - // We don't like if the offsetting generates more than one contour - // but throwing would be an overkill. Instead, we should warn the - // caller about the inability to create correct geometries - if(!found_the_contour) { - auto rr = ClipperPath_to_Slic3rPolygon(r); - sh.contour.points.swap(rr.points); - found_the_contour = true; - } else { - BOOST_LOG_TRIVIAL(warning) - << "Warning: offsetting result is invalid!"; - } - } else { - // TODO If there are multiple contours we can't be sure which hole - // belongs to the first contour. (But in this case the situation is - // bad enough to let it go...) - sh.holes.emplace_back(ClipperPath_to_Slic3rPolygon(r)); - } - } -} - -void offset(Polygon &sh, coord_t distance, bool edgerounding = true) -{ - using ClipperLib::ClipperOffset; - using ClipperLib::jtRound; - using ClipperLib::jtMiter; - using ClipperLib::etClosedPolygon; - using ClipperLib::Paths; - using ClipperLib::Path; - - auto &&ctour = Slic3rMultiPoint_to_ClipperPath(sh); - - // If the input is not at least a triangle, we can not do this algorithm - if (ctour.size() < 3) { - BOOST_LOG_TRIVIAL(error) << "Invalid geometry for offsetting!"; - return; - } - - ClipperOffset offs; - offs.ArcTolerance = 0.01 * scaled(1.); - Paths result; - offs.AddPath(ctour, edgerounding ? jtRound : jtMiter, etClosedPolygon); - offs.Execute(result, static_cast(distance)); - - // Offsetting reverts the orientation and also removes the last vertex - // so boost will not have a closed polygon. - - bool found_the_contour = false; - for (auto &r : result) { - if (ClipperLib::Orientation(r)) { - // We don't like if the offsetting generates more than one contour - // but throwing would be an overkill. Instead, we should warn the - // caller about the inability to create correct geometries - if (!found_the_contour) { - auto rr = ClipperPath_to_Slic3rPolygon(r); - sh.points.swap(rr.points); - found_the_contour = true; - } else { - BOOST_LOG_TRIVIAL(warning) - << "Warning: offsetting result is invalid!"; - } - } - } -} - -/// Unification of polygons (with clipper) preserving holes as well. -ExPolygons unify(const ExPolygons& shapes) { - using ClipperLib::ptSubject; - - ExPolygons retv; - - bool closed = true; - bool valid = true; - - ClipperLib::Clipper clipper; - - for(auto& path : shapes) { - auto clipperpath = Slic3rMultiPoint_to_ClipperPath(path.contour); - - if(!clipperpath.empty()) - valid &= clipper.AddPath(clipperpath, ptSubject, closed); - - auto clipperholes = Slic3rMultiPoints_to_ClipperPaths(path.holes); - - for(auto& hole : clipperholes) { - if(!hole.empty()) - valid &= clipper.AddPath(hole, ptSubject, closed); - } - } - - if(!valid) BOOST_LOG_TRIVIAL(warning) << "Unification of invalid shapes!"; - - ClipperLib::PolyTree result; - clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNonZero); - - retv.reserve(static_cast(result.Total())); - - // Now we will recursively traverse the polygon tree and serialize it - // into an ExPolygon with holes. The polygon tree has the clipper-ish - // PolyTree structure which alternates its nodes as contours and holes - - // A "declaration" of function for traversing leafs which are holes - std::function processHole; - - // Process polygon which calls processHoles which than calls processPoly - // again until no leafs are left. - auto processPoly = [&retv, &processHole](ClipperLib::PolyNode *pptr) { - ExPolygon poly; - poly.contour.points = ClipperPath_to_Slic3rPolygon(pptr->Contour); - for(auto h : pptr->Childs) { processHole(h, poly); } - retv.push_back(poly); - }; - - // Body of the processHole function - processHole = [&processPoly](ClipperLib::PolyNode *pptr, ExPolygon& poly) - { - poly.holes.emplace_back(); - poly.holes.back().points = ClipperPath_to_Slic3rPolygon(pptr->Contour); - for(auto c : pptr->Childs) processPoly(c); - }; - - // Wrapper for traversing. - auto traverse = [&processPoly] (ClipperLib::PolyNode *node) - { - for(auto ch : node->Childs) { - processPoly(ch); - } - }; - - // Here is the actual traverse - traverse(&result); - - return retv; -} - -Polygons unify(const Polygons& shapes) { - using ClipperLib::ptSubject; - - bool closed = true; - bool valid = true; - - ClipperLib::Clipper clipper; - - for(auto& path : shapes) { - auto clipperpath = Slic3rMultiPoint_to_ClipperPath(path); - - if(!clipperpath.empty()) - valid &= clipper.AddPath(clipperpath, ptSubject, closed); - } - - if(!valid) BOOST_LOG_TRIVIAL(warning) << "Unification of invalid shapes!"; - - ClipperLib::Paths result; - clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNonZero); - - Polygons ret; - for (ClipperLib::Path &p : result) { - Polygon pp = ClipperPath_to_Slic3rPolygon(p); - if (!pp.is_clockwise()) ret.emplace_back(std::move(pp)); - } - - return ret; -} - -// Function to cut tiny connector cavities for a given polygon. The input poly -// will be offsetted by "padding" and small rectangle shaped cavities will be -// inserted along the perimeter in every "stride" distance. The stick rectangles -// will have a with about "stick_width". The input dimensions are in world -// measure, not the scaled clipper units. -void breakstick_holes(ExPolygon& poly, - double padding, - double stride, - double stick_width, - double penetration) -{ - // SVG svg("bridgestick_plate.svg"); - // svg.draw(poly); - - auto transf = [stick_width, penetration, padding, stride](Points &pts) { - // The connector stick will be a small rectangle with dimensions - // stick_width x (penetration + padding) to have some penetration - // into the input polygon. - - Points out; - out.reserve(2 * pts.size()); // output polygon points - - // stick bottom and right edge dimensions - double sbottom = scaled(stick_width); - double sright = scaled(penetration + padding); - - // scaled stride distance - double sstride = scaled(stride); - double t = 0; - - // process pairs of vertices as an edge, start with the last and - // first point - for (size_t i = pts.size() - 1, j = 0; j < pts.size(); i = j, ++j) { - // Get vertices and the direction vectors - const Point &a = pts[i], &b = pts[j]; - Vec2d dir = b.cast() - a.cast(); - double nrm = dir.norm(); - dir /= nrm; - Vec2d dirp(-dir(Y), dir(X)); - - // Insert start point - out.emplace_back(a); - - // dodge the start point, do not make sticks on the joins - while (t < sbottom) t += sbottom; - double tend = nrm - sbottom; - - while (t < tend) { // insert the stick on the polygon perimeter - - // calculate the stick rectangle vertices and insert them - // into the output. - Point p1 = a + (t * dir).cast(); - Point p2 = p1 + (sright * dirp).cast(); - Point p3 = p2 + (sbottom * dir).cast(); - Point p4 = p3 + (sright * -dirp).cast(); - out.insert(out.end(), {p1, p2, p3, p4}); - - // continue along the perimeter - t += sstride; - } - - t = t - nrm; - - // Insert edge endpoint - out.emplace_back(b); - } - - // move the new points - out.shrink_to_fit(); - pts.swap(out); - }; - - if(stride > 0.0 && stick_width > 0.0 && padding > 0.0) { - transf(poly.contour.points); - for (auto &h : poly.holes) transf(h.points); - } - - // svg.draw(poly); - // svg.Close(); -} - -/// This method will create a rounded edge around a flat polygon in 3d space. -/// 'base_plate' parameter is the target plate. -/// 'radius' is the radius of the edges. -/// 'degrees' is tells how much of a circle should be created as the rounding. -/// It should be in degrees, not radians. -/// 'ceilheight_mm' is the Z coordinate of the flat polygon in 3D space. -/// 'dir' Is the direction of the round edges: inward or outward -/// 'thr' Throws if a cancel signal was received -/// 'last_offset' An auxiliary output variable to save the last offsetted -/// version of 'base_plate' -/// 'last_height' An auxiliary output to save the last z coordinate of the -/// offsetted base_plate. In other words, where the rounded edges end. -Contour3D round_edges(const ExPolygon& base_plate, - double radius_mm, - double degrees, - double ceilheight_mm, - bool dir, - ThrowOnCancel thr, - ExPolygon& last_offset, double& last_height) -{ - auto ob = base_plate; - auto ob_prev = ob; - double wh = ceilheight_mm, wh_prev = wh; - Contour3D curvedwalls; - - int steps = 30; - double stepx = radius_mm / steps; - coord_t s = dir? 1 : -1; - degrees = std::fmod(degrees, 180); - - // we use sin for x distance because we interpret the angle starting from - // PI/2 - int tos = degrees < 90? - int(radius_mm*std::cos(degrees * PI / 180 - PI/2) / stepx) : steps; - - for(int i = 1; i <= tos; ++i) { - thr(); - - ob = base_plate; - - double r2 = radius_mm * radius_mm; - double xx = i*stepx; - double x2 = xx*xx; - double stepy = std::sqrt(r2 - x2); - - offset(ob, s * scaled(xx)); - wh = ceilheight_mm - radius_mm + stepy; - - Contour3D pwalls; - double prev_x = xx - (i - 1) * stepx; - pwalls = walls(ob.contour, ob_prev.contour, wh, wh_prev, s*prev_x, thr); - - curvedwalls.merge(pwalls); - ob_prev = ob; - wh_prev = wh; - } - - if(degrees > 90) { - double tox = radius_mm - radius_mm*std::cos(degrees * PI / 180 - PI/2); - int tos = int(tox / stepx); - - for(int i = 1; i <= tos; ++i) { - thr(); - ob = base_plate; - - double r2 = radius_mm * radius_mm; - double xx = radius_mm - i*stepx; - double x2 = xx*xx; - double stepy = std::sqrt(r2 - x2); - offset(ob, s * scaled(xx)); - wh = ceilheight_mm - radius_mm - stepy; - - Contour3D pwalls; - double prev_x = xx - radius_mm + (i - 1)*stepx; - pwalls = - walls(ob_prev.contour, ob.contour, wh_prev, wh, s*prev_x, thr); - - curvedwalls.merge(pwalls); - ob_prev = ob; - wh_prev = wh; - } - } - - last_offset = std::move(ob); - last_height = wh; - - return curvedwalls; -} - -inline Point centroid(Points& pp) { - Point c; - switch(pp.size()) { - case 0: break; - case 1: c = pp.front(); break; - case 2: c = (pp[0] + pp[1]) / 2; break; - default: { - auto MAX = std::numeric_limits::max(); - auto MIN = std::numeric_limits::min(); - Point min = {MAX, MAX}, max = {MIN, MIN}; - - for(auto& p : pp) { - if(p(0) < min(0)) min(0) = p(0); - if(p(1) < min(1)) min(1) = p(1); - if(p(0) > max(0)) max(0) = p(0); - if(p(1) > max(1)) max(1) = p(1); - } - c(0) = min(0) + (max(0) - min(0)) / 2; - c(1) = min(1) + (max(1) - min(1)) / 2; - - // TODO: fails for non convex cluster -// c = std::accumulate(pp.begin(), pp.end(), Point{0, 0}); -// x(c) /= coord_t(pp.size()); y(c) /= coord_t(pp.size()); - break; - } - } - - return c; -} - -inline Point centroid(const Polygon& poly) { - return poly.centroid(); -} - -/// A fake concave hull that is constructed by connecting separate shapes -/// with explicit bridges. Bridges are generated from each shape's centroid -/// to the center of the "scene" which is the centroid calculated from the shape -/// centroids (a star is created...) -Polygons concave_hull(const Polygons& polys, double maxd_mm, ThrowOnCancel thr) -{ - namespace bgi = boost::geometry::index; - using SpatElement = std::pair; - using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >; - - if(polys.empty()) return Polygons(); - - const double max_dist = scaled(maxd_mm); - - Polygons punion = unify(polys); // could be redundant - - if(punion.size() == 1) return punion; - - // We get the centroids of all the islands in the 2D slice - Points centroids; centroids.reserve(punion.size()); - std::transform(punion.begin(), punion.end(), std::back_inserter(centroids), - [](const Polygon& poly) { return centroid(poly); }); - - SpatIndex ctrindex; - unsigned idx = 0; - for(const Point &ct : centroids) ctrindex.insert(std::make_pair(ct, idx++)); - - // Centroid of the centroids of islands. This is where the additional - // connector sticks are routed. - Point cc = centroid(centroids); - - punion.reserve(punion.size() + centroids.size()); - - idx = 0; - std::transform(centroids.begin(), centroids.end(), - std::back_inserter(punion), - [¢roids, &ctrindex, cc, max_dist, &idx, thr] - (const Point& c) - { - thr(); - double dx = x(c) - x(cc), dy = y(c) - y(cc); - double l = std::sqrt(dx * dx + dy * dy); - double nx = dx / l, ny = dy / l; - - Point& ct = centroids[idx]; - - std::vector result; - ctrindex.query(bgi::nearest(ct, 2), std::back_inserter(result)); - - double dist = max_dist; - for (const SpatElement &el : result) - if (el.second != idx) { - dist = Line(el.first, ct).length(); - break; - } - - idx++; - - if (dist >= max_dist) return Polygon(); - - Polygon r; - auto& ctour = r.points; - - ctour.reserve(3); - ctour.emplace_back(cc); - - Point d(scaled(nx), scaled(ny)); - ctour.emplace_back(c + Point( -y(d), x(d) )); - ctour.emplace_back(c + Point( y(d), -x(d) )); - offset(r, scaled(1.)); - - return r; - }); - - // This is unavoidable... - punion = unify(punion); - - return punion; -} - -void base_plate(const TriangleMesh & mesh, - ExPolygons & output, - const std::vector &heights, - ThrowOnCancel thrfn) -{ - if (mesh.empty()) return; - // m.require_shared_vertices(); // TriangleMeshSlicer needs this - TriangleMeshSlicer slicer(&mesh); - - std::vector out; out.reserve(heights.size()); - slicer.slice(heights, &out, thrfn); - - size_t count = 0; for(auto& o : out) count += o.size(); - - // Now we have to unify all slice layers which can be an expensive operation - // so we will try to simplify the polygons - ExPolygons tmp; tmp.reserve(count); - for(ExPolygons& o : out) - for(ExPolygon& e : o) { - auto&& exss = e.simplify(scaled(0.1)); - for(ExPolygon& ep : exss) tmp.emplace_back(std::move(ep)); - } - - ExPolygons utmp = unify(tmp); - - for(auto& o : utmp) { - auto&& smp = o.simplify(scaled(0.1)); - output.insert(output.end(), smp.begin(), smp.end()); - } -} - -void base_plate(const TriangleMesh &mesh, - ExPolygons & output, - float h, - float layerh, - ThrowOnCancel thrfn) -{ - auto bb = mesh.bounding_box(); - float gnd = float(bb.min(Z)); - std::vector heights = {float(bb.min(Z))}; - - for(float hi = gnd + layerh; hi <= gnd + h; hi += layerh) - heights.emplace_back(hi); - - base_plate(mesh, output, heights, thrfn); -} - -Contour3D create_base_pool(const Polygons &ground_layer, - const ExPolygons &obj_self_pad = {}, - const PoolConfig& cfg = PoolConfig()) -{ - // for debugging: - // Benchmark bench; - // bench.start(); - - double mergedist = 2*(1.8*cfg.min_wall_thickness_mm + 4*cfg.edge_radius_mm)+ - cfg.max_merge_distance_mm; - - // Here we get the base polygon from which the pad has to be generated. - // We create an artificial concave hull from this polygon and that will - // serve as the bottom plate of the pad. We will offset this concave hull - // and then offset back the result with clipper with rounding edges ON. This - // trick will create a nice rounded pad shape. - Polygons concavehs = concave_hull(ground_layer, mergedist, cfg.throw_on_cancel); - - const double thickness = cfg.min_wall_thickness_mm; - const double wingheight = cfg.min_wall_height_mm; - const double fullheight = wingheight + thickness; - const double slope = cfg.wall_slope; - const double wingdist = wingheight / std::tan(slope); - const double bottom_offs = (thickness + wingheight) / std::tan(slope); - - // scaled values - const coord_t s_thickness = scaled(thickness); - const coord_t s_eradius = scaled(cfg.edge_radius_mm); - const coord_t s_safety_dist = 2*s_eradius + coord_t(0.8*s_thickness); - const coord_t s_wingdist = scaled(wingdist); - const coord_t s_bottom_offs = scaled(bottom_offs); - - auto& thrcl = cfg.throw_on_cancel; - - Contour3D pool; - - for(Polygon& concaveh : concavehs) { - if(concaveh.points.empty()) return pool; - - // Here lies the trick that does the smoothing only with clipper offset - // calls. The offset is configured to round edges. Inner edges will - // be rounded because we offset twice: ones to get the outer (top) plate - // and again to get the inner (bottom) plate - auto outer_base = concaveh; - offset(outer_base, s_safety_dist + s_wingdist + s_thickness); - - ExPolygon bottom_poly; bottom_poly.contour = outer_base; - offset(bottom_poly, -s_bottom_offs); - - // Punching a hole in the top plate for the cavity - ExPolygon top_poly; - ExPolygon middle_base; - ExPolygon inner_base; - top_poly.contour = outer_base; - - if(wingheight > 0) { - inner_base.contour = outer_base; - offset(inner_base, -(s_thickness + s_wingdist + s_eradius)); - - middle_base.contour = outer_base; - offset(middle_base, -s_thickness); - top_poly.holes.emplace_back(middle_base.contour); - auto& tph = top_poly.holes.back().points; - std::reverse(tph.begin(), tph.end()); - } - - ExPolygon ob; ob.contour = outer_base; double wh = 0; - - // now we will calculate the angle or portion of the circle from - // pi/2 that will connect perfectly with the bottom plate. - // this is a tangent point calculation problem and the equation can - // be found for example here: - // http://www.ambrsoft.com/TrigoCalc/Circles2/CirclePoint/CirclePointDistance.htm - // the y coordinate would be: - // y = cy + (r^2*py - r*px*sqrt(px^2 + py^2 - r^2) / (px^2 + py^2) - // where px and py are the coordinates of the point outside the circle - // cx and cy are the circle center, r is the radius - // We place the circle center to (0, 0) in the calculation the make - // things easier. - // to get the angle we use arcsin function and subtract 90 degrees then - // flip the sign to get the right input to the round_edge function. - double r = cfg.edge_radius_mm; - double cy = 0; - double cx = 0; - double px = thickness + wingdist; - double py = r - fullheight; - - double pxcx = px - cx; - double pycy = py - cy; - double b_2 = pxcx*pxcx + pycy*pycy; - double r_2 = r*r; - double D = std::sqrt(b_2 - r_2); - double vy = (r_2*pycy - r*pxcx*D) / b_2; - double phi = -(std::asin(vy/r) * 180 / PI - 90); - - - // Generate the smoothed edge geometry - if(s_eradius > 0) pool.merge(round_edges(ob, - r, - phi, - 0, // z position of the input plane - true, - thrcl, - ob, wh)); - - // Now that we have the rounded edge connecting the top plate with - // the outer side walls, we can generate and merge the sidewall geometry - pool.merge(walls(ob.contour, bottom_poly.contour, wh, -fullheight, - bottom_offs, thrcl)); - - if(wingheight > 0) { - // Generate the smoothed edge geometry - wh = 0; - ob = middle_base; - if(s_eradius) pool.merge(round_edges(middle_base, - r, - phi - 90, // from tangent lines - 0, // z position of the input plane - false, - thrcl, - ob, wh)); - - // Next is the cavity walls connecting to the top plate's - // artificially created hole. - pool.merge(walls(inner_base.contour, ob.contour, -wingheight, - wh, -wingdist, thrcl)); - } - - if (cfg.embed_object) { - ExPolygons bttms = diff_ex(to_polygons(bottom_poly), - to_polygons(obj_self_pad)); - - assert(!bttms.empty()); - - std::sort(bttms.begin(), bttms.end(), - [](const ExPolygon& e1, const ExPolygon& e2) { - return e1.contour.area() > e2.contour.area(); - }); - - if(wingheight > 0) inner_base.holes = bttms.front().holes; - else top_poly.holes = bttms.front().holes; - - auto straight_walls = - [&pool](const Polygon &cntr, coord_t z_low, coord_t z_high) { - - auto lines = cntr.lines(); - - for (auto &l : lines) { - auto s = coord_t(pool.points.size()); - auto& pts = pool.points; - pts.emplace_back(unscale(l.a.x(), l.a.y(), z_low)); - pts.emplace_back(unscale(l.b.x(), l.b.y(), z_low)); - pts.emplace_back(unscale(l.a.x(), l.a.y(), z_high)); - pts.emplace_back(unscale(l.b.x(), l.b.y(), z_high)); - - pool.indices.emplace_back(s, s + 1, s + 3); - pool.indices.emplace_back(s, s + 3, s + 2); - } - }; - - coord_t z_lo = -scaled(fullheight), z_hi = -scaled(wingheight); - for (ExPolygon &ep : bttms) { - pool.merge(triangulate_expolygon_3d(ep, -fullheight, true)); - for (auto &h : ep.holes) straight_walls(h, z_lo, z_hi); - } - - // Skip the outer contour, triangulate the holes - for (auto it = std::next(bttms.begin()); it != bttms.end(); ++it) { - pool.merge(triangulate_expolygon_3d(*it, -wingheight)); - straight_walls(it->contour, z_lo, z_hi); - } - - } else { - // Now we need to triangulate the top and bottom plates as well as - // the cavity bottom plate which is the same as the bottom plate - // but it is elevated by the thickness. - - pool.merge(triangulate_expolygon_3d(bottom_poly, -fullheight, true)); - } - - pool.merge(triangulate_expolygon_3d(top_poly)); - - if(wingheight > 0) - pool.merge(triangulate_expolygon_3d(inner_base, -wingheight)); - - } - - return pool; -} - -void create_base_pool(const Polygons &ground_layer, TriangleMesh& out, - const ExPolygons &holes, const PoolConfig& cfg) -{ - - - // For debugging: - // bench.stop(); - // std::cout << "Pad creation time: " << bench.getElapsedSec() << std::endl; - // std::fstream fout("pad_debug.obj", std::fstream::out); - // if(fout.good()) pool.to_obj(fout); - - out.merge(mesh(create_base_pool(ground_layer, holes, cfg))); -} - -} -} diff --git a/src/libslic3r/SLA/SLABasePool.hpp b/src/libslic3r/SLA/SLABasePool.hpp deleted file mode 100644 index eec426bbf..000000000 --- a/src/libslic3r/SLA/SLABasePool.hpp +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef SLABASEPOOL_HPP -#define SLABASEPOOL_HPP - -#include -#include -#include - -namespace Slic3r { - -class ExPolygon; -class Polygon; -using ExPolygons = std::vector; -using Polygons = std::vector; - -class TriangleMesh; - -namespace sla { - -using ThrowOnCancel = std::function; - -/// Calculate the polygon representing the silhouette from the specified height -void base_plate(const TriangleMesh& mesh, // input mesh - ExPolygons& output, // Output will be merged with - float samplingheight = 0.1f, // The height range to sample - float layerheight = 0.05f, // The sampling height - ThrowOnCancel thrfn = [](){}); // Will be called frequently - -void base_plate(const TriangleMesh& mesh, // input mesh - ExPolygons& output, // Output will be merged with - const std::vector&, // Exact Z levels to sample - ThrowOnCancel thrfn = [](){}); // Will be called frequently - -// Function to cut tiny connector cavities for a given polygon. The input poly -// will be offsetted by "padding" and small rectangle shaped cavities will be -// inserted along the perimeter in every "stride" distance. The stick rectangles -// will have a with about "stick_width". The input dimensions are in world -// measure, not the scaled clipper units. -void breakstick_holes(ExPolygon &poly, - double padding, - double stride, - double stick_width, - double penetration = 0.0); - -Polygons concave_hull(const Polygons& polys, double max_dist_mm = 50, - ThrowOnCancel throw_on_cancel = [](){}); - -struct PoolConfig { - double min_wall_thickness_mm = 2; - double min_wall_height_mm = 5; - double max_merge_distance_mm = 50; - double edge_radius_mm = 1; - double wall_slope = std::atan(1.0); // Universal constant for Pi/4 - struct EmbedObject { - double object_gap_mm = 0.5; - double stick_stride_mm = 10; - double stick_width_mm = 0.3; - double stick_penetration_mm = 0.1; - bool enabled = false; - operator bool() const { return enabled; } - } embed_object; - - ThrowOnCancel throw_on_cancel = [](){}; - - inline PoolConfig() {} - inline PoolConfig(double wt, double wh, double md, double er, double slope): - min_wall_thickness_mm(wt), - min_wall_height_mm(wh), - max_merge_distance_mm(md), - edge_radius_mm(er), - wall_slope(slope) {} -}; - -/// Calculate the pool for the mesh for SLA printing -void create_base_pool(const Polygons& base_plate, - TriangleMesh& output_mesh, - const ExPolygons& holes, - const PoolConfig& = PoolConfig()); - -/// Returns the elevation needed for compensating the pad. -inline double get_pad_elevation(const PoolConfig& cfg) { - return cfg.min_wall_thickness_mm; -} - -inline double get_pad_fullheight(const PoolConfig& cfg) { - return cfg.min_wall_height_mm + cfg.min_wall_thickness_mm; -} - -} - -} - -#endif // SLABASEPOOL_HPP diff --git a/src/libslic3r/SLA/SLABoilerPlate.hpp b/src/libslic3r/SLA/SLABoilerPlate.hpp index f89ad6f1a..e226a81d7 100644 --- a/src/libslic3r/SLA/SLABoilerPlate.hpp +++ b/src/libslic3r/SLA/SLABoilerPlate.hpp @@ -8,35 +8,19 @@ #include #include +#include "SLACommon.hpp" +#include "SLASpatIndex.hpp" + namespace Slic3r { namespace sla { -/// Get x and y coordinates (because we are eigenizing...) -inline coord_t x(const Point& p) { return p(0); } -inline coord_t y(const Point& p) { return p(1); } -inline coord_t& x(Point& p) { return p(0); } -inline coord_t& y(Point& p) { return p(1); } - -inline coordf_t x(const Vec3d& p) { return p(0); } -inline coordf_t y(const Vec3d& p) { return p(1); } -inline coordf_t z(const Vec3d& p) { return p(2); } -inline coordf_t& x(Vec3d& p) { return p(0); } -inline coordf_t& y(Vec3d& p) { return p(1); } -inline coordf_t& z(Vec3d& p) { return p(2); } - -inline int32_t& x(Vec3i32& p) { return p(0); } -inline int32_t& y(Vec3i32& p) { return p(1); } -inline int32_t& z(Vec3i32& p) { return p(2); } -inline int32_t x(const Vec3i32& p) { return p(0); } -inline int32_t y(const Vec3i32& p) { return p(1); } -inline int32_t z(const Vec3i32& p) { return p(2); } - /// Intermediate struct for a 3D mesh struct Contour3D { Pointf3s points; std::vector indices; - void merge(const Contour3D& ctr) { + Contour3D& merge(const Contour3D& ctr) + { size_t s3 = size_t(points.size()); size_t s = indices.size(); @@ -44,21 +28,27 @@ struct Contour3D { indices.insert(indices.end(), ctr.indices.begin(), ctr.indices.end()); for(size_t n = s; n < indices.size(); n++) { - Vec3i32& idx = indices[n]; x(idx) += s3; y(idx) += s3; z(idx) += s3; + Vec3i32& idx = indices[n]; idx.x() += s3; idx.y() += s3; idx.z() += s3; } + + return *this; } - void merge(const Pointf3s& triangles) { + 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) + 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) { + void to_obj(std::ostream& stream) + { for(auto& p : points) { stream << "v " << p.transpose() << "\n"; } @@ -72,6 +62,31 @@ struct Contour3D { 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}; diff --git a/src/libslic3r/SLA/SLACommon.hpp b/src/libslic3r/SLA/SLACommon.hpp index d8e258035..97b459676 100644 --- a/src/libslic3r/SLA/SLACommon.hpp +++ b/src/libslic3r/SLA/SLACommon.hpp @@ -1,8 +1,9 @@ #ifndef SLACOMMON_HPP #define SLACOMMON_HPP -#include #include +#include +#include // #define SLIC3R_SLA_NEEDS_WINDTREE @@ -69,6 +70,8 @@ struct SupportPoint } }; +using SupportPoints = std::vector; + /// An index-triangle structure for libIGL functions. Also serves as an /// alternative (raw) input format for the SLASupportTree class EigenMesh3D { @@ -175,6 +178,8 @@ public: } }; +using PointSet = Eigen::MatrixXd; + } // namespace sla } // namespace Slic3r diff --git a/src/libslic3r/SLA/SLAConcurrency.hpp b/src/libslic3r/SLA/SLAConcurrency.hpp new file mode 100644 index 000000000..4beb2aead --- /dev/null +++ b/src/libslic3r/SLA/SLAConcurrency.hpp @@ -0,0 +1,56 @@ +#ifndef SLACONCURRENCY_H +#define SLACONCURRENCY_H + +#include +#include +#include + +namespace Slic3r { +namespace sla { + +// Set this to true to enable full parallelism in this module. +// Only the well tested parts will be concurrent if this is set to false. +const constexpr bool USE_FULL_CONCURRENCY = true; + +template struct _ccr {}; + +template<> struct _ccr +{ + using SpinningMutex = tbb::spin_mutex; + using BlockingMutex = tbb::mutex; + + template + static inline void enumerate(It from, It to, Fn fn) + { + auto iN = to - from; + size_t N = iN < 0 ? 0 : size_t(iN); + + tbb::parallel_for(size_t(0), N, [from, fn](size_t n) { + fn(*(from + decltype(iN)(n)), n); + }); + } +}; + +template<> struct _ccr +{ +private: + struct _Mtx { inline void lock() {} inline void unlock() {} }; + +public: + using SpinningMutex = _Mtx; + using BlockingMutex = _Mtx; + + template + static inline void enumerate(It from, It to, Fn fn) + { + for (auto it = from; it != to; ++it) fn(*it, size_t(it - from)); + } +}; + +using ccr = _ccr; +using ccr_seq = _ccr; +using ccr_par = _ccr; + +}} // namespace Slic3r::sla + +#endif // SLACONCURRENCY_H diff --git a/src/libslic3r/SLA/SLAPad.cpp b/src/libslic3r/SLA/SLAPad.cpp new file mode 100644 index 000000000..7cd9eb4e4 --- /dev/null +++ b/src/libslic3r/SLA/SLAPad.cpp @@ -0,0 +1,695 @@ +#include "SLAPad.hpp" +#include "SLABoilerPlate.hpp" +#include "SLASpatIndex.hpp" +#include "ConcaveHull.hpp" + +#include "boost/log/trivial.hpp" +#include "SLABoostAdapter.hpp" +#include "ClipperUtils.hpp" +#include "Tesselate.hpp" +#include "MTUtils.hpp" + +// For debugging: +// #include +// #include +#include "SVG.hpp" + +#include "I18N.hpp" +#include + +//! macro used to mark string used at localization, +//! return same string +#define L(s) Slic3r::I18N::translate(s) + +namespace Slic3r { namespace sla { + +namespace { + +/// This function will return a triangulation of a sheet connecting an upper +/// and a lower plate given as input polygons. It will not triangulate the +/// plates themselves only the sheet. The caller has to specify the lower and +/// upper z levels in world coordinates as well as the offset difference +/// between the sheets. If the lower_z_mm is higher than upper_z_mm or the +/// offset difference is negative, the resulting triangle orientation will be +/// reversed. +/// +/// IMPORTANT: This is not a universal triangulation algorithm. It assumes +/// that the lower and upper polygons are offsetted versions of the same +/// original polygon. In general, it assumes that one of the polygons is +/// completely inside the other. The offset difference is the reference +/// distance from the inner polygon's perimeter to the outer polygon's +/// perimeter. The real distance will be variable as the clipper offset has +/// different strategies (rounding, etc...). This algorithm should have +/// O(2n + 3m) complexity where n is the number of upper vertices and m is the +/// number of lower vertices. +Contour3D walls( + const Polygon &lower, + const Polygon &upper, + double lower_z_mm, + double upper_z_mm, + double offset_difference_mm, + ThrowOnCancel thr = [] {}) +{ + Contour3D ret; + + if(upper.points.size() < 3 || lower.size() < 3) return ret; + + // The concept of the algorithm is relatively simple. It will try to find + // the closest vertices from the upper and the lower polygon and use those + // as starting points. Then it will create the triangles sequentially using + // an edge from the upper polygon and a vertex from the lower or vice versa, + // depending on the resulting triangle's quality. + // The quality is measured by a scalar value. So far it looks like it is + // enough to derive it from the slope of the triangle's two edges connecting + // the upper and the lower part. A reference slope is calculated from the + // height and the offset difference. + + // Offset in the index array for the ceiling + const auto offs = upper.points.size(); + + // Shorthand for the vertex arrays + auto& upts = upper.points, &lpts = lower.points; + auto& rpts = ret.points; auto& ind = ret.indices; + + // If the Z levels are flipped, or the offset difference is negative, we + // will interpret that as the triangles normals should be inverted. + bool inverted = upper_z_mm < lower_z_mm || offset_difference_mm < 0; + + // Copy the points into the mesh, convert them from 2D to 3D + rpts.reserve(upts.size() + lpts.size()); + ind.reserve(2 * upts.size() + 2 * lpts.size()); + for (auto &p : upts) + rpts.emplace_back(unscaled(p.x()), unscaled(p.y()), upper_z_mm); + for (auto &p : lpts) + rpts.emplace_back(unscaled(p.x()), unscaled(p.y()), lower_z_mm); + + // Create pointing indices into vertex arrays. u-upper, l-lower + size_t uidx = 0, lidx = offs, unextidx = 1, lnextidx = offs + 1; + + // Simple squared distance calculation. + auto distfn = [](const Vec3d& p1, const Vec3d& p2) { + auto p = p1 - p2; return p.transpose() * p; + }; + + // We need to find the closest point on lower polygon to the first point on + // the upper polygon. These will be our starting points. + double distmin = std::numeric_limits::max(); + for(size_t l = lidx; l < rpts.size(); ++l) { + thr(); + double d = distfn(rpts[l], rpts[uidx]); + if(d < distmin) { lidx = l; distmin = d; } + } + + // Set up lnextidx to be ahead of lidx in cyclic mode + lnextidx = lidx + 1; + if(lnextidx == rpts.size()) lnextidx = offs; + + // This will be the flip switch to toggle between upper and lower triangle + // creation mode + enum class Proceed { + UPPER, // A segment from the upper polygon and one vertex from the lower + LOWER // A segment from the lower polygon and one vertex from the upper + } proceed = Proceed::UPPER; + + // Flags to help evaluating loop termination. + bool ustarted = false, lstarted = false; + + // The variables for the fitness values, one for the actual and one for the + // previous. + double current_fit = 0, prev_fit = 0; + + // Every triangle of the wall has two edges connecting the upper plate with + // the lower plate. From the length of these two edges and the zdiff we + // can calculate the momentary squared offset distance at a particular + // position on the wall. The average of the differences from the reference + // (squared) offset distance will give us the driving fitness value. + const double offsdiff2 = std::pow(offset_difference_mm, 2); + const double zdiff2 = std::pow(upper_z_mm - lower_z_mm, 2); + + // Mark the current vertex iterator positions. If the iterators return to + // the same position, the loop can be terminated. + size_t uendidx = uidx, lendidx = lidx; + + do { thr(); // check throw if canceled + + prev_fit = current_fit; + + switch(proceed) { // proceed depending on the current state + case Proceed::UPPER: + if(!ustarted || uidx != uendidx) { // there are vertices remaining + // Get the 3D vertices in order + const Vec3d& p_up1 = rpts[uidx]; + const Vec3d& p_low = rpts[lidx]; + const Vec3d& p_up2 = rpts[unextidx]; + + // Calculate fitness: the average of the two connecting edges + double a = offsdiff2 - (distfn(p_up1, p_low) - zdiff2); + double b = offsdiff2 - (distfn(p_up2, p_low) - zdiff2); + current_fit = (std::abs(a) + std::abs(b)) / 2; + + if(current_fit > prev_fit) { // fit is worse than previously + proceed = Proceed::LOWER; + } else { // good to go, create the triangle + inverted + ? ind.emplace_back(int(unextidx), int(lidx), int(uidx)) + : ind.emplace_back(int(uidx), int(lidx), int(unextidx)); + + // Increment the iterators, rotate if necessary + ++uidx; ++unextidx; + if(unextidx == offs) unextidx = 0; + if(uidx == offs) uidx = 0; + + ustarted = true; // mark the movement of the iterators + // so that the comparison to uendidx can be made correctly + } + } else proceed = Proceed::LOWER; + + break; + case Proceed::LOWER: + // Mode with lower segment, upper vertex. Same structure: + if(!lstarted || lidx != lendidx) { + const Vec3d& p_low1 = rpts[lidx]; + const Vec3d& p_low2 = rpts[lnextidx]; + const Vec3d& p_up = rpts[uidx]; + + double a = offsdiff2 - (distfn(p_up, p_low1) - zdiff2); + double b = offsdiff2 - (distfn(p_up, p_low2) - zdiff2); + current_fit = (std::abs(a) + std::abs(b)) / 2; + + if(current_fit > prev_fit) { + proceed = Proceed::UPPER; + } else { + inverted + ? ind.emplace_back(int(uidx), int(lnextidx), int(lidx)) + : ind.emplace_back(int(lidx), int(lnextidx), int(uidx)); + + ++lidx; ++lnextidx; + if(lnextidx == rpts.size()) lnextidx = offs; + if(lidx == rpts.size()) lidx = offs; + + lstarted = true; + } + } else proceed = Proceed::UPPER; + + break; + } // end of switch + } while(!ustarted || !lstarted || uidx != uendidx || lidx != lendidx); + + return ret; +} + +// Same as walls() but with identical higher and lower polygons. +Contour3D inline straight_walls(const Polygon &plate, + double lo_z, + double hi_z, + ThrowOnCancel thr) +{ + return walls(plate, plate, lo_z, hi_z, .0 /*offset_diff*/, thr); +} + +// Function to cut tiny connector cavities for a given polygon. The input poly +// will be offsetted by "padding" and small rectangle shaped cavities will be +// inserted along the perimeter in every "stride" distance. The stick rectangles +// will have a with about "stick_width". The input dimensions are in world +// measure, not the scaled clipper units. +void breakstick_holes(Points& pts, + double padding, + double stride, + double stick_width, + double penetration) +{ + if(stride <= EPSILON || stick_width <= EPSILON || padding <= EPSILON) + return; + + // SVG svg("bridgestick_plate.svg"); + // svg.draw(poly); + + // The connector stick will be a small rectangle with dimensions + // stick_width x (penetration + padding) to have some penetration + // into the input polygon. + + Points out; + out.reserve(2 * pts.size()); // output polygon points + + // stick bottom and right edge dimensions + double sbottom = scaled(stick_width); + double sright = scaled(penetration + padding); + + // scaled stride distance + double sstride = scaled(stride); + double t = 0; + + // process pairs of vertices as an edge, start with the last and + // first point + for (size_t i = pts.size() - 1, j = 0; j < pts.size(); i = j, ++j) { + // Get vertices and the direction vectors + const Point &a = pts[i], &b = pts[j]; + Vec2d dir = b.cast() - a.cast(); + double nrm = dir.norm(); + dir /= nrm; + Vec2d dirp(-dir(Y), dir(X)); + + // Insert start point + out.emplace_back(a); + + // dodge the start point, do not make sticks on the joins + while (t < sbottom) t += sbottom; + double tend = nrm - sbottom; + + while (t < tend) { // insert the stick on the polygon perimeter + + // calculate the stick rectangle vertices and insert them + // into the output. + Point p1 = a + (t * dir).cast(); + Point p2 = p1 + (sright * dirp).cast(); + Point p3 = p2 + (sbottom * dir).cast(); + Point p4 = p3 + (sright * -dirp).cast(); + out.insert(out.end(), {p1, p2, p3, p4}); + + // continue along the perimeter + t += sstride; + } + + t = t - nrm; + + // Insert edge endpoint + out.emplace_back(b); + } + + // move the new points + out.shrink_to_fit(); + pts.swap(out); +} + +template +ExPolygons breakstick_holes(const ExPolygons &input, Args...args) +{ + ExPolygons ret = input; + for (ExPolygon &p : ret) { + breakstick_holes(p.contour.points, args...); + for (auto &h : p.holes) breakstick_holes(h.points, args...); + } + + return ret; +} + +static inline coord_t get_waffle_offset(const PadConfig &c) +{ + return scaled(c.brim_size_mm + c.wing_distance()); +} + +static inline double get_merge_distance(const PadConfig &c) +{ + return 2. * (1.8 * c.wall_thickness_mm) + c.max_merge_dist_mm; +} + +// Part of the pad configuration that is used for 3D geometry generation +struct PadConfig3D { + double thickness, height, wing_height, slope; + + explicit PadConfig3D(const PadConfig &cfg2d) + : thickness{cfg2d.wall_thickness_mm} + , height{cfg2d.full_height()} + , wing_height{cfg2d.wall_height_mm} + , slope{cfg2d.wall_slope} + {} + + inline double bottom_offset() const + { + return (thickness + wing_height) / std::tan(slope); + } +}; + +// Outer part of the skeleton is used to generate the waffled edges of the pad. +// Inner parts will not be waffled or offsetted. Inner parts are only used if +// pad is generated around the object and correspond to holes and inner polygons +// in the model blueprint. +struct PadSkeleton { ExPolygons inner, outer; }; + +PadSkeleton divide_blueprint(const ExPolygons &bp) +{ + ClipperLib::PolyTree ptree = union_pt(bp); + + PadSkeleton ret; + ret.inner.reserve(size_t(ptree.Total())); + ret.outer.reserve(size_t(ptree.Total())); + + for (ClipperLib::PolyTree::PolyNode *node : ptree.Childs) { + ExPolygon poly(ClipperPath_to_Slic3rPolygon(node->Contour)); + for (ClipperLib::PolyTree::PolyNode *child : node->Childs) { + if (child->IsHole()) { + poly.holes.emplace_back( + ClipperPath_to_Slic3rPolygon(child->Contour)); + + traverse_pt_unordered(child->Childs, &ret.inner); + } + else traverse_pt_unordered(child, &ret.inner); + } + + ret.outer.emplace_back(poly); + } + + return ret; +} + +// A helper class for storing polygons and maintaining a spatial index of their +// bounding boxes. +class Intersector { + BoxIndex m_index; + ExPolygons m_polys; + +public: + + // Add a new polygon to the index + void add(const ExPolygon &ep) + { + m_polys.emplace_back(ep); + m_index.insert(BoundingBox{ep}, unsigned(m_index.size())); + } + + // Check an arbitrary polygon for intersection with the indexed polygons + bool intersects(const ExPolygon &poly) + { + // Create a suitable query bounding box. + auto bb = poly.contour.bounding_box(); + + std::vector qres = m_index.query(bb, BoxIndex::qtIntersects); + + // Now check intersections on the actual polygons (not just the boxes) + bool is_overlap = false; + auto qit = qres.begin(); + while (!is_overlap && qit != qres.end()) + is_overlap = is_overlap || poly.overlaps(m_polys[(qit++)->second]); + + return is_overlap; + } +}; + +// This dummy intersector to implement the "force pad everywhere" feature +struct DummyIntersector +{ + inline void add(const ExPolygon &) {} + inline bool intersects(const ExPolygon &) { return true; } +}; + +template +class _AroundPadSkeleton : public PadSkeleton +{ + // A spatial index used to be able to efficiently find intersections of + // support polygons with the model polygons. + _Intersector m_intersector; + +public: + _AroundPadSkeleton(const ExPolygons &support_blueprint, + const ExPolygons &model_blueprint, + const PadConfig & cfg, + ThrowOnCancel thr) + { + // We need to merge the support and the model contours in a special + // way in which the model contours have to be substracted from the + // support contours. The pad has to have a hole in which the model can + // fit perfectly (thus the substraction -- diff_ex). Also, the pad has + // to be eliminated from areas where there is no need for a pad, due + // to missing supports. + + add_supports_to_index(support_blueprint); + + auto model_bp_offs = + offset_ex(model_blueprint, + scaled(cfg.embed_object.object_gap_mm), + ClipperLib::jtMiter, 1); + + ExPolygons fullcvh = + wafflized_concave_hull(support_blueprint, model_bp_offs, cfg, thr); + + auto model_bp_sticks = + breakstick_holes(model_bp_offs, cfg.embed_object.object_gap_mm, + cfg.embed_object.stick_stride_mm, + cfg.embed_object.stick_width_mm, + cfg.embed_object.stick_penetration_mm); + + ExPolygons fullpad = diff_ex(fullcvh, model_bp_sticks); + + remove_redundant_parts(fullpad); + + PadSkeleton divided = divide_blueprint(fullpad); + outer = std::move(divided.outer); + inner = std::move(divided.inner); + } + +private: + + // Add the support blueprint to the search index to be queried later + void add_supports_to_index(const ExPolygons &supp_bp) + { + for (auto &ep : supp_bp) m_intersector.add(ep); + } + + // Create the wafflized pad around all object in the scene. This pad doesnt + // have any holes yet. + ExPolygons wafflized_concave_hull(const ExPolygons &supp_bp, + const ExPolygons &model_bp, + const PadConfig &cfg, + ThrowOnCancel thr) + { + auto allin = reserve_vector(supp_bp.size() + model_bp.size()); + + for (auto &ep : supp_bp) allin.emplace_back(ep.contour); + for (auto &ep : model_bp) allin.emplace_back(ep.contour); + + ConcaveHull cchull{allin, get_merge_distance(cfg), thr}; + return offset_waffle_style_ex(cchull, get_waffle_offset(cfg)); + } + + // To remove parts of the pad skeleton which do not host any supports + void remove_redundant_parts(ExPolygons &parts) + { + auto endit = std::remove_if(parts.begin(), parts.end(), + [this](const ExPolygon &p) { + return !m_intersector.intersects(p); + }); + + parts.erase(endit, parts.end()); + } +}; + +using AroundPadSkeleton = _AroundPadSkeleton; +using BrimPadSkeleton = _AroundPadSkeleton; + +class BelowPadSkeleton : public PadSkeleton +{ +public: + BelowPadSkeleton(const ExPolygons &support_blueprint, + const ExPolygons &model_blueprint, + const PadConfig & cfg, + ThrowOnCancel thr) + { + outer.reserve(support_blueprint.size() + model_blueprint.size()); + + for (auto &ep : support_blueprint) outer.emplace_back(ep.contour); + for (auto &ep : model_blueprint) outer.emplace_back(ep.contour); + + ConcaveHull ochull{outer, get_merge_distance(cfg), thr}; + + outer = offset_waffle_style_ex(ochull, get_waffle_offset(cfg)); + } +}; + +// Offset the contour only, leave the holes untouched +template +ExPolygon offset_contour_only(const ExPolygon &poly, coord_t delta, Args...args) +{ + ExPolygons tmp = offset_ex(poly.contour, float(delta), args...); + + if (tmp.empty()) return {}; + + Polygons holes = poly.holes; + for (auto &h : holes) h.reverse(); + + tmp = diff_ex(to_polygons(tmp), holes); + + if (tmp.empty()) return {}; + + return tmp.front(); +} + +bool add_cavity(Contour3D &pad, ExPolygon &top_poly, const PadConfig3D &cfg, + ThrowOnCancel thr) +{ + auto logerr = []{BOOST_LOG_TRIVIAL(error)<<"Could not create pad cavity";}; + + double wing_distance = cfg.wing_height / std::tan(cfg.slope); + coord_t delta_inner = -scaled(cfg.thickness + wing_distance); + coord_t delta_middle = -scaled(cfg.thickness); + ExPolygon inner_base = offset_contour_only(top_poly, delta_inner); + ExPolygon middle_base = offset_contour_only(top_poly, delta_middle); + + if (inner_base.empty() || middle_base.empty()) { logerr(); return false; } + + ExPolygons pdiff = diff_ex(top_poly, middle_base.contour); + + if (pdiff.size() != 1) { logerr(); return false; } + + top_poly = pdiff.front(); + + double z_min = -cfg.wing_height, z_max = 0; + double offset_difference = -wing_distance; + pad.merge(walls(inner_base.contour, middle_base.contour, z_min, z_max, + offset_difference, thr)); + + pad.merge(triangulate_expolygon_3d(inner_base, z_min, NORMALS_UP)); + + return true; +} + +Contour3D create_outer_pad_geometry(const ExPolygons & skeleton, + const PadConfig3D &cfg, + ThrowOnCancel thr) +{ + Contour3D ret; + + for (const ExPolygon &pad_part : skeleton) { + ExPolygon top_poly{pad_part}; + ExPolygon bottom_poly = + offset_contour_only(pad_part, -scaled(cfg.bottom_offset())); + + if (bottom_poly.empty()) continue; + + double z_min = -cfg.height, z_max = 0; + ret.merge(walls(top_poly.contour, bottom_poly.contour, z_max, z_min, + cfg.bottom_offset(), thr)); + + if (cfg.wing_height > 0. && add_cavity(ret, top_poly, cfg, thr)) + z_max = -cfg.wing_height; + + for (auto &h : bottom_poly.holes) + ret.merge(straight_walls(h, z_max, z_min, thr)); + + ret.merge(triangulate_expolygon_3d(bottom_poly, z_min, NORMALS_DOWN)); + ret.merge(triangulate_expolygon_3d(top_poly, NORMALS_UP)); + } + + return ret; +} + +Contour3D create_inner_pad_geometry(const ExPolygons & skeleton, + const PadConfig3D &cfg, + ThrowOnCancel thr) +{ + Contour3D ret; + + double z_max = 0., z_min = -cfg.height; + for (const ExPolygon &pad_part : skeleton) { + ret.merge(straight_walls(pad_part.contour, z_max, z_min,thr)); + + for (auto &h : pad_part.holes) + ret.merge(straight_walls(h, z_max, z_min, thr)); + + ret.merge(triangulate_expolygon_3d(pad_part, z_min, NORMALS_DOWN)); + ret.merge(triangulate_expolygon_3d(pad_part, z_max, NORMALS_UP)); + } + + return ret; +} + +Contour3D create_pad_geometry(const PadSkeleton &skelet, + const PadConfig & cfg, + ThrowOnCancel thr) +{ +#ifndef NDEBUG + SVG svg("pad_skeleton.svg"); + svg.draw(skelet.outer, "green"); + svg.draw(skelet.inner, "blue"); + svg.Close(); +#endif + + PadConfig3D cfg3d(cfg); + return create_outer_pad_geometry(skelet.outer, cfg3d, thr) + .merge(create_inner_pad_geometry(skelet.inner, cfg3d, thr)); +} + +Contour3D create_pad_geometry(const ExPolygons &supp_bp, + const ExPolygons &model_bp, + const PadConfig & cfg, + ThrowOnCancel thr) +{ + PadSkeleton skelet; + + if (cfg.embed_object.enabled) { + if (cfg.embed_object.everywhere) + skelet = BrimPadSkeleton(supp_bp, model_bp, cfg, thr); + else + skelet = AroundPadSkeleton(supp_bp, model_bp, cfg, thr); + } else + skelet = BelowPadSkeleton(supp_bp, model_bp, cfg, thr); + + return create_pad_geometry(skelet, cfg, thr); +} + +} // namespace + +void pad_blueprint(const TriangleMesh & mesh, + ExPolygons & output, + const std::vector &heights, + ThrowOnCancel thrfn) +{ + if (mesh.empty()) return; + TriangleMeshSlicer slicer(&mesh); + + auto out = reserve_vector(heights.size()); + slicer.slice(heights, 0.f, &out, thrfn); + + size_t count = 0; + for(auto& o : out) count += o.size(); + + // Unification is expensive, a simplify also speeds up the pad generation + auto tmp = reserve_vector(count); + for(ExPolygons& o : out) + for(ExPolygon& e : o) { + auto&& exss = e.simplify(scaled(0.1)); + for(ExPolygon& ep : exss) tmp.emplace_back(std::move(ep)); + } + + ExPolygons utmp = union_ex(tmp); + + for(auto& o : utmp) { + auto&& smp = o.simplify(scaled(0.1)); + output.insert(output.end(), smp.begin(), smp.end()); + } +} + +void pad_blueprint(const TriangleMesh &mesh, + ExPolygons & output, + float h, + float layerh, + ThrowOnCancel thrfn) +{ + float gnd = float(mesh.bounding_box().min(Z)); + + std::vector slicegrid = grid(gnd, gnd + h, layerh); + pad_blueprint(mesh, output, slicegrid, thrfn); +} + +void create_pad(const ExPolygons &sup_blueprint, + const ExPolygons &model_blueprint, + TriangleMesh & out, + const PadConfig & cfg, + ThrowOnCancel thr) +{ + Contour3D t = create_pad_geometry(sup_blueprint, model_blueprint, cfg, thr); + out.merge(mesh(std::move(t))); +} + +std::string PadConfig::validate() const +{ + static const double constexpr MIN_BRIM_SIZE_MM = .1; + + if (brim_size_mm < MIN_BRIM_SIZE_MM || + bottom_offset() > brim_size_mm + wing_distance() || + get_waffle_offset(*this) <= MIN_BRIM_SIZE_MM) + return L("Pad brim size is too small for the current configuration."); + + return ""; +} + +}} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/SLAPad.hpp b/src/libslic3r/SLA/SLAPad.hpp new file mode 100644 index 000000000..4abcdd281 --- /dev/null +++ b/src/libslic3r/SLA/SLAPad.hpp @@ -0,0 +1,94 @@ +#ifndef SLABASEPOOL_HPP +#define SLABASEPOOL_HPP + +#include +#include +#include +#include + +namespace Slic3r { + +class ExPolygon; +class Polygon; +using ExPolygons = std::vector; +using Polygons = std::vector; + +class TriangleMesh; + +namespace sla { + +using ThrowOnCancel = std::function; + +/// Calculate the polygon representing the silhouette. +void pad_blueprint( + const TriangleMesh &mesh, // input mesh + ExPolygons & output, // Output will be merged with + const std::vector &, // Exact Z levels to sample + ThrowOnCancel thrfn = [] {}); // Function that throws if cancel was requested + +void pad_blueprint( + const TriangleMesh &mesh, + ExPolygons & output, + float samplingheight = 0.1f, // The height range to sample + float layerheight = 0.05f, // The sampling height + ThrowOnCancel thrfn = [] {}); + +struct PadConfig { + double wall_thickness_mm = 1.; + double wall_height_mm = 1.; + double max_merge_dist_mm = 50; + double wall_slope = std::atan(1.0); // Universal constant for Pi/4 + double brim_size_mm = 1.6; + + struct EmbedObject { + double object_gap_mm = 1.; + double stick_stride_mm = 10.; + double stick_width_mm = 0.5; + double stick_penetration_mm = 0.1; + bool enabled = false; + bool everywhere = false; + operator bool() const { return enabled; } + } embed_object; + + inline PadConfig() = default; + inline PadConfig(double thickness, + double height, + double mergedist, + double slope) + : wall_thickness_mm(thickness) + , wall_height_mm(height) + , max_merge_dist_mm(mergedist) + , wall_slope(slope) + {} + + inline double bottom_offset() const + { + return (wall_thickness_mm + wall_height_mm) / std::tan(wall_slope); + } + + inline double wing_distance() const + { + return wall_height_mm / std::tan(wall_slope); + } + + inline double full_height() const + { + return wall_height_mm + wall_thickness_mm; + } + + /// Returns the elevation needed for compensating the pad. + inline double required_elevation() const { return wall_thickness_mm; } + + std::string validate() const; +}; + +void create_pad(const ExPolygons &support_contours, + const ExPolygons &model_contours, + TriangleMesh & output_mesh, + const PadConfig & = PadConfig(), + ThrowOnCancel throw_on_cancel = []{}); + +} // namespace sla +} // namespace Slic3r + +#endif // SLABASEPOOL_HPP diff --git a/src/libslic3r/SLA/SLARaster.cpp b/src/libslic3r/SLA/SLARaster.cpp index 32a88b1b5..091cadd23 100644 --- a/src/libslic3r/SLA/SLARaster.cpp +++ b/src/libslic3r/SLA/SLARaster.cpp @@ -5,6 +5,7 @@ #include "SLARaster.hpp" #include "libslic3r/ExPolygon.hpp" +#include "libslic3r/MTUtils.hpp" #include // For rasterizing @@ -32,25 +33,30 @@ inline const ClipperLib::Paths& holes(const ClipperLib::Polygon& p) { return p.H namespace sla { +const Raster::TMirroring Raster::NoMirror = {false, false}; +const Raster::TMirroring Raster::MirrorX = {true, false}; +const Raster::TMirroring Raster::MirrorY = {false, true}; +const Raster::TMirroring Raster::MirrorXY = {true, true}; + + +using TPixelRenderer = agg::pixfmt_gray8; // agg::pixfmt_rgb24; +using TRawRenderer = agg::renderer_base; +using TPixel = TPixelRenderer::color_type; +using TRawBuffer = agg::rendering_buffer; +using TBuffer = std::vector; + +using TRendererAA = agg::renderer_scanline_aa_solid; + class Raster::Impl { public: - using TPixelRenderer = agg::pixfmt_gray8; // agg::pixfmt_rgb24; - using TRawRenderer = agg::renderer_base; - using TPixel = TPixelRenderer::color_type; - using TRawBuffer = agg::rendering_buffer; - - using TBuffer = std::vector; - - using TRendererAA = agg::renderer_scanline_aa_solid; static const TPixel ColorWhite; static const TPixel ColorBlack; - using Format = Raster::Format; + using Format = Raster::RawData; private: Raster::Resolution m_resolution; -// Raster::PixelDim m_pxdim; Raster::PixelDim m_pxdim_scaled; // used for scaled coordinate polygons TBuffer m_buf; TRawBuffer m_rbuf; @@ -59,74 +65,49 @@ private: TRendererAA m_renderer; std::function m_gammafn; - std::array m_mirror; - Format m_fmt = Format::PNG; + Trafo m_trafo; inline void flipy(agg::path_storage& path) const { - path.flip_y(0, m_resolution.height_px); + path.flip_y(0, double(m_resolution.height_px)); } inline void flipx(agg::path_storage& path) const { - path.flip_x(0, m_resolution.width_px); + path.flip_x(0, double(m_resolution.width_px)); } public: - - inline Impl(const Raster::Resolution& res, const Raster::PixelDim &pd, - const std::array& mirror, double gamma = 1.0): - m_resolution(res), -// m_pxdim(pd), - m_pxdim_scaled(SCALING_FACTOR / pd.w_mm, SCALING_FACTOR / pd.h_mm), - m_buf(res.pixels()), - m_rbuf(reinterpret_cast(m_buf.data()), - res.width_px, res.height_px, - int(res.width_px*TPixelRenderer::num_components)), - m_pixfmt(m_rbuf), - m_raw_renderer(m_pixfmt), - m_renderer(m_raw_renderer), - m_mirror(mirror) + inline Impl(const Raster::Resolution & res, + const Raster::PixelDim & pd, + const Trafo &trafo) + : m_resolution(res) + , m_pxdim_scaled(SCALING_FACTOR / pd.w_mm, SCALING_FACTOR / pd.h_mm) + , m_buf(res.pixels()) + , m_rbuf(reinterpret_cast(m_buf.data()), + unsigned(res.width_px), + unsigned(res.height_px), + int(res.width_px * TPixelRenderer::num_components)) + , m_pixfmt(m_rbuf) + , m_raw_renderer(m_pixfmt) + , m_renderer(m_raw_renderer) + , m_trafo(trafo) { m_renderer.color(ColorWhite); - if(gamma > 0) m_gammafn = agg::gamma_power(gamma); + if (trafo.gamma > 0) m_gammafn = agg::gamma_power(trafo.gamma); else m_gammafn = agg::gamma_threshold(0.5); clear(); } - - inline Impl(const Raster::Resolution& res, - const Raster::PixelDim &pd, - Format fmt, - double gamma = 1.0): - Impl(res, pd, {false, false}, gamma) - { - switch (fmt) { - case Format::PNG: m_mirror = {false, true}; break; - case Format::RAW: m_mirror = {false, false}; break; - } - m_fmt = fmt; - } template void draw(const P &poly) { agg::rasterizer_scanline_aa<> ras; agg::scanline_p8 scanlines; ras.gamma(m_gammafn); - - auto&& path = to_path(contour(poly)); - if(m_mirror[X]) flipx(path); - if(m_mirror[Y]) flipy(path); - - ras.add_path(path); - - for(auto& h : holes(poly)) { - auto&& holepath = to_path(h); - if(m_mirror[X]) flipx(holepath); - if(m_mirror[Y]) flipy(holepath); - ras.add_path(holepath); - } - + ras.add_path(to_path(contour(poly))); + for(auto& h : holes(poly)) ras.add_path(to_path(h)); + agg::render_scanlines(ras, scanlines, m_renderer); } @@ -135,11 +116,16 @@ public: } inline TBuffer& buffer() { return m_buf; } + inline const TBuffer& buffer() const { return m_buf; } - inline Format format() const { return m_fmt; } inline const Raster::Resolution resolution() { return m_resolution; } - + inline const Raster::PixelDim pixdim() + { + return {SCALING_FACTOR / m_pxdim_scaled.w_mm, + SCALING_FACTOR / m_pxdim_scaled.h_mm}; + } + private: inline double getPx(const Point& p) { return p(0) * m_pxdim_scaled.w_mm; @@ -162,49 +148,67 @@ private: return p.Y * m_pxdim_scaled.h_mm; } - template agg::path_storage to_path(const PointVec& poly) + template agg::path_storage _to_path(const PointVec& v) { agg::path_storage path; - auto it = poly.begin(); + auto it = v.begin(); path.move_to(getPx(*it), getPy(*it)); + while(++it != v.end()) path.line_to(getPx(*it), getPy(*it)); + path.line_to(getPx(v.front()), getPy(v.front())); + + return path; + } + + template agg::path_storage _to_path_flpxy(const PointVec& v) + { + agg::path_storage path; + + auto it = v.begin(); + path.move_to(getPy(*it), getPx(*it)); + while(++it != v.end()) path.line_to(getPy(*it), getPx(*it)); + path.line_to(getPy(v.front()), getPx(v.front())); + + return path; + } + + template agg::path_storage to_path(const PointVec &v) + { + auto path = m_trafo.flipXY ? _to_path_flpxy(v) : _to_path(v); + + path.translate_all_paths(m_trafo.origin_x * m_pxdim_scaled.w_mm, + m_trafo.origin_y * m_pxdim_scaled.h_mm); + + if(m_trafo.mirror_x) flipx(path); + if(m_trafo.mirror_y) flipy(path); - while(++it != poly.end()) - path.line_to(getPx(*it), getPy(*it)); - - path.line_to(getPx(poly.front()), getPy(poly.front())); return path; } }; -const Raster::Impl::TPixel Raster::Impl::ColorWhite = Raster::Impl::TPixel(255); -const Raster::Impl::TPixel Raster::Impl::ColorBlack = Raster::Impl::TPixel(0); +const TPixel Raster::Impl::ColorWhite = TPixel(255); +const TPixel Raster::Impl::ColorBlack = TPixel(0); + +Raster::Raster() { reset(); } + +Raster::Raster(const Raster::Resolution &r, + const Raster::PixelDim & pd, + const Raster::Trafo & tr) +{ + reset(r, pd, tr); +} -template<> Raster::Raster() { reset(); }; Raster::~Raster() = default; -// Raster::Raster(Raster &&m) = default; -// Raster& Raster::operator=(Raster&&) = default; - -// FIXME: remove after migrating to higher version of windows compiler -Raster::Raster(Raster &&m): m_impl(std::move(m.m_impl)) {} -Raster& Raster::operator=(Raster &&m) { - m_impl = std::move(m.m_impl); return *this; -} +Raster::Raster(Raster &&m) = default; +Raster &Raster::operator=(Raster &&) = default; void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd, - Format fmt, double gamma) + const Trafo &trafo) { m_impl.reset(); - m_impl.reset(new Impl(r, pd, fmt, gamma)); -} - -void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd, - const std::array& mirror, double gamma) -{ - m_impl.reset(); - m_impl.reset(new Impl(r, pd, mirror, gamma)); + m_impl.reset(new Impl(r, pd, trafo)); } void Raster::reset() @@ -214,9 +218,16 @@ void Raster::reset() Raster::Resolution Raster::resolution() const { - if(m_impl) return m_impl->resolution(); + if (m_impl) return m_impl->resolution(); + + return Resolution{0, 0}; +} - return Resolution(0, 0); +Raster::PixelDim Raster::pixel_dimensions() const +{ + if (m_impl) return m_impl->pixdim(); + + return PixelDim{0., 0.}; } void Raster::clear() @@ -227,103 +238,83 @@ void Raster::clear() void Raster::draw(const ExPolygon &expoly) { + assert(m_impl); m_impl->draw(expoly); } void Raster::draw(const ClipperLib::Polygon &poly) { + assert(m_impl); m_impl->draw(poly); } -void Raster::save(std::ostream& stream, Format fmt) +uint8_t Raster::read_pixel(size_t x, size_t y) const { - assert(m_impl); - if(!stream.good()) return; - - switch(fmt) { - case Format::PNG: { - auto& b = m_impl->buffer(); - size_t out_len = 0; - void * rawdata = tdefl_write_image_to_png_file_in_memory( - b.data(), - int(resolution().width_px), - int(resolution().height_px), 1, &out_len); - - if(rawdata == nullptr) break; - - stream.write(static_cast(rawdata), - std::streamsize(out_len)); - - MZ_FREE(rawdata); - - break; - } - case Format::RAW: { - stream << "P5 " - << m_impl->resolution().width_px << " " - << m_impl->resolution().height_px << " " - << "255 "; - - auto sz = m_impl->buffer().size()*sizeof(Impl::TBuffer::value_type); - stream.write(reinterpret_cast(m_impl->buffer().data()), - std::streamsize(sz)); - } - } + assert (m_impl); + TPixel::value_type px; + m_impl->buffer()[y * resolution().width_px + x].get(px); + return px; } -void Raster::save(std::ostream &stream) +PNGImage & PNGImage::serialize(const Raster &raster) { - save(stream, m_impl->format()); + size_t s = 0; + m_buffer.clear(); + + void *rawdata = tdefl_write_image_to_png_file_in_memory( + get_internals(raster).buffer().data(), + int(raster.resolution().width_px), + int(raster.resolution().height_px), 1, &s); + + // On error, data() will return an empty vector. No other info can be + // retrieved from miniz anyway... + if (rawdata == nullptr) return *this; + + auto ptr = static_cast(rawdata); + + m_buffer.reserve(s); + std::copy(ptr, ptr + s, std::back_inserter(m_buffer)); + + MZ_FREE(rawdata); + return *this; } -RawBytes Raster::save(Format fmt) +std::ostream &operator<<(std::ostream &stream, const Raster::RawData &bytes) { - assert(m_impl); - - std::vector data; size_t s = 0; - - switch(fmt) { - case Format::PNG: { - void *rawdata = tdefl_write_image_to_png_file_in_memory( - m_impl->buffer().data(), - int(resolution().width_px), - int(resolution().height_px), 1, &s); - - if(rawdata == nullptr) break; - auto ptr = static_cast(rawdata); - - data.reserve(s); std::copy(ptr, ptr + s, std::back_inserter(data)); - - MZ_FREE(rawdata); - break; - } - case Format::RAW: { - auto header = std::string("P5 ") + - std::to_string(m_impl->resolution().width_px) + " " + - std::to_string(m_impl->resolution().height_px) + " " + "255 "; - - auto sz = m_impl->buffer().size()*sizeof(Impl::TBuffer::value_type); - s = sz + header.size(); - - data.reserve(s); - - auto buff = reinterpret_cast(m_impl->buffer().data()); - std::copy(header.begin(), header.end(), std::back_inserter(data)); - std::copy(buff, buff+sz, std::back_inserter(data)); - - break; - } - } - - return {std::move(data)}; + stream.write(reinterpret_cast(bytes.data()), + std::streamsize(bytes.size())); + + return stream; } -RawBytes Raster::save() +Raster::RawData::~RawData() = default; + +PPMImage & PPMImage::serialize(const Raster &raster) { - return save(m_impl->format()); + auto header = std::string("P5 ") + + std::to_string(raster.resolution().width_px) + " " + + std::to_string(raster.resolution().height_px) + " " + "255 "; + + const auto &impl = get_internals(raster); + auto sz = impl.buffer().size() * sizeof(TBuffer::value_type); + size_t s = sz + header.size(); + + m_buffer.clear(); + m_buffer.reserve(s); + + auto buff = reinterpret_cast(impl.buffer().data()); + std::copy(header.begin(), header.end(), std::back_inserter(m_buffer)); + std::copy(buff, buff+sz, std::back_inserter(m_buffer)); + + return *this; } +const Raster::Impl &Raster::RawData::get_internals(const Raster &raster) +{ + return *raster.m_impl; } -} + +} // namespace sla +} // namespace Slic3r #endif // SLARASTER_CPP diff --git a/src/libslic3r/SLA/SLARaster.hpp b/src/libslic3r/SLA/SLARaster.hpp index 8b27fd153..b3d73536b 100644 --- a/src/libslic3r/SLA/SLARaster.hpp +++ b/src/libslic3r/SLA/SLARaster.hpp @@ -8,45 +8,13 @@ #include #include +#include + namespace ClipperLib { struct Polygon; } -namespace Slic3r { - -class ExPolygon; - +namespace Slic3r { namespace sla { -// Raw byte buffer paired with its size. Suitable for compressed PNG data. -class RawBytes { - - std::vector m_buffer; -public: - - RawBytes() = default; - RawBytes(std::vector&& data): m_buffer(std::move(data)) {} - - size_t size() const { return m_buffer.size(); } - const uint8_t * data() { return m_buffer.data(); } - - RawBytes(const RawBytes&) = delete; - RawBytes& operator=(const RawBytes&) = delete; - - // ///////////////////////////////////////////////////////////////////////// - // FIXME: the following is needed for MSVC2013 compatibility - // ///////////////////////////////////////////////////////////////////////// - - // RawBytes(RawBytes&&) = default; - // RawBytes& operator=(RawBytes&&) = default; - - RawBytes(RawBytes&& mv) : m_buffer(std::move(mv.m_buffer)) {} - RawBytes& operator=(RawBytes&& mv) { - m_buffer = std::move(mv.m_buffer); - return *this; - } - - // ///////////////////////////////////////////////////////////////////////// -}; - /** * @brief Raster captures an anti-aliased monochrome canvas where vectorial * polygons can be rasterized. Fill color is always white and the background is @@ -60,10 +28,28 @@ class Raster { std::unique_ptr m_impl; public: - /// Supported compression types - enum class Format { - RAW, //!> Uncompressed pixel data - PNG //!> PNG compression + // Raw byte buffer paired with its size. Suitable for compressed image data. + class RawData + { + protected: + std::vector m_buffer; + const Impl& get_internals(const Raster& raster); + public: + RawData() = default; + RawData(std::vector&& data): m_buffer(std::move(data)) {} + virtual ~RawData(); + + RawData(const RawData &) = delete; + RawData &operator=(const RawData &) = delete; + + RawData(RawData &&) = default; + RawData &operator=(RawData &&) = default; + + size_t size() const { return m_buffer.size(); } + const uint8_t * data() const { return m_buffer.data(); } + + virtual RawData& serialize(const Raster &/*raster*/) { return *this; } + virtual std::string get_file_extension() const = 0; }; /// Type that represents a resolution in pixels. @@ -86,11 +72,36 @@ public: w_mm(px_width_mm), h_mm(px_height_mm) {} }; - /// Constructor taking the resolution and the pixel dimension. - template Raster(Args...args) { - reset(std::forward(args)...); - } - + enum Orientation { roLandscape, roPortrait }; + + using TMirroring = std::array; + static const TMirroring NoMirror; + static const TMirroring MirrorX; + static const TMirroring MirrorY; + static const TMirroring MirrorXY; + + struct Trafo { + bool mirror_x = false, mirror_y = false, flipXY = false; + coord_t origin_x = 0, origin_y = 0; + + // If gamma is zero, thresholding will be performed which disables AA. + double gamma = 1.; + + // Portrait orientation will make sure the drawed polygons are rotated + // by 90 degrees. + Trafo(Orientation o = roLandscape, const TMirroring &mirror = NoMirror) + // XY flipping implicitly does an X mirror + : mirror_x(o == roPortrait ? !mirror[0] : mirror[0]) + , mirror_y(!mirror[1]) // Makes raster origin to be top left corner + , flipXY(o == roPortrait) + {} + }; + + Raster(); + Raster(const Resolution &r, + const PixelDim & pd, + const Trafo & tr = {}); + Raster(const Raster& cpy) = delete; Raster& operator=(const Raster& cpy) = delete; Raster(Raster&& m); @@ -98,18 +109,10 @@ public: ~Raster(); /// Reallocated everything for the given resolution and pixel dimension. - /// The third parameter is either the X, Y mirroring or a supported format - /// for which the correct mirroring will be configured. - void reset(const Resolution&, - const PixelDim&, - const std::array& mirror, - double gamma = 1.0); - - void reset(const Resolution& r, - const PixelDim& pd, - Format o, - double gamma = 1.0); - + void reset(const Resolution& r, + const PixelDim& pd, + const Trafo &tr = {}); + /** * Release the allocated resources. Drawing in this state ends in * unspecified behavior. @@ -118,6 +121,7 @@ public: /// Get the resolution of the raster. Resolution resolution() const; + PixelDim pixel_dimensions() const; /// Clear the raster with black color. void clear(); @@ -126,24 +130,28 @@ public: void draw(const ExPolygon& poly); void draw(const ClipperLib::Polygon& poly); - // Saving the raster: - // It is possible to override the format given in the constructor but - // be aware that the mirroring will not be modified. - - /// Save the raster on the specified stream. - void save(std::ostream& stream, Format); - void save(std::ostream& stream); + uint8_t read_pixel(size_t w, size_t h) const; + + inline bool empty() const { return ! bool(m_impl); } - /// Save into a continuous byte stream which is returned. - RawBytes save(Format fmt); - RawBytes save(); }; -// This prevents the duplicate default constructor warning on MSVC2013 -template<> Raster::Raster(); +class PNGImage: public Raster::RawData { +public: + PNGImage& serialize(const Raster &raster) override; + std::string get_file_extension() const override { return "png"; } +}; +class PPMImage: public Raster::RawData { +public: + PPMImage& serialize(const Raster &raster) override; + std::string get_file_extension() const override { return "ppm"; } +}; + +std::ostream& operator<<(std::ostream &stream, const Raster::RawData &bytes); } // sla } // Slic3r + #endif // SLARASTER_HPP diff --git a/src/libslic3r/SLA/SLARasterWriter.cpp b/src/libslic3r/SLA/SLARasterWriter.cpp index 3e6f015d4..6ac86827e 100644 --- a/src/libslic3r/SLA/SLARasterWriter.cpp +++ b/src/libslic3r/SLA/SLARasterWriter.cpp @@ -10,7 +10,7 @@ namespace Slic3r { namespace sla { -std::string SLARasterWriter::createIniContent(const std::string& projectname) const +std::string RasterWriter::createIniContent(const std::string& projectname) const { std::string out("action = print\njobDir = "); out += projectname + "\n"; @@ -21,65 +21,51 @@ std::string SLARasterWriter::createIniContent(const std::string& projectname) co return out; } -void SLARasterWriter::flpXY(ClipperLib::Polygon &poly) -{ - for(auto& p : poly.Contour) std::swap(p.X, p.Y); - std::reverse(poly.Contour.begin(), poly.Contour.end()); - - for(auto& h : poly.Holes) { - for(auto& p : h) std::swap(p.X, p.Y); - std::reverse(h.begin(), h.end()); - } -} +RasterWriter::RasterWriter(const Raster::Resolution &res, + const Raster::PixelDim & pixdim, + const Raster::Trafo & trafo, + double gamma) + : m_res(res), m_pxdim(pixdim), m_trafo(trafo), m_gamma(gamma) +{} -void SLARasterWriter::flpXY(ExPolygon &poly) -{ - for(auto& p : poly.contour.points) p = Point(p.y(), p.x()); - std::reverse(poly.contour.points.begin(), poly.contour.points.end()); - - for(auto& h : poly.holes) { - for(auto& p : h.points) p = Point(p.y(), p.x()); - std::reverse(h.points.begin(), h.points.end()); - } -} - -SLARasterWriter::SLARasterWriter(const Raster::Resolution &res, - const Raster::PixelDim &pixdim, - const std::array &mirror, - double gamma) - : m_res(res), m_pxdim(pixdim), m_mirror(mirror), m_gamma(gamma) -{ - // PNG raster will implicitly do an Y mirror - m_mirror[1] = !m_mirror[1]; -} - -void SLARasterWriter::save(const std::string &fpath, const std::string &prjname) +void RasterWriter::save(const std::string &fpath, const std::string &prjname) { try { Zipper zipper(fpath); // zipper with no compression - - std::string project = prjname.empty()? - boost::filesystem::path(fpath).stem().string() : prjname; - + save(zipper, prjname); + zipper.finalize(); + } catch(std::exception& e) { + BOOST_LOG_TRIVIAL(error) << e.what(); + // Rethrow the exception + throw; + } +} + +void RasterWriter::save(Zipper &zipper, const std::string &prjname) +{ + try { + std::string project = + prjname.empty() ? + boost::filesystem::path(zipper.get_filename()).stem().string() : + prjname; + zipper.add_entry("config.ini"); - + zipper << createIniContent(project); - + for(unsigned i = 0; i < m_layers_rst.size(); i++) { if(m_layers_rst[i].rawbytes.size() > 0) { char lyrnum[6]; std::sprintf(lyrnum, "%.5d", i); auto zfilename = project + lyrnum + ".png"; - + // Add binary entry to the zipper zipper.add_entry(zfilename, m_layers_rst[i].rawbytes.data(), m_layers_rst[i].rawbytes.size()); } } - - zipper.finalize(); } catch(std::exception& e) { BOOST_LOG_TRIVIAL(error) << e.what(); // Rethrow the exception @@ -103,7 +89,7 @@ std::string get_cfg_value(const DynamicPrintConfig &cfg, const std::string &key) } // namespace -void SLARasterWriter::set_config(const DynamicPrintConfig &cfg) +void RasterWriter::set_config(const DynamicPrintConfig &cfg) { m_config["layerHeight"] = get_cfg_value(cfg, "layer_height"); m_config["expTime"] = get_cfg_value(cfg, "exposure_time"); @@ -114,11 +100,11 @@ void SLARasterWriter::set_config(const DynamicPrintConfig &cfg) 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::current_utc_time2str(); + m_config["fileCreationTimestamp"] = Utils::utc_timestamp(); m_config["prusaSlicerVersion"] = SLIC3R_BUILD_ID; } -void SLARasterWriter::set_statistics(const PrintStatistics &stats) +void RasterWriter::set_statistics(const PrintStatistics &stats) { m_config["usedMaterial"] = std::to_string(stats.used_material); m_config["numFade"] = std::to_string(stats.num_fade); diff --git a/src/libslic3r/SLA/SLARasterWriter.hpp b/src/libslic3r/SLA/SLARasterWriter.hpp index b9202c464..93a315c82 100644 --- a/src/libslic3r/SLA/SLARasterWriter.hpp +++ b/src/libslic3r/SLA/SLARasterWriter.hpp @@ -12,23 +12,21 @@ #include "libslic3r/PrintConfig.hpp" #include "SLARaster.hpp" +#include "libslic3r/Zipper.hpp" namespace Slic3r { namespace sla { -// Implementation for PNG raster output +// API to write the zipped sla output layers and metadata. +// Implementation uses PNG raster output. // Be aware that if a large number of layers are allocated, it can very well // exhaust the available memory especially on 32 bit platform. // This class is designed to be used in parallel mode. Layers have an ID and // each layer can be written and compressed independently (in parallel). // At the end when all layers where written, the save method can be used to // write out the result into a zipped archive. -class SLARasterWriter +class RasterWriter { public: - enum Orientation { - roLandscape, - roPortrait - }; // Used for addressing parameters of set_statistics() struct PrintStatistics @@ -45,7 +43,7 @@ private: // A struct to bind the raster image data and its compressed bytes together. struct Layer { Raster raster; - RawBytes rawbytes; + PNGImage rawbytes; Layer() = default; @@ -61,78 +59,64 @@ private: // parallel. Later we can write every layer to the disk sequentially. std::vector m_layers_rst; Raster::Resolution m_res; - Raster::PixelDim m_pxdim; - std::array m_mirror; - double m_gamma; - + Raster::PixelDim m_pxdim; + Raster::Trafo m_trafo; + double m_gamma; + std::map m_config; std::string createIniContent(const std::string& projectname) const; - - static void flpXY(ClipperLib::Polygon& poly); - static void flpXY(ExPolygon& poly); public: - SLARasterWriter(const Raster::Resolution &res, - const Raster::PixelDim &pixdim, - const std::array &mirror, - double gamma = 1.); + + // SLARasterWriter is using Raster in custom mirroring mode + RasterWriter(const Raster::Resolution &res, + const Raster::PixelDim & pixdim, + const Raster::Trafo & trafo, + double gamma = 1.); - SLARasterWriter(const SLARasterWriter& ) = delete; - SLARasterWriter& operator=(const SLARasterWriter&) = delete; - SLARasterWriter(SLARasterWriter&& m) = default; - SLARasterWriter& operator=(SLARasterWriter&&) = default; + RasterWriter(const RasterWriter& ) = delete; + RasterWriter& operator=(const RasterWriter&) = delete; + RasterWriter(RasterWriter&& m) = default; + RasterWriter& operator=(RasterWriter&&) = default; inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); } inline unsigned layers() const { return unsigned(m_layers_rst.size()); } - template void draw_polygon(const Poly& p, unsigned lyr, - Orientation o = roPortrait) + template void draw_polygon(const Poly& p, unsigned lyr) { assert(lyr < m_layers_rst.size()); - - switch (o) { - case roPortrait: { - Poly poly(p); - flpXY(poly); - m_layers_rst[lyr].raster.draw(poly); - break; - } - case roLandscape: - m_layers_rst[lyr].raster.draw(p); - break; - } + m_layers_rst[lyr].raster.draw(p); } inline void begin_layer(unsigned lyr) { if(m_layers_rst.size() <= lyr) m_layers_rst.resize(lyr+1); - m_layers_rst[lyr].raster.reset(m_res, m_pxdim, m_mirror, m_gamma); + m_layers_rst[lyr].raster.reset(m_res, m_pxdim, m_trafo); } inline void begin_layer() { m_layers_rst.emplace_back(); - m_layers_rst.front().raster.reset(m_res, m_pxdim, m_mirror, m_gamma); + m_layers_rst.front().raster.reset(m_res, m_pxdim, m_trafo); } inline void finish_layer(unsigned lyr_id) { assert(lyr_id < m_layers_rst.size()); - m_layers_rst[lyr_id].rawbytes = - m_layers_rst[lyr_id].raster.save(Raster::Format::PNG); + m_layers_rst[lyr_id].rawbytes.serialize(m_layers_rst[lyr_id].raster); m_layers_rst[lyr_id].raster.reset(); } inline void finish_layer() { if(!m_layers_rst.empty()) { - m_layers_rst.back().rawbytes = - m_layers_rst.back().raster.save(Raster::Format::PNG); + m_layers_rst.back().rawbytes.serialize(m_layers_rst.back().raster); m_layers_rst.back().raster.reset(); } } void save(const std::string &fpath, const std::string &prjname = ""); + 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/SLASpatIndex.hpp b/src/libslic3r/SLA/SLASpatIndex.hpp index 90dcdc362..20b6fcd58 100644 --- a/src/libslic3r/SLA/SLASpatIndex.hpp +++ b/src/libslic3r/SLA/SLASpatIndex.hpp @@ -39,14 +39,19 @@ public: insert(std::make_pair(v, unsigned(idx))); } - std::vector query(std::function); - std::vector nearest(const Vec3d&, unsigned k); + std::vector query(std::function) const; + std::vector nearest(const Vec3d&, unsigned k) const; + std::vector query(const Vec3d &v, unsigned k) const // wrapper + { + return nearest(v, k); + } // For testing size_t size() const; bool empty() const { return size() == 0; } void foreach(std::function fn); + void foreach(std::function fn) const; }; using BoxIndexEl = std::pair; diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index a3e88243f..8d4c8ec1f 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -7,7 +7,7 @@ #include "SLASupportTree.hpp" #include "SLABoilerPlate.hpp" #include "SLASpatIndex.hpp" -#include "SLABasePool.hpp" +#include "SLASupportTreeBuilder.hpp" #include #include @@ -17,51 +17,14 @@ #include #include #include +#include +#include #include //! macro used to mark string used at localization, //! return same string #define L(s) Slic3r::I18N::translate(s) -/** - * Terminology: - * - * Support point: - * The point on the model surface that needs support. - * - * Pillar: - * A thick column that spans from a support point to the ground and has - * a thick cone shaped base where it touches the ground. - * - * Ground facing support point: - * A support point that can be directly connected with the ground with a pillar - * that does not collide or cut through the model. - * - * Non ground facing support point: - * A support point that cannot be directly connected with the ground (only with - * the model surface). - * - * Head: - * The pinhead that connects to the model surface with the sharp end end - * to a pillar or bridge stick with the dull end. - * - * Headless support point: - * A support point on the model surface for which there is not enough place for - * the head. It is either in a hole or there is some barrier that would collide - * with the head geometry. The headless support point can be ground facing and - * non ground facing as well. - * - * Bridge: - * A stick that connects two pillars or a head with a pillar. - * - * Junction: - * A small ball in the intersection of two or more sticks (pillar, bridge, ...) - * - * CompactBridge: - * A bridge that connects a headless support point with the model surface or a - * nearby pillar. - */ - namespace Slic3r { namespace sla { @@ -80,2527 +43,16 @@ const unsigned SupportConfig::optimizer_max_iterations = 1000; const unsigned SupportConfig::pillar_cascade_neighbors = 3; const unsigned SupportConfig::max_bridges_on_pillar = 3; -using Coordf = double; -using Portion = std::tuple; - -// Set this to true to enable full parallelism in this module. -// Only the well tested parts will be concurrent if this is set to false. -const constexpr bool USE_FULL_CONCURRENCY = false; - -template struct _ccr {}; - -template<> struct _ccr -{ - using Mutex = SpinMutex; - - template - static inline void enumerate(It from, It to, Fn fn) - { - using TN = size_t; - auto iN = to - from; - TN N = iN < 0 ? 0 : TN(iN); - - tbb::parallel_for(TN(0), N, [from, fn](TN n) { fn(*(from + n), n); }); - } -}; - -template<> struct _ccr -{ - struct Mutex { inline void lock() {} inline void unlock() {} }; - - template - static inline void enumerate(It from, It to, Fn fn) - { - for (auto it = from; it != to; ++it) fn(*it, it - from); - } -}; - -using ccr = _ccr; -using ccr_seq = _ccr; -using ccr_par = _ccr; - -inline Portion make_portion(double a, double b) { - return std::make_tuple(a, b); -} - -template double distance(const Vec& p) { - return std::sqrt(p.transpose() * p); -} - -template double distance(const Vec& pp1, const Vec& pp2) { - auto p = pp2 - pp1; - return distance(p); -} - -Contour3D sphere(double rho, Portion portion = make_portion(0.0, 2.0*PI), - double fa=(2*PI/360)) { - - Contour3D ret; - - // prohibit close to zero radius - if(rho <= 1e-6 && rho >= -1e-6) return ret; - - Pointf3s& vertices = ret.points; - std::vector& facets = ret.indices; - - // Algorithm: - // Add points one-by-one to the sphere grid and form facets using relative - // coordinates. Sphere is composed effectively of a mesh of stacked circles. - - // adjust via rounding to get an even multiple for any provided angle. - double angle = (2*PI / floor(2*PI / fa)); - - // Ring to be scaled to generate the steps of the sphere - std::vector ring; - - for (double i = 0; i < 2*PI; i+=angle) ring.emplace_back(i); - - const auto sbegin = size_t(2*std::get<0>(portion)/angle); - const auto send = size_t(2*std::get<1>(portion)/angle); - - const size_t steps = ring.size(); - const double increment = 1.0 / double(steps); - - // special case: first ring connects to 0,0,0 - // insert and form facets. - if(sbegin == 0) - vertices.emplace_back(Vec3d(0.0, 0.0, -rho + increment*sbegin*2.0*rho)); - - int32_t id = int32_t(vertices.size()); - for (size_t i = 0; i < ring.size(); i++) { - // Fixed scaling - const double z = -rho + increment*rho*2.0 * (sbegin + 1.0); - // radius of the circle for this step. - const double r = std::sqrt(std::abs(rho*rho - z*z)); - Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r); - vertices.emplace_back(Vec3d(b(0), b(1), z)); - - if(sbegin == 0) - facets.emplace_back((i == 0) ? Vec3i32(int32_t(ring.size()), 0, 1) : - Vec3i32(id - 1, 0, id)); - ++ id; +void SupportTree::retrieve_full_mesh(TriangleMesh &outmesh) const { + outmesh.merge(retrieve_mesh(MeshType::Support)); + outmesh.merge(retrieve_mesh(MeshType::Pad)); } - // General case: insert and form facets for each step, - // joining it to the ring below it. - for (size_t s = sbegin + 2; s < send - 1; s++) { - const double z = -rho + increment*double(s*2.0*rho); - const double r = std::sqrt(std::abs(rho*rho - z*z)); - - for (size_t i = 0; i < ring.size(); i++) { - Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r); - vertices.emplace_back(Vec3d(b(0), b(1), z)); - auto id_ringsize = int32_t(id - int(ring.size())); - if (i == 0) { - // wrap around - facets.emplace_back(Vec3i32(id - 1, id, - id + int32_t(ring.size() - 1))); - facets.emplace_back(Vec3i32(id - 1, id_ringsize, id)); - } else { - facets.emplace_back(Vec3i32(id_ringsize - 1, id_ringsize, id)); - facets.emplace_back(Vec3i32(id - 1, id_ringsize - 1, id)); - } - id++; - } - } - - // special case: last ring connects to 0,0,rho*2.0 - // only form facets. - if(send >= size_t(2*PI / angle)) { - vertices.emplace_back(Vec3d(0.0, 0.0, -rho + increment*send*2.0*rho)); - for (size_t i = 0; i < ring.size(); i++) { - auto id_ringsize = int32_t(id - int(ring.size())); - if (i == 0) { - // third vertex is on the other side of the ring. - facets.emplace_back(Vec3i32(id - 1, id_ringsize, id)); - } else { - auto ci = int32_t(id_ringsize + int32_t(i)); - facets.emplace_back(Vec3i32(ci - 1, ci, id)); - } - } - } - id++; - - return ret; -} - -// Down facing cylinder in Z direction with arguments: -// r: radius -// 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 ret; - - int32_t steps = int32_t(ssteps); - Pointf3s& points = ret.points; - std::vector& indices = ret.indices; - points.reserve(2*ssteps); - double a = 2*PI/steps; - - Vec3d jp = sp; - Vec3d endp = {sp(X), sp(Y), sp(Z) + h}; - - // Upper circle points - for(int i = 0; i < steps; ++i) { - double phi = i*a; - double ex = endp(X) + r*std::cos(phi); - double ey = endp(Y) + r*std::sin(phi); - points.emplace_back(ex, ey, endp(Z)); - } - - // Lower circle points - for(int i = 0; i < steps; ++i) { - double phi = i*a; - double x = jp(X) + r*std::cos(phi); - double y = jp(Y) + r*std::sin(phi); - points.emplace_back(x, y, jp(Z)); - } - - // Now create long triangles connecting upper and lower circles - indices.reserve(2*ssteps); - int32_t offs = steps; - for(int i = 0; i < steps - 1; ++i) { - indices.emplace_back(i, i + offs, offs + i + 1); - indices.emplace_back(i, offs + i + 1, i + 1); - } - - // Last triangle connecting the first and last vertices - auto last = steps - 1; - indices.emplace_back(0, last, offs); - indices.emplace_back(last, offs + last, offs); - - // According to the slicing algorithms, we need to aid them with generating - // a watertight body. So we create a triangle fan for the upper and lower - // ending of the cylinder to close the geometry. - points.emplace_back(jp); int ci = int(points.size() - 1); - for(int32_t i = 0; i < steps - 1; ++i) - indices.emplace_back(int32_t(i + offs + 1), int32_t(i + offs), int32_t(ci)); - - indices.emplace_back(offs, int32_t(steps + offs - 1), int32_t(ci)); - - points.emplace_back(endp); ci = int(points.size() - 1); - for(int32_t i = 0; i < steps - 1; ++i) - indices.emplace_back(int32_t(ci), i, i + 1); - - indices.emplace_back(int32_t(steps - 1), int32_t(0), int32_t(ci)); - - return ret; -} - -struct Head { - Contour3D mesh; - - size_t steps = 45; - Vec3d dir = {0, 0, -1}; - Vec3d tr = {0, 0, 0}; - - double r_back_mm = 1; - double r_pin_mm = 0.5; - double width_mm = 2; - double penetration_mm = 0.5; - - // For identification purposes. This will be used as the index into the - // container holding the head structures. See SLASupportTree::Impl - long id = -1; - - // If there is a pillar connecting to this head, then the id will be set. - long pillar_id = -1; - - inline void invalidate() { id = -1; } - inline bool is_valid() const { return id >= 0; } - - Head(double r_big_mm, - double r_small_mm, - double length_mm, - double penetration, - Vec3d direction = {0, 0, -1}, // direction (normal to the dull end ) - Vec3d offset = {0, 0, 0}, // displacement - const size_t circlesteps = 45): - steps(circlesteps), dir(direction), tr(offset), - r_back_mm(r_big_mm), r_pin_mm(r_small_mm), width_mm(length_mm), - penetration_mm(penetration) - { - - // We create two spheres which will be connected with a robe that fits - // both circles perfectly. - - // Set up the model detail level - const double detail = 2*PI/steps; - - // We don't generate whole circles. Instead, we generate only the - // portions which are visible (not covered by the robe) To know the - // exact portion of the bottom and top circles we need to use some - // rules of tangent circles from which we can derive (using simple - // triangles the following relations: - - // The height of the whole mesh - const double h = r_big_mm + r_small_mm + width_mm; - double phi = PI/2 - std::acos( (r_big_mm - r_small_mm) / h ); - - // To generate a whole circle we would pass a portion of (0, Pi) - // To generate only a half horizontal circle we can pass (0, Pi/2) - // The calculated phi is an offset to the half circles needed to smooth - // the transition from the circle to the robe geometry - - Contour3D&& s1 = sphere(r_big_mm, make_portion(0, PI/2 + phi), detail); - Contour3D&& s2 = sphere(r_small_mm, make_portion(PI/2 + phi, PI), detail); - - for(Vec3d & p : s2.points) z(p) += h; - - mesh.merge(s1); - mesh.merge(s2); - - for(size_t idx1 = s1.points.size() - steps, idx2 = s1.points.size(); - idx1 < s1.points.size() - 1; - idx1++, idx2++) - { - int32_t i1s1 = int32_t(idx1), i1s2 = int32_t(idx2); - int32_t i2s1 = i1s1 + 1, i2s2 = i1s2 + 1; - - mesh.indices.emplace_back(i1s1, i2s1, i2s2); - mesh.indices.emplace_back(i1s1, i2s2, i1s2); - } - - int32_t i1s1 = int32_t(s1.points.size()) - int32_t(steps); - int32_t i2s1 = int32_t(s1.points.size()) - 1; - int32_t i1s2 = int32_t(s1.points.size()); - int32_t i2s2 = int32_t(s1.points.size()) + int32_t(steps) - 1; - - mesh.indices.emplace_back(i2s2, i2s1, i1s1); - mesh.indices.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) - for(auto& p : mesh.points) z(p) -= (h + r_small_mm - penetration_mm); - } - - void transform() - { - using Quaternion = Eigen::Quaternion; - - // We rotate the head to the specified direction The head's pointing - // side is facing upwards so this means that it would hold a support - // point with a normal pointing straight down. This is the reason of - // the -1 z coordinate - auto quatern = Quaternion::FromTwoVectors(Vec3d{0, 0, -1}, dir); - - for(auto& p : mesh.points) p = quatern * p + tr; - } - - double fullwidth() const { - return 2 * r_pin_mm + width_mm + 2*r_back_mm - penetration_mm; - } - - static double fullwidth(const SupportConfig& cfg) { - return 2 * cfg.head_front_radius_mm + cfg.head_width_mm + - 2 * cfg.head_back_radius_mm - cfg.head_penetration_mm; - } - - Vec3d junction_point() const { - return tr + ( 2 * r_pin_mm + width_mm + r_back_mm - penetration_mm)*dir; - } - - double request_pillar_radius(double radius) const { - const double rmax = r_back_mm; - return radius > 0 && radius < rmax ? radius : rmax; - } -}; - -struct Junction { - Contour3D mesh; - double r = 1; - size_t steps = 45; - Vec3d pos; - - long id = -1; - - Junction(const Vec3d& tr, double r_mm, size_t stepnum = 45): - r(r_mm), steps(stepnum), pos(tr) - { - mesh = sphere(r_mm, make_portion(0, PI), 2*PI/steps); - for(auto& p : mesh.points) p += tr; - } -}; - -struct Pillar { - Contour3D mesh; - Contour3D base; - double r = 1; - size_t steps = 0; - Vec3d endpt; - double height = 0; - - long id = -1; - - // If the pillar connects to a head, this is the id of that head - bool starts_from_head = true; // Could start from a junction as well - long start_junction_id = -1; - - // How many bridges are connected to this pillar - unsigned bridges = 0; - - // How many pillars are cascaded with this one - unsigned links = 0; - - Pillar(const Vec3d& jp, const Vec3d& endp, - double radius = 1, size_t st = 45): - r(radius), steps(st), endpt(endp), starts_from_head(false) - { - assert(steps > 0); - - height = jp(Z) - endp(Z); - if(height > EPSILON) { // Endpoint is below the starting point - - // We just create a bridge geometry with the pillar parameters and - // move the data. - Contour3D body = cylinder(radius, height, st, endp); - mesh.points.swap(body.points); - mesh.indices.swap(body.indices); - } - } - - Pillar(const Junction& junc, const Vec3d& endp): - Pillar(junc.pos, endp, junc.r, junc.steps){} - - Pillar(const Head& head, const Vec3d& endp, double radius = 1): - Pillar(head.junction_point(), endp, head.request_pillar_radius(radius), - head.steps) - { - } - - inline Vec3d startpoint() const { - return {endpt(X), endpt(Y), endpt(Z) + height}; - } - - inline const Vec3d& endpoint() const { return endpt; } - - Pillar& add_base(double baseheight = 3, double radius = 2) { - if(baseheight <= 0) return *this; - if(baseheight > height) baseheight = height; - - assert(steps >= 0); - auto last = int(steps - 1); - - if(radius < r ) radius = r; - - double a = 2*PI/steps; - double z = endpt(Z) + baseheight; - - for(size_t i = 0; i < steps; ++i) { - double phi = i*a; - double x = endpt(X) + r*std::cos(phi); - double y = endpt(Y) + r*std::sin(phi); - base.points.emplace_back(x, y, z); - } - - for(size_t i = 0; i < steps; ++i) { - double phi = i*a; - double x = endpt(X) + radius*std::cos(phi); - double y = endpt(Y) + radius*std::sin(phi); - base.points.emplace_back(x, y, z - baseheight); - } - - auto ep = endpt; ep(Z) += baseheight; - base.points.emplace_back(endpt); - base.points.emplace_back(ep); - - auto& indices = base.indices; - auto hcenter = int(base.points.size() - 1); - auto lcenter = int(base.points.size() - 2); - auto offs = int(steps); - for(int i = 0; i < last; ++i) { - indices.emplace_back(i, i + offs, offs + i + 1); - indices.emplace_back(i, offs + i + 1, i + 1); - indices.emplace_back(i, i + 1, hcenter); - indices.emplace_back(lcenter, offs + i + 1, offs + i); - } - - indices.emplace_back(0, last, offs); - indices.emplace_back(last, offs + last, offs); - indices.emplace_back(hcenter, last, 0); - indices.emplace_back(offs, offs + last, lcenter); - return *this; - } - - bool has_base() const { return !base.points.empty(); } -}; - -// A Bridge between two pillars (with junction endpoints) -struct Bridge { - Contour3D mesh; - double r = 0.8; - - long id = -1; - long start_jid = -1; - long end_jid = -1; - - // We should reduce the radius a tiny bit to help the convex hull algorithm - Bridge(const Vec3d& j1, const Vec3d& j2, - double r_mm = 0.8, size_t steps = 45): - r(r_mm) - { - using Quaternion = Eigen::Quaternion; - Vec3d dir = (j2 - j1).normalized(); - double d = distance(j2, j1); - - mesh = cylinder(r, d, steps); - - auto quater = Quaternion::FromTwoVectors(Vec3d{0,0,1}, dir); - for(auto& p : mesh.points) p = quater * p + j1; - } - - Bridge(const Junction& j1, const Junction& j2, double r_mm = 0.8): - Bridge(j1.pos, j2.pos, r_mm, j1.steps) {} - -}; - -// A bridge that spans from model surface to model surface with small connecting -// edges on the endpoints. Used for headless support points. -struct CompactBridge { - Contour3D mesh; - long id = -1; - - CompactBridge(const Vec3d& sp, - const Vec3d& ep, - const Vec3d& n, - double r, - bool endball = true, - size_t steps = 45) - { - Vec3d startp = sp + r * n; - Vec3d dir = (ep - startp).normalized(); - Vec3d endp = ep - r * dir; - - Bridge br(startp, endp, r, steps); - mesh.merge(br.mesh); - - // now add the pins - double fa = 2*PI/steps; - auto upperball = sphere(r, Portion{PI / 2 - fa, PI}, fa); - for(auto& p : upperball.points) p += startp; - - if(endball) { - auto lowerball = sphere(r, Portion{0, PI/2 + 2*fa}, fa); - for(auto& p : lowerball.points) p += endp; - mesh.merge(lowerball); - } - - mesh.merge(upperball); - } -}; - -// A wrapper struct around the base pool (pad) -struct Pad { - TriangleMesh tmesh; - PoolConfig cfg; - double zlevel = 0; - - Pad() = default; - - Pad(const TriangleMesh& support_mesh, - const ExPolygons& modelbase, - double ground_level, - const PoolConfig& pcfg) : - cfg(pcfg), - zlevel(ground_level + - sla::get_pad_fullheight(pcfg) - - sla::get_pad_elevation(pcfg)) - { - Polygons basep; - auto &thr = cfg.throw_on_cancel; - - thr(); - - // Get a sample for the pad from the support mesh - { - ExPolygons platetmp; - - float zstart = float(zlevel); - float zend = zstart + float(get_pad_fullheight(pcfg) + EPSILON); - - base_plate(support_mesh, platetmp, grid(zstart, zend, 0.1f), thr); - - // We don't need no... holes control... - for (const ExPolygon &bp : platetmp) - basep.emplace_back(std::move(bp.contour)); - } - - if(pcfg.embed_object) { - - // If the zero elevation mode is ON, we need to process the model - // base silhouette. Create the offsetted version and punch the - // breaksticks across its perimeter. - - ExPolygons modelbase_offs = modelbase; - - if (pcfg.embed_object.object_gap_mm > 0.0) - modelbase_offs - = offset_ex(modelbase_offs, - float(scaled(pcfg.embed_object.object_gap_mm))); - - // Create a spatial index of the support silhouette polygons. - // This will be used to check for intersections with the model - // silhouette polygons. If there is no intersection, then a certain - // part of the pad is redundant as it does not host any supports. - BoxIndex bindex; - { - unsigned idx = 0; - for(auto &bp : basep) { - auto bb = bp.bounding_box(); - bb.offset(float(scaled(pcfg.min_wall_thickness_mm))); - bindex.insert(bb, idx++); - } - } - - ExPolygons concaveh = offset_ex( - concave_hull(basep, pcfg.max_merge_distance_mm, thr), - scaled(pcfg.min_wall_thickness_mm)); - - // Punching the breaksticks across the offsetted polygon perimeters - auto pad_stickholes = reserve_vector(modelbase.size()); - for(auto& poly : modelbase_offs) { - - bool overlap = false; - for (const ExPolygon &p : concaveh) - overlap = overlap || poly.overlaps(p); - - auto bb = poly.contour.bounding_box(); - bb.offset(scaled(pcfg.min_wall_thickness_mm)); - - std::vector qres = - bindex.query(bb, BoxIndex::qtIntersects); - - if (!qres.empty() || overlap) { - - // The model silhouette polygon 'poly' HAS an intersection - // with the support silhouettes. Include this polygon - // in the pad holes with the breaksticks and merge the - // original (offsetted) version with the rest of the pad - // base plate. - - basep.emplace_back(poly.contour); - - // The holes of 'poly' will become positive parts of the - // pad, so they has to be checked for intersections as well - // and erased if there is no intersection with the supports - auto it = poly.holes.begin(); - while(it != poly.holes.end()) { - if (bindex.query(it->bounding_box(), - BoxIndex::qtIntersects).empty()) - it = poly.holes.erase(it); - else - ++it; - } - - // Punch the breaksticks - sla::breakstick_holes( - poly, - pcfg.embed_object.object_gap_mm, // padding - pcfg.embed_object.stick_stride_mm, - pcfg.embed_object.stick_width_mm, - pcfg.embed_object.stick_penetration_mm); - - pad_stickholes.emplace_back(poly); - } - } - - create_base_pool(basep, tmesh, pad_stickholes, cfg); - } else { - for (const ExPolygon &bp : modelbase) basep.emplace_back(bp.contour); - create_base_pool(basep, tmesh, {}, cfg); - } - - tmesh.translate(0, 0, float(zlevel)); - if (!tmesh.empty()) tmesh.require_shared_vertices(); - } - - bool empty() const { return tmesh.facets_count() == 0; } -}; - -// The minimum distance for two support points to remain valid. -static const double /*constexpr*/ D_SP = 0.1; - -enum { // For indexing Eigen vectors as v(X), v(Y), v(Z) instead of numbers - X, Y, Z -}; - -// 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 = {}); - -inline Vec2d to_vec2(const Vec3d& v3) { - return {v3(X), v3(Y)}; -} - -bool operator==(const PointIndexEl& e1, const PointIndexEl& e2) { - return e1.second == e2.second; -} - -// 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); - -// This class will hold the support tree meshes with some additional bookkeeping -// as well. Various parts of the support geometry are stored separately and are -// merged when the caller queries the merged mesh. The merged result is cached -// for fast subsequent delivery of the merged mesh which can be quite complex. -// An object of this class will be used as the result type during the support -// generation algorithm. Parts will be added with the appropriate methods such -// as add_head or add_pillar which forwards the constructor arguments and fills -// the IDs of these substructures. The IDs are basically indices into the arrays -// of the appropriate type (heads, pillars, etc...). One can later query e.g. a -// pillar for a specific head... -// -// The support pad is considered an auxiliary geometry and is not part of the -// merged mesh. It can be retrieved using a dedicated method (pad()) -class SLASupportTree::Impl { - // For heads it is beneficial to use the same IDs as for the support points. - std::vector m_heads; - std::vector m_head_indices; - - std::vector m_pillars; - std::vector m_junctions; - std::vector m_bridges; - std::vector m_compact_bridges; - Controller m_ctl; - - Pad m_pad; - - using Mutex = ccr::Mutex; - - mutable Mutex m_mutex; - mutable TriangleMesh meshcache; mutable bool meshcache_valid = false; - mutable double model_height = 0; // the full height of the model - -public: - double ground_level = 0; - - Impl() = default; - inline Impl(const Controller& ctl): m_ctl(ctl) {} - - const Controller& ctl() const { return m_ctl; } - - template Head& add_head(unsigned id, Args&&... args) - { - std::lock_guard lk(m_mutex); - m_heads.emplace_back(std::forward(args)...); - m_heads.back().id = id; - - if (id >= m_head_indices.size()) m_head_indices.resize(id + 1); - m_head_indices[id] = m_heads.size() - 1; - - meshcache_valid = false; - return m_heads.back(); - } - - template Pillar& add_pillar(unsigned headid, Args&&... args) - { - std::lock_guard lk(m_mutex); - - assert(headid < m_head_indices.size()); - Head &head = m_heads[m_head_indices[headid]]; - - m_pillars.emplace_back(head, std::forward(args)...); - Pillar& pillar = m_pillars.back(); - pillar.id = long(m_pillars.size() - 1); - head.pillar_id = pillar.id; - pillar.start_junction_id = head.id; - pillar.starts_from_head = true; - - meshcache_valid = false; - return m_pillars.back(); - } - - void increment_bridges(const Pillar& pillar) - { - std::lock_guard lk(m_mutex); - assert(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size()); - - if(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size()) - m_pillars[size_t(pillar.id)].bridges++; - } - - void increment_links(const Pillar& pillar) - { - std::lock_guard lk(m_mutex); - assert(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size()); - - if(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size()) - m_pillars[size_t(pillar.id)].links++; - } - - template Pillar& add_pillar(Args&&...args) - { - std::lock_guard lk(m_mutex); - m_pillars.emplace_back(std::forward(args)...); - Pillar& pillar = m_pillars.back(); - pillar.id = long(m_pillars.size() - 1); - pillar.starts_from_head = false; - meshcache_valid = false; - return m_pillars.back(); - } - - const Head& pillar_head(long pillar_id) const - { - std::lock_guard lk(m_mutex); - assert(pillar_id >= 0 && pillar_id < long(m_pillars.size())); - - const Pillar& p = m_pillars[size_t(pillar_id)]; - assert(p.starts_from_head && p.start_junction_id >= 0); - assert(size_t(p.start_junction_id) < m_head_indices.size()); - - return m_heads[m_head_indices[p.start_junction_id]]; - } - - const Pillar& head_pillar(unsigned headid) const - { - std::lock_guard lk(m_mutex); - assert(headid < m_head_indices.size()); - - const Head& h = m_heads[m_head_indices[headid]]; - assert(h.pillar_id >= 0 && h.pillar_id < long(m_pillars.size())); - - return m_pillars[size_t(h.pillar_id)]; - } - - template const Junction& add_junction(Args&&... args) - { - std::lock_guard lk(m_mutex); - m_junctions.emplace_back(std::forward(args)...); - m_junctions.back().id = long(m_junctions.size() - 1); - meshcache_valid = false; - return m_junctions.back(); - } - - template const Bridge& add_bridge(Args&&... args) - { - std::lock_guard lk(m_mutex); - m_bridges.emplace_back(std::forward(args)...); - m_bridges.back().id = long(m_bridges.size() - 1); - meshcache_valid = false; - return m_bridges.back(); - } - - template const CompactBridge& add_compact_bridge(Args&&...args) - { - std::lock_guard lk(m_mutex); - m_compact_bridges.emplace_back(std::forward(args)...); - m_compact_bridges.back().id = long(m_compact_bridges.size() - 1); - meshcache_valid = false; - return m_compact_bridges.back(); - } - - Head &head(unsigned id) - { - std::lock_guard lk(m_mutex); - assert(id < m_head_indices.size()); - - meshcache_valid = false; - return m_heads[m_head_indices[id]]; - } - - inline size_t pillarcount() const { - std::lock_guard lk(m_mutex); - return m_pillars.size(); - } - - template inline IntegerOnly pillar(T id) const - { - std::lock_guard lk(m_mutex); - assert(id >= 0 && size_t(id) < m_pillars.size() && - size_t(id) < std::numeric_limits::max()); - - return m_pillars[size_t(id)]; - } - - const Pad &create_pad(const TriangleMesh &object_supports, - const ExPolygons & modelbase, - const PoolConfig & cfg) - { - m_pad = Pad(object_supports, modelbase, ground_level, cfg); - return m_pad; - } - - void remove_pad() { m_pad = Pad(); } - - const Pad& pad() const { return m_pad; } - - // WITHOUT THE PAD!!! - const TriangleMesh &merged_mesh() const - { - if (meshcache_valid) return meshcache; - - Contour3D merged; - - for (auto &head : m_heads) { - if (m_ctl.stopcondition()) break; - if (head.is_valid()) merged.merge(head.mesh); - } - - for (auto &stick : m_pillars) { - if (m_ctl.stopcondition()) break; - merged.merge(stick.mesh); - merged.merge(stick.base); - } - - for (auto &j : m_junctions) { - if (m_ctl.stopcondition()) break; - merged.merge(j.mesh); - } - - for (auto &cb : m_compact_bridges) { - if (m_ctl.stopcondition()) break; - merged.merge(cb.mesh); - } - - for (auto &bs : m_bridges) { - if (m_ctl.stopcondition()) break; - merged.merge(bs.mesh); - } - - if (m_ctl.stopcondition()) { - // In case of failure we have to return an empty mesh - meshcache = TriangleMesh(); - return meshcache; - } - - meshcache = mesh(merged); - - // The mesh will be passed by const-pointer to TriangleMeshSlicer, - // which will need this. - if (!meshcache.empty()) meshcache.require_shared_vertices(); - - BoundingBoxf3 &&bb = meshcache.bounding_box(); - model_height = bb.max(Z) - bb.min(Z); - - meshcache_valid = true; - return meshcache; - } - - // WITH THE PAD - double full_height() const - { - if (merged_mesh().empty() && !pad().empty()) - return get_pad_fullheight(pad().cfg); - - double h = mesh_height(); - if (!pad().empty()) h += sla::get_pad_elevation(pad().cfg); - return h; - } - - // WITHOUT THE PAD!!! - double mesh_height() const - { - if (!meshcache_valid) merged_mesh(); - return model_height; - } - - // Intended to be called after the generation is fully complete - void merge_and_cleanup() - { - merged_mesh(); // in case the mesh is not generated, it should be... - - // Doing clear() does not garantee to release the memory. - m_heads = {}; - m_head_indices = {}; - m_pillars = {}; - m_junctions = {}; - m_bridges = {}; - m_compact_bridges = {}; - } -}; - -// This function returns the position of the centroid in the input 'clust' -// vector of point indices. -template -long cluster_centroid(const ClusterEl& clust, - std::function pointfn, - DistFn df) -{ - switch(clust.size()) { - case 0: /* empty cluster */ return -1; - case 1: /* only one element */ return 0; - case 2: /* if two elements, there is no center */ return 0; - default: ; - } - - // The function works by calculating for each point the average distance - // from all the other points in the cluster. We create a selector bitmask of - // the same size as the cluster. The bitmask will have two true bits and - // false bits for the rest of items and we will loop through all the - // permutations of the bitmask (combinations of two points). Get the - // distance for the two points and add the distance to the averages. - // The point with the smallest average than wins. - - // The complexity should be O(n^2) but we will mostly apply this function - // for small clusters only (cca 3 elements) - - std::vector sel(clust.size(), false); // create full zero bitmask - std::fill(sel.end() - 2, sel.end(), true); // insert the two ones - std::vector avgs(clust.size(), 0.0); // store the average distances - - do { - std::array idx; - for(size_t i = 0, j = 0; i < clust.size(); i++) if(sel[i]) idx[j++] = i; - - double d = df(pointfn(clust[idx[0]]), - pointfn(clust[idx[1]])); - - // add the distance to the sums for both associated points - for(auto i : idx) avgs[i] += d; - - // now continue with the next permutation of the bitmask with two 1s - } while(std::next_permutation(sel.begin(), sel.end())); - - // Divide by point size in the cluster to get the average (may be redundant) - for(auto& a : avgs) a /= clust.size(); - - // get the lowest average distance and return the index - auto minit = std::min_element(avgs.begin(), avgs.end()); - return long(minit - avgs.begin()); -} - -inline Vec3d dirv(const Vec3d& startp, const Vec3d& endp) { - return (endp - startp).normalized(); -} - -class SLASupportTree::Algorithm { - const SupportConfig& m_cfg; - const EigenMesh3D& m_mesh; - const std::vector& m_support_pts; - - using PtIndices = std::vector; - - PtIndices m_iheads; // support points with pinhead - PtIndices m_iheadless; // headless support points - - // supp. pts. connecting to model: point index and the ray hit data - std::vector> m_iheads_onmodel; - - // normals for support points from model faces. - PointSet m_support_nmls; - - // Clusters of points which can reach the ground directly and can be - // bridged to one central pillar - std::vector m_pillar_clusters; - - // This algorithm uses the Impl class as its output stream. It will be - // filled gradually with support elements (heads, pillars, bridges, ...) - using Result = SLASupportTree::Impl; - - Result& m_result; - - // support points in Eigen/IGL format - PointSet m_points; - - // throw if canceled: It will be called many times so a shorthand will - // come in handy. - ThrowOnCancel m_thr; - - // A spatial index to easily find strong pillars to connect to. - PointIndex m_pillar_index; - - inline double ray_mesh_intersect(const Vec3d& s, - const Vec3d& dir) - { - return m_mesh.query_ray_hit(s, dir).distance(); - } - - // This function will test if a future pinhead would not collide with the - // model geometry. It does not take a 'Head' object because those are - // created after this test. Parameters: s: The touching point on the model - // surface. dir: This is the direction of the head from the pin to the back - // r_pin, r_back: the radiuses of the pin and the back sphere width: This - // is the full width from the pin center to the back center m: The object - // mesh. - // The return value is the hit result from the ray casting. If the starting - // point was inside the model, an "invalid" hit_result will be returned - // with a zero distance value instead of a NAN. This way the result can - // be used safely for comparison with other distances. - EigenMesh3D::hit_result 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 - - // 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; - - // 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; - - // 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(); - - auto& m = m_mesh; - using HitResult = EigenMesh3D::hit_result; - - // 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_par::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; - } - - // Checking bridge (pillar and stick as well) intersection with the model. - // If the function is used for headless sticks, the ins_check parameter - // have to be true as the beginning of the stick might be inside the model - // geometry. - // The return value is the hit result from the ray casting. If the starting - // point was inside the model, an "invalid" hit_result will be returned - // with a zero distance value instead of a NAN. This way the result can - // be used safely for comparison with other distances. - EigenMesh3D::hit_result bridge_mesh_intersect( - const Vec3d& s, - const Vec3d& dir, - double r, - bool ins_check = false) - { - static const size_t SAMPLES = 8; - - // 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; - - // Hit results - std::array hits; - - ccr_par::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; - }); - - auto mit = std::min_element(hits.begin(), hits.end()); - - return *mit; - } - - // Helper function for interconnecting two pillars with zig-zag bridges. - bool interconnect(const Pillar& pillar, const Pillar& nextpillar) - { - // We need to get the starting point of the zig-zag pattern. We have to - // be aware that the two head junctions are at different heights. We - // may start from the lowest junction and call it a day but this - // strategy would leave unconnected a lot of pillar duos where the - // shorter pillar is too short to start a new bridge but the taller - // pillar could still be bridged with the shorter one. - bool was_connected = false; - - Vec3d supper = pillar.startpoint(); - Vec3d slower = nextpillar.startpoint(); - Vec3d eupper = pillar.endpoint(); - Vec3d elower = nextpillar.endpoint(); - - double zmin = m_result.ground_level + m_cfg.base_height_mm; - eupper(Z) = std::max(eupper(Z), zmin); - elower(Z) = std::max(elower(Z), zmin); - - // The usable length of both pillars should be positive - if(slower(Z) - elower(Z) < 0) return false; - if(supper(Z) - eupper(Z) < 0) return false; - - double pillar_dist = distance(Vec2d{slower(X), slower(Y)}, - Vec2d{supper(X), supper(Y)}); - double bridge_distance = pillar_dist / std::cos(-m_cfg.bridge_slope); - double zstep = pillar_dist * std::tan(-m_cfg.bridge_slope); - - if(pillar_dist < 2 * m_cfg.head_back_radius_mm || - pillar_dist > m_cfg.max_pillar_link_distance_mm) return false; - - if(supper(Z) < slower(Z)) supper.swap(slower); - if(eupper(Z) < elower(Z)) eupper.swap(elower); - - double startz = 0, endz = 0; - - startz = slower(Z) - zstep < supper(Z) ? slower(Z) - zstep : slower(Z); - endz = eupper(Z) + zstep > elower(Z) ? eupper(Z) + zstep : eupper(Z); - - if(slower(Z) - eupper(Z) < std::abs(zstep)) { - // no space for even one cross - - // Get max available space - startz = std::min(supper(Z), slower(Z) - zstep); - endz = std::max(eupper(Z) + zstep, elower(Z)); - - // Align to center - double available_dist = (startz - endz); - double rounds = std::floor(available_dist / std::abs(zstep)); - startz -= 0.5 * (available_dist - rounds * std::abs(zstep));; - } - - auto pcm = m_cfg.pillar_connection_mode; - bool docrosses = - pcm == PillarConnectionMode::cross || - (pcm == PillarConnectionMode::dynamic && - pillar_dist > 2*m_cfg.base_radius_mm); - - // 'sj' means starting junction, 'ej' is the end junction of a bridge. - // They will be swapped in every iteration thus the zig-zag pattern. - // According to a config parameter, a second bridge may be added which - // results in a cross connection between the pillars. - Vec3d sj = supper, ej = slower; sj(Z) = startz; ej(Z) = sj(Z) + zstep; - - // 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) - { - m_result.add_bridge(sj, ej, pillar.r); - was_connected = true; - } - - // double bridging: (crosses) - if(docrosses) { - 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), - pillar.r) >= bridge_distance) - { - // need to check collision for the cross stick - m_result.add_bridge(sjback, ejback, pillar.r); - was_connected = true; - } - } - - sj.swap(ej); - ej(Z) = sj(Z) + zstep; - } - - return was_connected; - } - - // For connecting a head to a nearby pillar. - bool connect_to_nearpillar(const Head& head, long nearpillar_id) { - - auto nearpillar = [this, nearpillar_id]() { - return m_result.pillar(nearpillar_id); - }; - - if (nearpillar().bridges > m_cfg.max_bridges_on_pillar) return false; - - Vec3d headjp = head.junction_point(); - Vec3d nearjp_u = nearpillar().startpoint(); - Vec3d nearjp_l = nearpillar().endpoint(); - - double r = head.r_back_mm; - double d2d = distance(to_2d(headjp), to_2d(nearjp_u)); - double d3d = distance(headjp, nearjp_u); - - double hdiff = nearjp_u(Z) - headjp(Z); - double slope = std::atan2(hdiff, d2d); - - Vec3d bridgestart = headjp; - Vec3d bridgeend = nearjp_u; - double max_len = m_cfg.max_bridge_length_mm; - double max_slope = m_cfg.bridge_slope; - double zdiff = 0.0; - - // check the default situation if feasible for a bridge - if(d3d > max_len || slope > -max_slope) { - // not feasible to connect the two head junctions. We have to search - // for a suitable touch point. - - double Zdown = headjp(Z) + d2d * std::tan(-max_slope); - Vec3d touchjp = bridgeend; touchjp(Z) = Zdown; - double D = distance(headjp, touchjp); - zdiff = Zdown - nearjp_u(Z); - - if(zdiff > 0) { - Zdown -= zdiff; - bridgestart(Z) -= zdiff; - touchjp(Z) = Zdown; - - double t = bridge_mesh_intersect(headjp, {0,0,-1}, r); - - // We can't insert a pillar under the source head to connect - // with the nearby pillar's starting junction - if(t < zdiff) return false; - } - - if(Zdown <= nearjp_u(Z) && Zdown >= nearjp_l(Z) && D < max_len) - bridgeend(Z) = Zdown; - else - return false; - } - - // There will be a minimum distance from the ground where the - // bridge is allowed to connect. This is an empiric value. - double minz = m_result.ground_level + 2 * m_cfg.head_width_mm; - if(bridgeend(Z) < minz) return false; - - double t = bridge_mesh_intersect(bridgestart, - dirv(bridgestart, bridgeend), r); - - // Cannot insert the bridge. (further search might not worth the hassle) - if(t < distance(bridgestart, bridgeend)) return false; - - // A partial pillar is needed under the starting head. - if(zdiff > 0) { - m_result.add_pillar(unsigned(head.id), bridgestart, r); - m_result.add_junction(bridgestart, r); - } - - m_result.add_bridge(bridgestart, bridgeend, r); - m_result.increment_bridges(nearpillar()); - - return true; - } - - bool search_pillar_and_connect(const Head& head) { - PointIndex spindex = m_pillar_index; - - long nearest_id = -1; - - Vec3d querypoint = head.junction_point(); - - while(nearest_id < 0 && !spindex.empty()) { m_thr(); - // loop until a suitable head is not found - // if there is a pillar closer than the cluster center - // (this may happen as the clustering is not perfect) - // than we will bridge to this closer pillar - - Vec3d qp(querypoint(X), querypoint(Y), m_result.ground_level); - auto qres = spindex.nearest(qp, 1); - if(qres.empty()) break; - - auto ne = qres.front(); - nearest_id = ne.second; - - if(nearest_id >= 0) { - auto nearpillarID = unsigned(nearest_id); - if(nearpillarID < m_result.pillarcount()) { - if(!connect_to_nearpillar(head, nearpillarID)) { - nearest_id = -1; // continue searching - spindex.remove(ne); // without the current pillar - } - } - } - } - - return nearest_id >= 0; - } - - // This is a proxy function for pillar creation which will mind the gap - // between the pad and the model bottom in zero elevation mode. - void create_ground_pillar(const Vec3d &jp, - const Vec3d &sourcedir, - double radius, - int head_id = -1) - { - // 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}; - - double gndlvl = m_result.ground_level; - Vec3d endp = {jp(X), jp(Y), gndlvl}; - double sd = m_cfg.pillar_base_safety_distance_mm; - int pillar_id = -1; - double min_dist = sd + m_cfg.base_radius_mm + EPSILON; - double dist = 0; - bool can_add_base = true; - bool normal_mode = true; - - 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; - double mv = 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); - - auto result = solver.optimize_max( - [this, dir, jp, gndlvl](double mv) { - Vec3d endp = jp + SQR2 * mv * dir; - endp(Z) = gndlvl; - return std::sqrt(m_mesh.squared_distance(endp)); - }, - initvals(mv), bound(0.0, 2 * min_dist)); - - mv = std::get<0>(result.optimum); - endp = jp + SQR2 * mv * dir; - Vec3d pgnd = {endp(X), endp(Y), gndlvl}; - can_add_base = result.score > min_dist; - - double gnd_offs = m_mesh.ground_level_offset(); - auto abort_in_shame = - [gnd_offs, &normal_mode, &can_add_base, &endp, jp, gndlvl]() - { - normal_mode = true; - can_add_base = false; // Nothing left to do, hope for the best - endp = {jp(X), jp(Y), gndlvl - gnd_offs }; - }; - - // We have to check if the bridge is feasible. - if (bridge_mesh_intersect(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 - else { - - auto hit = bridge_mesh_intersect(endp, DOWN, radius); - if (!std::isinf(hit.distance())) abort_in_shame(); - - Pillar &plr = m_result.add_pillar(endp, pgnd, radius); - - if (can_add_base) - plr.add_base(m_cfg.base_height_mm, - m_cfg.base_radius_mm); - - pillar_id = plr.id; - } - - m_result.add_bridge(jp, endp, radius); - m_result.add_junction(endp, radius); - - // Add a degenerated pillar and the bridge. - // The degenerate pillar will have zero length and it will - // prevent from queries of head_pillar() to have non-existing - // pillar when the head should have one. - if (head_id >= 0) - m_result.add_pillar(unsigned(head_id), jp, radius); - } - } - - if (normal_mode) { - Pillar &plr = head_id >= 0 - ? m_result.add_pillar(unsigned(head_id), - endp, - radius) - : m_result.add_pillar(jp, endp, radius); - - if (can_add_base) - plr.add_base(m_cfg.base_height_mm, m_cfg.base_radius_mm); - - pillar_id = plr.id; - } - - if(pillar_id >= 0) // Save the pillar endpoint in the spatial index - m_pillar_index.insert(endp, pillar_id); - } - -public: - - Algorithm(const SupportConfig& config, - const EigenMesh3D& emesh, - const std::vector& support_pts, - Result& result, - ThrowOnCancel thr) : - m_cfg(config), - m_mesh(emesh), - m_support_pts(support_pts), - m_support_nmls(support_pts.size(), 3), - m_result(result), - m_points(support_pts.size(), 3), - m_thr(thr) - { - // Prepare the support points in Eigen/IGL format as well, we will use - // it mostly in this form. - - long i = 0; - for(const SupportPoint& sp : m_support_pts) { - m_points.row(i)(X) = double(sp.pos(X)); - m_points.row(i)(Y) = double(sp.pos(Y)); - m_points.row(i)(Z) = double(sp.pos(Z)); - ++i; - } - } - - - // Now let's define the individual steps of the support generation algorithm - - // Filtering step: here we will discard inappropriate support points - // and decide the future of the appropriate ones. We will check if a - // pinhead is applicable and adjust its angle at each support point. We - // will also merge the support points that are just too close and can - // be considered as one. - void filter() { - // Get the points that are too close to each other and keep only the - // first one - auto aliases = cluster(m_points, D_SP, 2); - - PtIndices filtered_indices; - filtered_indices.reserve(aliases.size()); - m_iheads.reserve(aliases.size()); - m_iheadless.reserve(aliases.size()); - for(auto& a : aliases) { - // Here we keep only the front point of the cluster. - filtered_indices.emplace_back(a.front()); - } - - // calculate the normals to the triangles for filtered points - auto nmls = sla::normals(m_points, m_mesh, m_cfg.head_front_radius_mm, - m_thr, filtered_indices); - - // Not all of the support points have to be a valid position for - // support creation. The angle may be inappropriate or there may - // 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::Mutex mutex; - auto addfn = [&mutex](PtIndices &container, unsigned val) { - std::lock_guard lk(mutex); - container.emplace_back(val); - }; - - ccr::enumerate(filtered_indices.begin(), filtered_indices.end(), - [this, &nmls, addfn](unsigned fidx, size_t i) - { - m_thr(); - - auto n = nmls.row(i); - - // for all normals we generate the spherical coordinates and - // saturate the polar angle to 45 degrees from the bottom then - // convert back to standard coordinates to get the new normal. - // Then we just create a quaternion from the two normals - // (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)); - - // skip if the tilt is not sane - if(polar >= PI - m_cfg.normal_cutoff_angle) { - - // We saturate the polar angle to 3pi/4 - polar = std::max(polar, 3*PI / 4); - - // save the head (pinpoint) position - Vec3d hp = m_points.row(fidx); - - double w = m_cfg.head_width_mm + - m_cfg.head_back_radius_mm + - 2*m_cfg.head_front_radius_mm; - - 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(); - - // check available distance - EigenMesh3D::hit_result t - = pinhead_mesh_intersect(hp, // touching point - nn, // normal - pin_r, - m_cfg.head_back_radius_mm, - w); - - if(t.distance() <= w) { - - // Let's try to optimize this angle, there might be a - // viable normal that doesn't collide with the model - // geometry and its very close to the default. - - StopCriteria stc; - stc.max_iterations = m_cfg.optimizer_max_iterations; - stc.relative_score_difference = m_cfg.optimizer_rel_score_diff; - stc.stop_score = w; // space greater than w is enough - GeneticOptimizer solver(stc); - solver.seed(0); // we want deterministic behavior - - auto oresult = solver.optimize_max( - [this, pin_r, w, hp](double plr, double azm) - { - auto n = Vec3d(std::cos(azm) * std::sin(plr), - std::sin(azm) * std::sin(plr), - std::cos(plr)).normalized(); - - double score = pinhead_mesh_intersect( - hp, n, 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(-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; - } - } - - // save the verified and corrected normal - m_support_nmls.row(fidx) = nn; - - if (t.distance() > w) { - // Check distance from ground, we might have zero elevation. - if (hp(Z) + w * nn(Z) < m_result.ground_level) { - addfn(m_iheadless, fidx); - } else { - // mark the point for needing a head. - addfn(m_iheads, fidx); - } - } else if (polar >= 3 * PI / 4) { - // Headless supports do not tilt like the headed ones - // so the normal should point almost to the ground. - addfn(m_iheadless, fidx); - } - } - }); - - m_thr(); - } - - // Pinhead creation: based on the filtering results, the Head objects - // will be constructed (together with their triangle meshes). - void add_pinheads() - { - for (unsigned i : m_iheads) { - m_thr(); - m_result.add_head( - i, - m_cfg.head_back_radius_mm, - m_support_pts[i].head_front_radius, - m_cfg.head_width_mm, - m_cfg.head_penetration_mm, - m_support_nmls.row(i), // dir - m_support_pts[i].pos.cast() // displacement - ); - } - } - - // Further classification of the support points with pinheads. If the - // ground is directly reachable through a vertical line parallel to the - // Z axis we consider a support point as pillar candidate. If touches - // the model geometry, it will be marked as non-ground facing and - // further steps will process it. Also, the pillars will be grouped - // into clusters that can be interconnected with bridges. Elements of - // these groups may or may not be interconnected. Here we only run the - // clustering algorithm. - void classify() - { - // We should first get the heads that reach the ground directly - PtIndices ground_head_indices; - ground_head_indices.reserve(m_iheads.size()); - m_iheads_onmodel.reserve(m_iheads.size()); - - // First we decide which heads reach the ground and can be full - // pillars and which shall be connected to the model surface (or - // search a suitable path around the surface that leads to the - // ground -- TODO) - for(unsigned i : m_iheads) { - m_thr(); - - auto& head = m_result.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); - - 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)); - } - - // We want to search for clusters of points that are far enough - // from each other in the XY plane to not cross their pillar bases - // These clusters of support points will join in one pillar, - // possibly in their centroid support point. - - auto pointfn = [this](unsigned i) { - return m_result.head(i).junction_point(); - }; - - auto predicate = [this](const PointIndexEl &e1, - const PointIndexEl &e2) { - double d2d = distance(to_2d(e1.first), to_2d(e2.first)); - double d3d = distance(e1.first, e2.first); - return d2d < 2 * m_cfg.base_radius_mm - && d3d < m_cfg.max_bridge_length_mm; - }; - - m_pillar_clusters = cluster(ground_head_indices, - pointfn, - predicate, - m_cfg.max_bridges_on_pillar); - } - - // Step: Routing the ground connected pinheads, and interconnecting - // them with additional (angled) bridges. Not all of these pinheads - // will be a full pillar (ground connected). Some will connect to a - // nearby pillar using a bridge. The max number of such side-heads for - // a central pillar is limited to avoid bad weight distribution. - void routing_to_ground() - { - const double pradius = m_cfg.head_back_radius_mm; - // const double gndlvl = m_result.ground_level; - - ClusterEl cl_centroids; - cl_centroids.reserve(m_pillar_clusters.size()); - - for(auto& cl : m_pillar_clusters) { m_thr(); - // place all the centroid head positions into the index. We - // will query for alternative pillar positions. If a sidehead - // cannot connect to the cluster centroid, we have to search - // for another head with a full pillar. Also when there are two - // elements in the cluster, the centroid is arbitrary and the - // sidehead is allowed to connect to a nearby pillar to - // increase structural stability. - - if(cl.empty()) continue; - - // get the current cluster centroid - auto& thr = m_thr; const auto& points = m_points; - 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 - - cl_centroids.emplace_back(hid); - - Head& h = m_result.head(hid); - h.transform(); - - create_ground_pillar(h.junction_point(), h.dir, h.r_back_mm, h.id); - } - - // now we will go through the clusters ones again and connect the - // sidepoints with the cluster centroid (which is a ground pillar) - // or a nearby pillar if the centroid is unreachable. - size_t ci = 0; - for(auto cl : m_pillar_clusters) { m_thr(); - - auto cidx = cl_centroids[ci++]; - - // TODO: don't consider the cluster centroid but calculate a - // central position where the pillar can be placed. this way - // the weight is distributed more effectively on the pillar. - - auto centerpillarID = m_result.head_pillar(cidx).id; - - for(auto c : cl) { m_thr(); - if(c == cidx) continue; - - auto& sidehead = m_result.head(c); - sidehead.transform(); - - if(!connect_to_nearpillar(sidehead, centerpillarID) && - !search_pillar_and_connect(sidehead)) - { - Vec3d pstart = sidehead.junction_point(); - //Vec3d pend = Vec3d{pstart(X), pstart(Y), gndlvl}; - // Could not find a pillar, create one - create_ground_pillar(pstart, - sidehead.dir, - pradius, - sidehead.id); - } - } - } - } - - // Step: routing the pinheads that would connect to the model surface - // along the Z axis downwards. For now these will actually be connected with - // the model surface with a flipped pinhead. In the future here we could use - // some smart algorithms to search for a safe path to the ground or to a - // nearby pillar that can hold the supported weight. - void 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 hjp = head.junction_point(); - Vec3d endp = hjp + dist * dir; - m_result.add_bridge(hjp, endp, head.r_back_mm); - m_result.add_junction(endp, head.r_back_mm); - - this->create_ground_pillar(endp, dir, head.r_back_mm); - }; - - std::vector modelpillars; - ccr::Mutex mutex; - - // TODO: connect these to the ground pillars if possible - ccr::enumerate(m_iheads_onmodel.begin(), m_iheads_onmodel.end(), - [this, routedown, &modelpillars, &mutex] - (const std::pair &el, - size_t) - { - m_thr(); - unsigned idx = el.first; - EigenMesh3D::hit_result hit = el.second; - - auto& head = m_result.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. - - 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(); - - Pillar& pill = m_result.add_pillar(unsigned(head.id), - endp, - head.r_back_mm); - - 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; - - 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; - } - - // We have failed to route this head. - BOOST_LOG_TRIVIAL(warning) - << "Failed to route model facing support point." - << " ID: " << idx; - head.invalidate(); - }); - - for(auto pillid : modelpillars) { - auto& pillar = m_result.pillar(pillid); - m_pillar_index.insert(pillar.endpoint(), pillid); - } - } - - // Helper function for interconnect_pillars where pairs of already connected - // pillars should be checked for not to be processed again. This can be done - // in O(log) or even constant time with a set or an unordered set of hash - // values uniquely representing a pair of integers. The order of numbers - // within the pair should not matter, it has the same unique hash. - template static I pairhash(I a, I b) - { - using std::ceil; using std::log2; using std::max; using std::min; - - static_assert(std::is_integral::value, - "This function works only for integral types."); - - I g = min(a, b), l = max(a, b); - - auto bits_g = g ? int(ceil(log2(g))) : 0; - - // Assume the hash will fit into the output variable - assert((l ? (ceil(log2(l))) : 0) + bits_g < int(sizeof(I) * CHAR_BIT)); - - return (l << bits_g) + g; - } - - void interconnect_pillars() { - // Now comes the algorithm that connects pillars with each other. - // Ideally every pillar should be connected with at least one of its - // neighbors if that neighbor is within max_pillar_link_distance - - // Pillars with height exceeding H1 will require at least one neighbor - // to connect with. Height exceeding H2 require two neighbors. - double H1 = m_cfg.max_solo_pillar_height_mm; - double H2 = m_cfg.max_dual_pillar_height_mm; - double d = m_cfg.max_pillar_link_distance_mm; - - //A connection between two pillars only counts if the height ratio is - // bigger than 50% - double min_height_ratio = 0.5; - - std::set pairs; - - // A function to connect one pillar with its neighbors. THe number of - // neighbors is given in the configuration. This function if called - // for every pillar in the pillar index. A pair of pillar will not - // be connected multiple times this is ensured by the 'pairs' set which - // remembers the processed pillar pairs - auto cascadefn = - [this, d, &pairs, min_height_ratio, H1] (const PointIndexEl& el) - { - Vec3d qp = el.first; // endpoint of the pillar - - const Pillar& pillar = m_result.pillar(el.second); // actual pillar - - // Get the max number of neighbors a pillar should connect to - unsigned neighbors = m_cfg.pillar_cascade_neighbors; - - // connections are already enough for the pillar - if(pillar.links >= neighbors) return; - - // Query all remaining points within reach - auto qres = m_pillar_index.query([qp, d](const PointIndexEl& e){ - return distance(e.first, qp) < d; - }); - - // sort the result by distance (have to check if this is needed) - std::sort(qres.begin(), qres.end(), - [qp](const PointIndexEl& e1, const PointIndexEl& e2){ - return distance(e1.first, qp) < distance(e2.first, qp); - }); - - for(auto& re : qres) { // process the queried neighbors - - if(re.second == el.second) continue; // Skip self - - auto a = el.second, b = re.second; - - // Get unique hash for the given pair (order doesn't matter) - auto hashval = pairhash(a, b); - - // Search for the pair amongst the remembered pairs - if(pairs.find(hashval) != pairs.end()) continue; - - const Pillar& neighborpillar = m_result.pillar(re.second); - - // this neighbor is occupied, skip - if(neighborpillar.links >= neighbors) continue; - - if(interconnect(pillar, neighborpillar)) { - pairs.insert(hashval); - - // If the interconnection length between the two pillars is - // less than 50% of the longer pillar's height, don't count - if(pillar.height < H1 || - neighborpillar.height / pillar.height > min_height_ratio) - m_result.increment_links(pillar); - - if(neighborpillar.height < H1 || - pillar.height / neighborpillar.height > min_height_ratio) - m_result.increment_links(neighborpillar); - - } - - // connections are enough for one pillar - if(pillar.links >= neighbors) break; - } - }; - - // Run the cascade for the pillars in the index - m_pillar_index.foreach(cascadefn); - - // We would be done here if we could allow some pillars to not be - // connected with any neighbors. But this might leave the support tree - // unprintable. - // - // The current solution is to insert additional pillars next to these - // lonely pillars. One or even two additional pillar might get inserted - // depending on the length of the lonely pillar. - - size_t pillarcount = m_result.pillarcount(); - - // Again, go through all pillars, this time in the whole support tree - // not just the index. - for(size_t pid = 0; pid < pillarcount; pid++) { - auto pillar = [this, pid]() { return m_result.pillar(pid); }; - - // Decide how many additional pillars will be needed: - - unsigned needpillars = 0; - if (pillar().bridges > m_cfg.max_bridges_on_pillar) - needpillars = 3; - else if (pillar().links < 2 && pillar().height > H2) { - // Not enough neighbors to support this pillar - needpillars = 2 - pillar().links; - } else if (pillar().links < 1 && pillar().height > H1) { - // No neighbors could be found and the pillar is too long. - needpillars = 1; - } - - // Search for new pillar locations: - - bool found = false; - double alpha = 0; // goes to 2Pi - double r = 2 * m_cfg.base_radius_mm; - Vec3d pillarsp = pillar().startpoint(); - - // temp value for starting point detection - Vec3d sp(pillarsp(X), pillarsp(Y), pillarsp(Z) - r); - - // A vector of bool for placement feasbility - std::vector canplace(needpillars, false); - std::vector spts(needpillars); // vector of starting points - - double gnd = m_result.ground_level; - double min_dist = m_cfg.pillar_base_safety_distance_mm + - m_cfg.base_radius_mm + EPSILON; - - while(!found && alpha < 2*PI) { - for (unsigned n = 0; - n < needpillars && (!n || canplace[n - 1]); - n++) - { - double a = alpha + n * PI / 3; - Vec3d s = sp; - s(X) += std::cos(a) * r; - s(Y) += std::sin(a) * r; - spts[n] = s; - - // Check the path vertically down - auto hr = bridge_mesh_intersect(s, {0, 0, -1}, pillar().r); - Vec3d gndsp{s(X), s(Y), gnd}; - - // If the path is clear, check for pillar base collisions - canplace[n] = std::isinf(hr.distance()) && - std::sqrt(m_mesh.squared_distance(gndsp)) > - min_dist; - } - - found = std::all_of(canplace.begin(), canplace.end(), - [](bool v) { return v; }); - - // 20 angles will be tried... - alpha += 0.1 * PI; - } - - std::vector newpills; - newpills.reserve(needpillars); - - if(found) for(unsigned n = 0; n < needpillars; n++) { - Vec3d s = spts[n]; - Pillar p(s, Vec3d(s(X), s(Y), gnd), pillar().r); - p.add_base(m_cfg.base_height_mm, m_cfg.base_radius_mm); - - if(interconnect(pillar(), p)) { - Pillar& pp = m_result.add_pillar(p); - m_pillar_index.insert(pp.endpoint(), unsigned(pp.id)); - - m_result.add_junction(s, pillar().r); - double t = bridge_mesh_intersect(pillarsp, - dirv(pillarsp, s), - pillar().r); - if(distance(pillarsp, s) < t) - m_result.add_bridge(pillarsp, s, pillar().r); - - if(pillar().endpoint()(Z) > m_result.ground_level) - m_result.add_junction(pillar().endpoint(), pillar().r); - - newpills.emplace_back(pp.id); - m_result.increment_links(pillar()); - } - } - - if(!newpills.empty()) { - for(auto it = newpills.begin(), nx = std::next(it); - nx != newpills.end(); ++it, ++nx) { - const Pillar& itpll = m_result.pillar(*it); - const Pillar& nxpll = m_result.pillar(*nx); - if(interconnect(itpll, nxpll)) { - m_result.increment_links(itpll); - m_result.increment_links(nxpll); - } - } - - m_pillar_index.foreach(cascadefn); - } - } - } - - // Step: process the support points where there is not enough space for a - // full pinhead. In this case we will use a rounded sphere as a touching - // point and use a thinner bridge (let's call it a stick). - void routing_headless () - { - // For now we will just generate smaller headless sticks with a sharp - // ending point that connects to the mesh surface. - - // We will sink the pins into the model surface for a distance of 1/3 of - // the pin radius - for(unsigned i : m_iheadless) { m_thr(); - - const auto R = double(m_support_pts[i].head_front_radius); - const double HWIDTH_MM = R/3; - - // Exact support position - Vec3d sph = m_support_pts[i].pos.cast(); - 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 dist = ray_mesh_intersect(sj, dir); - if (std::isinf(dist)) - dist = sph(Z) - m_mesh.ground_level() - + m_mesh.ground_level_offset(); - - if(std::isnan(idist) || idist < 2*R || - std::isnan(dist) || dist < 2*R) - { - BOOST_LOG_TRIVIAL(warning) << "Can not find route for headless" - << " support stick at: " - << sj.transpose(); - continue; - } - - Vec3d ej = sj + (dist + HWIDTH_MM)* dir; - m_result.add_compact_bridge(sp, ej, n, R, !std::isinf(dist)); - } - } - - void merge_result() { m_result.merge_and_cleanup(); } -}; - -bool SLASupportTree::generate(const std::vector &support_points, - const EigenMesh3D& mesh, - const SupportConfig &cfg, - const Controller &ctl) -{ - if(support_points.empty()) return false; - - Algorithm alg(cfg, mesh, support_points, *m_impl, ctl.cancelfn); - - // Let's define the individual steps of the processing. We can experiment - // later with the ordering and the dependencies between them. - enum Steps { - BEGIN, - FILTER, - PINHEADS, - CLASSIFY, - ROUTING_GROUND, - ROUTING_NONGROUND, - CASCADE_PILLARS, - HEADLESS, - MERGE_RESULT, - DONE, - ABORT, - NUM_STEPS - //... - }; - - // Collect the algorithm steps into a nice sequence - std::array, NUM_STEPS> program = { - [] () { - // Begin... - // Potentially clear up the shared data (not needed for now) - }, - - std::bind(&Algorithm::filter, &alg), - - std::bind(&Algorithm::add_pinheads, &alg), - - std::bind(&Algorithm::classify, &alg), - - std::bind(&Algorithm::routing_to_ground, &alg), - - std::bind(&Algorithm::routing_to_model, &alg), - - std::bind(&Algorithm::interconnect_pillars, &alg), - - std::bind(&Algorithm::routing_headless, &alg), - - std::bind(&Algorithm::merge_result, &alg), - - [] () { - // Done - }, - - [] () { - // Abort - } - }; - - Steps pc = BEGIN; - - if(cfg.ground_facing_only) { - program[ROUTING_NONGROUND] = []() { - BOOST_LOG_TRIVIAL(info) - << "Skipping model-facing supports as requested."; - }; - program[HEADLESS] = []() { - BOOST_LOG_TRIVIAL(info) << "Skipping headless stick generation as" - " requested."; - }; - } - - // Let's define a simple automaton that will run our program. - auto progress = [&ctl, &pc] () { - static const std::array stepstr { - "Starting", - "Filtering", - "Generate pinheads", - "Classification", - "Routing to ground", - "Routing supports to model surface", - "Interconnecting pillars", - "Processing small holes", - "Merging support mesh", - "Done", - "Abort" - }; - - static const std::array stepstate { - 0, - 10, - 30, - 50, - 60, - 70, - 80, - 85, - 99, - 100, - 0 - }; - - if(ctl.stopcondition()) pc = ABORT; - - switch(pc) { - case BEGIN: pc = FILTER; break; - case FILTER: pc = PINHEADS; break; - case PINHEADS: pc = CLASSIFY; break; - case CLASSIFY: pc = ROUTING_GROUND; break; - case ROUTING_GROUND: pc = ROUTING_NONGROUND; break; - case ROUTING_NONGROUND: pc = CASCADE_PILLARS; break; - case CASCADE_PILLARS: pc = HEADLESS; break; - case HEADLESS: pc = MERGE_RESULT; break; - case MERGE_RESULT: pc = DONE; break; - case DONE: - case ABORT: break; - default: ; - } - - ctl.statuscb(stepstate[pc], stepstr[pc]); - }; - - // Just here we run the computation... - while(pc < DONE) { - progress(); - program[pc](); - } - - return pc == ABORT; -} - -SLASupportTree::SLASupportTree(double gnd_lvl): m_impl(new Impl()) { - m_impl->ground_level = gnd_lvl; -} - -const TriangleMesh &SLASupportTree::merged_mesh() const -{ - return m_impl->merged_mesh(); -} - -void SLASupportTree::merged_mesh_with_pad(TriangleMesh &outmesh) const { - outmesh.merge(merged_mesh()); - outmesh.merge(get_pad()); -} - -std::vector SLASupportTree::slice( +std::vector SupportTree::slice( const std::vector &grid, float cr) const { - const TriangleMesh &sup_mesh = m_impl->merged_mesh(); - const TriangleMesh &pad_mesh = get_pad(); + const TriangleMesh &sup_mesh = retrieve_mesh(MeshType::Support); + const TriangleMesh &pad_mesh = retrieve_mesh(MeshType::Pad); using Slices = std::vector; auto slices = reserve_vector(2); @@ -2610,7 +62,7 @@ std::vector SLASupportTree::slice( TriangleMeshSlicer sup_slicer(&sup_mesh); sup_slicer.closing_radius = cr; - sup_slicer.slice(grid, &slices.back(), m_impl->ctl().cancelfn); + sup_slicer.slice(grid, &slices.back(), ctl().cancelfn); } if (!pad_mesh.empty()) { @@ -2618,13 +70,14 @@ std::vector SLASupportTree::slice( auto bb = pad_mesh.bounding_box(); auto maxzit = std::upper_bound(grid.begin(), grid.end(), bb.max.z()); - - auto padgrid = reserve_vector(grid.end() - maxzit); + + auto cap = grid.end() - maxzit; + auto padgrid = reserve_vector(size_t(cap > 0 ? cap : 0)); std::copy(grid.begin(), maxzit, std::back_inserter(padgrid)); TriangleMeshSlicer pad_slicer(&pad_mesh); - pad_slicer.closing_radius = cr; - pad_slicer.slice(padgrid, &slices.back(), m_impl->ctl().cancelfn); + sup_slicer.closing_radius = cr; + pad_slicer.slice(padgrid, &slices.back(), ctl().cancelfn); } size_t len = grid.size(); @@ -2646,33 +99,20 @@ std::vector SLASupportTree::slice( return mrg; } -const TriangleMesh &SLASupportTree::add_pad(const ExPolygons& modelbase, - const PoolConfig& pcfg) const +SupportTree::UPtr SupportTree::create(const SupportableMesh &sm, + const JobController & ctl) { - return m_impl->create_pad(merged_mesh(), modelbase, pcfg).tmesh; + auto builder = make_unique(); + builder->m_ctl = ctl; + + if (sm.cfg.enabled) { + builder->build(sm); + builder->merge_and_cleanup(); // clean metadata, leave only the meshes. + } else { + builder->ground_level = sm.emesh.ground_level(); } -const TriangleMesh &SLASupportTree::get_pad() const -{ - return m_impl->pad().tmesh; + return std::move(builder); } -void SLASupportTree::remove_pad() -{ - m_impl->remove_pad(); -} - -SLASupportTree::SLASupportTree(const std::vector &points, - const EigenMesh3D& emesh, - const SupportConfig &cfg, - const Controller &ctl): - m_impl(new Impl(ctl)) -{ - m_impl->ground_level = emesh.ground_level() - cfg.object_elevation_mm; - generate(points, emesh, cfg, ctl); -} - -SLASupportTree::~SLASupportTree() {} - -} -} +}} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index 59485ba77..e4206246d 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -2,14 +2,12 @@ #define SLASUPPORTTREE_HPP #include -#include -#include #include #include #include #include "SLACommon.hpp" - +#include "SLAPad.hpp" namespace Slic3r { @@ -25,13 +23,17 @@ using ExPolygons = std::vector; namespace sla { -enum class PillarConnectionMode { +enum class PillarConnectionMode +{ zigzag, cross, dynamic }; -struct SupportConfig { +struct SupportConfig +{ + bool enabled = true; + // Radius in mm of the pointing side of the head. double head_front_radius_mm = 0.2; @@ -78,6 +80,11 @@ struct SupportConfig { // The shortest distance between a pillar base perimeter from the model // body. This is only useful when elevation is set to zero. double pillar_base_safety_distance_mm = 0.5; + + double head_fullwidth() const { + return 2 * head_front_radius_mm + head_width_mm + + 2 * head_back_radius_mm - head_penetration_mm; + } // ///////////////////////////////////////////////////////////////////////// // Compile time configuration values (candidates for runtime) @@ -97,101 +104,78 @@ struct SupportConfig { static const unsigned max_bridges_on_pillar; }; -struct PoolConfig; +enum class MeshType { Support, Pad }; /// A Control structure for the support calculation. Consists of the status /// indicator callback and the stop condition predicate. -struct Controller { - +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 - std::function statuscb = - [](unsigned, const std::string&){}; - + StatusFn statuscb = [](unsigned, const std::string&){}; + // Returns true if the calculation should be aborted. - std::function stopcondition = [](){ return false; }; - + 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 - std::function cancelfn = [](){}; + CancelFn cancelfn = [](){}; }; -using PointSet = Eigen::MatrixXd; +struct SupportableMesh +{ + EigenMesh3D emesh; + SupportPoints pts; + SupportConfig cfg; -//EigenMesh3D to_eigenmesh(const TriangleMesh& m); - -// needed for find best rotation -//EigenMesh3D to_eigenmesh(const ModelObject& model); - -// Simple conversion of 'vector of points' to an Eigen matrix -//PointSet to_point_set(const std::vector&); - - -/* ************************************************************************** */ + explicit SupportableMesh(const TriangleMesh & trmsh, + const SupportPoints &sp, + const SupportConfig &c) + : emesh{trmsh}, pts{sp}, cfg{c} + {} + + explicit SupportableMesh(const EigenMesh3D &em, + const SupportPoints &sp, + const SupportConfig &c) + : emesh{em}, pts{sp}, cfg{c} + {} +}; /// The class containing mesh data for the generated supports. -class SLASupportTree { - class Impl; // persistent support data - std::unique_ptr m_impl; - - Impl& get() { return *m_impl; } - const Impl& get() const { return *m_impl; } - - friend void add_sla_supports(Model&, - const SupportConfig&, - const Controller&); - - // The generation algorithm is quite long and will be captured in a separate - // class with private data, helper methods, etc... This data is only needed - // during the calculation whereas the Impl class contains the persistent - // data, mostly the meshes. - class Algorithm; - - // Generate the 3D supports for a model intended for SLA print. This - // will instantiate the Algorithm class and call its appropriate methods - // with status indication. - bool generate(const std::vector& pts, - const EigenMesh3D& mesh, - const SupportConfig& cfg = {}, - const Controller& ctl = {}); - +class SupportTree +{ + JobController m_ctl; public: - - SLASupportTree(double ground_level = 0.0); - - SLASupportTree(const std::vector& pts, - const EigenMesh3D& em, - const SupportConfig& cfg = {}, - const Controller& ctl = {}); + using UPtr = std::unique_ptr; - SLASupportTree(const SLASupportTree&) = delete; - SLASupportTree& operator=(const SLASupportTree&) = delete; + static UPtr create(const SupportableMesh &input, + const JobController &ctl = {}); - ~SLASupportTree(); + virtual ~SupportTree() = default; - /// Get the whole mesh united into the output TriangleMesh - /// WITHOUT THE PAD - const TriangleMesh& merged_mesh() const; + virtual const TriangleMesh &retrieve_mesh(MeshType meshtype) const = 0; - void merged_mesh_with_pad(TriangleMesh&) const; - - std::vector slice(const std::vector &, - float closing_radius) const; - - /// Adding the "pad" (base pool) under the supports + /// Adding the "pad" under the supports. /// modelbase will be used according to the embed_object flag in PoolConfig. - /// If set, the plate will interpreted as the model's intrinsic pad. + /// If set, the plate will be interpreted as the model's intrinsic pad. /// Otherwise, the modelbase will be unified with the base plate calculated /// from the supports. - const TriangleMesh& add_pad(const ExPolygons& modelbase, - const PoolConfig& pcfg) const; - - /// Get the pad geometry - const TriangleMesh& get_pad() const; - - void remove_pad(); - + virtual const TriangleMesh &add_pad(const ExPolygons &modelbase, + const PadConfig & pcfg) = 0; + + virtual void remove_pad() = 0; + + std::vector slice(const std::vector &, + float closing_radius) const; + + void retrieve_full_mesh(TriangleMesh &outmesh) const; + + const JobController &ctl() const { return m_ctl; } }; } diff --git a/src/libslic3r/SLA/SLASupportTreeBuilder.cpp b/src/libslic3r/SLA/SLASupportTreeBuilder.cpp new file mode 100644 index 000000000..2e0310ed8 --- /dev/null +++ b/src/libslic3r/SLA/SLASupportTreeBuilder.cpp @@ -0,0 +1,525 @@ +#include "SLASupportTreeBuilder.hpp" +#include "SLASupportTreeBuildsteps.hpp" + +namespace Slic3r { +namespace sla { + +Contour3D sphere(double rho, Portion portion, double fa) { + + Contour3D ret; + + // prohibit close to zero radius + if(rho <= 1e-6 && rho >= -1e-6) return ret; + + auto& vertices = ret.points; + auto& facets = ret.indices; + + // Algorithm: + // Add points one-by-one to the sphere grid and form facets using relative + // coordinates. Sphere is composed effectively of a mesh of stacked circles. + + // adjust via rounding to get an even multiple for any provided angle. + double angle = (2*PI / floor(2*PI / fa)); + + // Ring to be scaled to generate the steps of the sphere + std::vector ring; + + for (double i = 0; i < 2*PI; i+=angle) ring.emplace_back(i); + + const auto sbegin = size_t(2*std::get<0>(portion)/angle); + const auto send = size_t(2*std::get<1>(portion)/angle); + + const size_t steps = ring.size(); + const double increment = 1.0 / double(steps); + + // special case: first ring connects to 0,0,0 + // insert and form facets. + if(sbegin == 0) + vertices.emplace_back(Vec3d(0.0, 0.0, -rho + increment*sbegin*2.0*rho)); + + auto id = coord_t(vertices.size()); + for (size_t i = 0; i < ring.size(); i++) { + // Fixed scaling + const double z = -rho + increment*rho*2.0 * (sbegin + 1.0); + // radius of the circle for this step. + const double r = std::sqrt(std::abs(rho*rho - z*z)); + Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r); + vertices.emplace_back(Vec3d(b(0), b(1), z)); + + if (sbegin == 0) + facets.emplace_back((i == 0) ? + Vec3crd(coord_t(ring.size()), 0, 1) : + Vec3crd(id - 1, 0, id)); + ++id; + } + + // General case: insert and form facets for each step, + // joining it to the ring below it. + for (size_t s = sbegin + 2; s < send - 1; s++) { + const double z = -rho + increment*double(s*2.0*rho); + const double r = std::sqrt(std::abs(rho*rho - z*z)); + + for (size_t i = 0; i < ring.size(); i++) { + Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r); + vertices.emplace_back(Vec3d(b(0), b(1), z)); + auto id_ringsize = coord_t(id - int(ring.size())); + if (i == 0) { + // wrap around + facets.emplace_back(Vec3crd(id - 1, id, + id + coord_t(ring.size() - 1))); + facets.emplace_back(Vec3crd(id - 1, id_ringsize, id)); + } else { + facets.emplace_back(Vec3crd(id_ringsize - 1, id_ringsize, id)); + facets.emplace_back(Vec3crd(id - 1, id_ringsize - 1, id)); + } + id++; + } + } + + // special case: last ring connects to 0,0,rho*2.0 + // only form facets. + if(send >= size_t(2*PI / angle)) { + vertices.emplace_back(Vec3d(0.0, 0.0, -rho + increment*send*2.0*rho)); + for (size_t i = 0; i < ring.size(); i++) { + auto id_ringsize = coord_t(id - int(ring.size())); + if (i == 0) { + // third vertex is on the other side of the ring. + facets.emplace_back(Vec3crd(id - 1, id_ringsize, id)); + } else { + auto ci = coord_t(id_ringsize + coord_t(i)); + facets.emplace_back(Vec3crd(ci - 1, ci, id)); + } + } + } + id++; + + return ret; +} + +Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d &sp) +{ + Contour3D ret; + + auto steps = int(ssteps); + auto& points = ret.points; + auto& indices = ret.indices; + points.reserve(2*ssteps); + double a = 2*PI/steps; + + Vec3d jp = sp; + Vec3d endp = {sp(X), sp(Y), sp(Z) + h}; + + // Upper circle points + for(int i = 0; i < steps; ++i) { + double phi = i*a; + double ex = endp(X) + r*std::cos(phi); + double ey = endp(Y) + r*std::sin(phi); + points.emplace_back(ex, ey, endp(Z)); + } + + // Lower circle points + for(int i = 0; i < steps; ++i) { + double phi = i*a; + double x = jp(X) + r*std::cos(phi); + double y = jp(Y) + r*std::sin(phi); + points.emplace_back(x, y, jp(Z)); + } + + // Now create long triangles connecting upper and lower circles + indices.reserve(2*ssteps); + auto offs = steps; + for(int i = 0; i < steps - 1; ++i) { + indices.emplace_back(i, i + offs, offs + i + 1); + indices.emplace_back(i, offs + i + 1, i + 1); + } + + // Last triangle connecting the first and last vertices + auto last = steps - 1; + indices.emplace_back(0, last, offs); + indices.emplace_back(last, offs + last, offs); + + // According to the slicing algorithms, we need to aid them with generating + // a watertight body. So we create a triangle fan for the upper and lower + // ending of the cylinder to close the geometry. + points.emplace_back(jp); int ci = int(points.size() - 1); + for(int i = 0; i < steps - 1; ++i) + indices.emplace_back(i + offs + 1, i + offs, ci); + + indices.emplace_back(offs, steps + offs - 1, ci); + + points.emplace_back(endp); ci = int(points.size() - 1); + for(int i = 0; i < steps - 1; ++i) + indices.emplace_back(ci, i, i + 1); + + indices.emplace_back(steps - 1, 0, ci); + + return ret; +} + +Head::Head(double r_big_mm, + double r_small_mm, + double length_mm, + double penetration, + const Vec3d &direction, + const Vec3d &offset, + const size_t circlesteps) + : steps(circlesteps) + , dir(direction) + , tr(offset) + , r_back_mm(r_big_mm) + , r_pin_mm(r_small_mm) + , width_mm(length_mm) + , penetration_mm(penetration) +{ + assert(width_mm > 0.); + assert(r_back_mm > 0.); + assert(r_pin_mm > 0.); + + // We create two spheres which will be connected with a robe that fits + // both circles perfectly. + + // Set up the model detail level + const double detail = 2*PI/steps; + + // We don't generate whole circles. Instead, we generate only the + // portions which are visible (not covered by the robe) To know the + // exact portion of the bottom and top circles we need to use some + // rules of tangent circles from which we can derive (using simple + // triangles the following relations: + + // The height of the whole mesh + const double h = r_big_mm + r_small_mm + width_mm; + double phi = PI/2 - std::acos( (r_big_mm - r_small_mm) / h ); + + // To generate a whole circle we would pass a portion of (0, Pi) + // To generate only a half horizontal circle we can pass (0, Pi/2) + // The calculated phi is an offset to the half circles needed to smooth + // the transition from the circle to the robe geometry + + auto&& s1 = sphere(r_big_mm, make_portion(0, PI/2 + phi), detail); + auto&& s2 = sphere(r_small_mm, make_portion(PI/2 + phi, PI), detail); + + for(auto& p : s2.points) p.z() += h; + + mesh.merge(s1); + mesh.merge(s2); + + for(size_t idx1 = s1.points.size() - steps, idx2 = s1.points.size(); + idx1 < s1.points.size() - 1; + idx1++, idx2++) + { + 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); + } + + auto i1s1 = coord_t(s1.points.size()) - coord_t(steps); + auto i2s1 = coord_t(s1.points.size()) - 1; + 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); + + // 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) + for(auto& p : mesh.points) p.z() -= (h + r_small_mm - penetration_mm); +} + +Pillar::Pillar(const Vec3d &jp, const Vec3d &endp, double radius, size_t st): + r(radius), steps(st), endpt(endp), starts_from_head(false) +{ + assert(steps > 0); + + height = jp(Z) - endp(Z); + if(height > EPSILON) { // Endpoint is below the starting point + + // We just create a bridge geometry with the pillar parameters and + // move the data. + Contour3D body = cylinder(radius, height, st, endp); + mesh.points.swap(body.points); + mesh.indices.swap(body.indices); + } +} + +Pillar &Pillar::add_base(double baseheight, double radius) +{ + if(baseheight <= 0) return *this; + if(baseheight > height) baseheight = height; + + assert(steps >= 0); + auto last = int(steps - 1); + + if(radius < r ) radius = r; + + double a = 2*PI/steps; + double z = endpt(Z) + baseheight; + + for(size_t i = 0; i < steps; ++i) { + double phi = i*a; + double x = endpt(X) + r*std::cos(phi); + double y = endpt(Y) + r*std::sin(phi); + base.points.emplace_back(x, y, z); + } + + for(size_t i = 0; i < steps; ++i) { + double phi = i*a; + double x = endpt(X) + radius*std::cos(phi); + double y = endpt(Y) + radius*std::sin(phi); + base.points.emplace_back(x, y, z - baseheight); + } + + auto ep = endpt; ep(Z) += baseheight; + base.points.emplace_back(endpt); + base.points.emplace_back(ep); + + auto& indices = base.indices; + auto hcenter = int(base.points.size() - 1); + auto lcenter = int(base.points.size() - 2); + auto offs = int(steps); + for(int i = 0; i < last; ++i) { + indices.emplace_back(i, i + offs, offs + i + 1); + indices.emplace_back(i, offs + i + 1, i + 1); + indices.emplace_back(i, i + 1, hcenter); + indices.emplace_back(lcenter, offs + i + 1, offs + i); + } + + indices.emplace_back(0, last, offs); + indices.emplace_back(last, offs + last, offs); + indices.emplace_back(hcenter, last, 0); + indices.emplace_back(offs, offs + last, lcenter); + return *this; +} + +Bridge::Bridge(const Vec3d &j1, const Vec3d &j2, double r_mm, size_t steps): + r(r_mm), startp(j1), endp(j2) +{ + using Quaternion = Eigen::Quaternion; + Vec3d dir = (j2 - j1).normalized(); + double d = distance(j2, j1); + + mesh = cylinder(r, d, steps); + + auto quater = Quaternion::FromTwoVectors(Vec3d{0,0,1}, dir); + for(auto& p : mesh.points) p = quater * p + j1; +} + +CompactBridge::CompactBridge(const Vec3d &sp, + const Vec3d &ep, + const Vec3d &n, + double r, + bool endball, + size_t steps) +{ + Vec3d startp = sp + r * n; + Vec3d dir = (ep - startp).normalized(); + Vec3d endp = ep - r * dir; + + Bridge br(startp, endp, r, steps); + mesh.merge(br.mesh); + + // now add the pins + double fa = 2*PI/steps; + auto upperball = sphere(r, Portion{PI / 2 - fa, PI}, fa); + for(auto& p : upperball.points) p += startp; + + if(endball) { + auto lowerball = sphere(r, Portion{0, PI/2 + 2*fa}, fa); + for(auto& p : lowerball.points) p += endp; + mesh.merge(lowerball); + } + + mesh.merge(upperball); +} + +Pad::Pad(const TriangleMesh &support_mesh, + const ExPolygons & model_contours, + double ground_level, + const PadConfig & pcfg, + ThrowOnCancel thr) + : cfg(pcfg) + , zlevel(ground_level + pcfg.full_height() - pcfg.required_elevation()) +{ + thr(); + + ExPolygons sup_contours; + + float zstart = float(zlevel); + float zend = zstart + float(pcfg.full_height() + EPSILON); + + pad_blueprint(support_mesh, sup_contours, grid(zstart, zend, 0.1f), thr); + create_pad(sup_contours, model_contours, tmesh, pcfg); + + tmesh.translate(0, 0, float(zlevel)); + if (!tmesh.empty()) tmesh.require_shared_vertices(); +} + +const TriangleMesh &SupportTreeBuilder::add_pad(const ExPolygons &modelbase, + const PadConfig & cfg) +{ + m_pad = Pad{merged_mesh(), modelbase, ground_level, cfg, ctl().cancelfn}; + return m_pad.tmesh; +} + +SupportTreeBuilder::SupportTreeBuilder(SupportTreeBuilder &&o) + : m_heads(std::move(o.m_heads)) + , m_head_indices{std::move(o.m_head_indices)} + , m_pillars{std::move(o.m_pillars)} + , m_bridges{std::move(o.m_bridges)} + , m_crossbridges{std::move(o.m_crossbridges)} + , m_compact_bridges{std::move(o.m_compact_bridges)} + , m_pad{std::move(o.m_pad)} + , m_meshcache{std::move(o.m_meshcache)} + , m_meshcache_valid{o.m_meshcache_valid} + , m_model_height{o.m_model_height} + , ground_level{o.ground_level} +{} + +SupportTreeBuilder::SupportTreeBuilder(const SupportTreeBuilder &o) + : m_heads(o.m_heads) + , m_head_indices{o.m_head_indices} + , m_pillars{o.m_pillars} + , m_bridges{o.m_bridges} + , m_crossbridges{o.m_crossbridges} + , m_compact_bridges{o.m_compact_bridges} + , m_pad{o.m_pad} + , m_meshcache{o.m_meshcache} + , m_meshcache_valid{o.m_meshcache_valid} + , m_model_height{o.m_model_height} + , ground_level{o.ground_level} +{} + +SupportTreeBuilder &SupportTreeBuilder::operator=(SupportTreeBuilder &&o) +{ + m_heads = std::move(o.m_heads); + m_head_indices = std::move(o.m_head_indices); + m_pillars = std::move(o.m_pillars); + m_bridges = std::move(o.m_bridges); + m_crossbridges = std::move(o.m_crossbridges); + m_compact_bridges = std::move(o.m_compact_bridges); + m_pad = std::move(o.m_pad); + m_meshcache = std::move(o.m_meshcache); + m_meshcache_valid = o.m_meshcache_valid; + m_model_height = o.m_model_height; + ground_level = o.ground_level; + return *this; +} + +SupportTreeBuilder &SupportTreeBuilder::operator=(const SupportTreeBuilder &o) +{ + m_heads = o.m_heads; + m_head_indices = o.m_head_indices; + m_pillars = o.m_pillars; + m_bridges = o.m_bridges; + m_crossbridges = o.m_crossbridges; + m_compact_bridges = o.m_compact_bridges; + m_pad = o.m_pad; + m_meshcache = o.m_meshcache; + m_meshcache_valid = o.m_meshcache_valid; + m_model_height = o.m_model_height; + ground_level = o.ground_level; + return *this; +} + +const TriangleMesh &SupportTreeBuilder::merged_mesh() const +{ + if (m_meshcache_valid) return m_meshcache; + + Contour3D merged; + + for (auto &head : m_heads) { + if (ctl().stopcondition()) break; + if (head.is_valid()) merged.merge(head.mesh); + } + + for (auto &stick : m_pillars) { + if (ctl().stopcondition()) break; + merged.merge(stick.mesh); + merged.merge(stick.base); + } + + for (auto &j : m_junctions) { + if (ctl().stopcondition()) break; + merged.merge(j.mesh); + } + + for (auto &cb : m_compact_bridges) { + if (ctl().stopcondition()) break; + merged.merge(cb.mesh); + } + + for (auto &bs : m_bridges) { + if (ctl().stopcondition()) break; + merged.merge(bs.mesh); + } + + for (auto &bs : m_crossbridges) { + if (ctl().stopcondition()) break; + merged.merge(bs.mesh); + } + + if (ctl().stopcondition()) { + // In case of failure we have to return an empty mesh + m_meshcache = TriangleMesh(); + return m_meshcache; + } + + m_meshcache = mesh(merged); + + // The mesh will be passed by const-pointer to TriangleMeshSlicer, + // which will need this. + if (!m_meshcache.empty()) m_meshcache.require_shared_vertices(); + + BoundingBoxf3 &&bb = m_meshcache.bounding_box(); + m_model_height = bb.max(Z) - bb.min(Z); + + m_meshcache_valid = true; + return m_meshcache; +} + +double SupportTreeBuilder::full_height() const +{ + if (merged_mesh().empty() && !pad().empty()) + return pad().cfg.full_height(); + + double h = mesh_height(); + if (!pad().empty()) h += pad().cfg.required_elevation(); + return h; +} + +const TriangleMesh &SupportTreeBuilder::merge_and_cleanup() +{ + // in case the mesh is not generated, it should be... + auto &ret = merged_mesh(); + + // Doing clear() does not garantee to release the memory. + m_heads = {}; + m_head_indices = {}; + m_pillars = {}; + m_junctions = {}; + m_bridges = {}; + m_compact_bridges = {}; + + return ret; +} + +const TriangleMesh &SupportTreeBuilder::retrieve_mesh(MeshType meshtype) const +{ + switch(meshtype) { + case MeshType::Support: return merged_mesh(); + case MeshType::Pad: return pad().tmesh; + } + + return m_meshcache; +} + +bool SupportTreeBuilder::build(const SupportableMesh &sm) +{ + ground_level = sm.emesh.ground_level() - sm.cfg.object_elevation_mm; + return SupportTreeBuildsteps::execute(*this, sm); +} + +} +} diff --git a/src/libslic3r/SLA/SLASupportTreeBuilder.hpp b/src/libslic3r/SLA/SLASupportTreeBuilder.hpp new file mode 100644 index 000000000..c0d9f04c0 --- /dev/null +++ b/src/libslic3r/SLA/SLASupportTreeBuilder.hpp @@ -0,0 +1,496 @@ +#ifndef SUPPORTTREEBUILDER_HPP +#define SUPPORTTREEBUILDER_HPP + +#include "SLAConcurrency.hpp" +#include "SLABoilerPlate.hpp" +#include "SLASupportTree.hpp" +#include "SLAPad.hpp" +#include + +namespace Slic3r { +namespace sla { + +/** + * Terminology: + * + * Support point: + * The point on the model surface that needs support. + * + * Pillar: + * A thick column that spans from a support point to the ground and has + * a thick cone shaped base where it touches the ground. + * + * Ground facing support point: + * A support point that can be directly connected with the ground with a pillar + * that does not collide or cut through the model. + * + * Non ground facing support point: + * A support point that cannot be directly connected with the ground (only with + * the model surface). + * + * Head: + * The pinhead that connects to the model surface with the sharp end end + * to a pillar or bridge stick with the dull end. + * + * Headless support point: + * A support point on the model surface for which there is not enough place for + * the head. It is either in a hole or there is some barrier that would collide + * with the head geometry. The headless support point can be ground facing and + * non ground facing as well. + * + * Bridge: + * A stick that connects two pillars or a head with a pillar. + * + * Junction: + * A small ball in the intersection of two or more sticks (pillar, bridge, ...) + * + * CompactBridge: + * A bridge that connects a headless support point with the model surface or a + * nearby pillar. + */ + +using Coordf = double; +using Portion = std::tuple; + +inline Portion make_portion(double a, double b) { + return std::make_tuple(a, b); +} + +template double distance(const Vec& p) { + return std::sqrt(p.transpose() * p); +} + +template double distance(const Vec& pp1, const Vec& pp2) { + auto p = pp2 - pp1; + return distance(p); +} + +Contour3D sphere(double rho, Portion portion = make_portion(0.0, 2.0*PI), + double fa=(2*PI/360)); + +// Down facing cylinder in Z direction with arguments: +// r: radius +// 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}); + +const constexpr long ID_UNSET = -1; + +struct Head { + Contour3D mesh; + + size_t steps = 45; + Vec3d dir = {0, 0, -1}; + Vec3d tr = {0, 0, 0}; + + double r_back_mm = 1; + double r_pin_mm = 0.5; + double width_mm = 2; + double penetration_mm = 0.5; + + // For identification purposes. This will be used as the index into the + // container holding the head structures. See SLASupportTree::Impl + long id = ID_UNSET; + + // If there is a pillar connecting to this head, then the id will be set. + long pillar_id = ID_UNSET; + + long bridge_id = ID_UNSET; + + inline void invalidate() { id = ID_UNSET; } + inline bool is_valid() const { return id >= 0; } + + Head(double r_big_mm, + double r_small_mm, + double length_mm, + double penetration, + const Vec3d &direction = {0, 0, -1}, // direction (normal to the dull end) + const Vec3d &offset = {0, 0, 0}, // displacement + const size_t circlesteps = 45); + + void transform() + { + using Quaternion = Eigen::Quaternion; + + // We rotate the head to the specified direction The head's pointing + // side is facing upwards so this means that it would hold a support + // point with a normal pointing straight down. This is the reason of + // the -1 z coordinate + auto quatern = Quaternion::FromTwoVectors(Vec3d{0, 0, -1}, dir); + + for(auto& p : mesh.points) p = quatern * p + tr; + } + + inline double fullwidth() const + { + return 2 * r_pin_mm + width_mm + 2*r_back_mm - penetration_mm; + } + + inline Vec3d junction_point() const + { + return tr + ( 2 * r_pin_mm + width_mm + r_back_mm - penetration_mm)*dir; + } + + inline double request_pillar_radius(double radius) const + { + const double rmax = r_back_mm; + return radius > 0 && radius < rmax ? radius : rmax; + } +}; + +struct Junction { + Contour3D mesh; + double r = 1; + size_t steps = 45; + Vec3d pos; + + long id = ID_UNSET; + + Junction(const Vec3d& tr, double r_mm, size_t stepnum = 45): + r(r_mm), steps(stepnum), pos(tr) + { + mesh = sphere(r_mm, make_portion(0, PI), 2*PI/steps); + for(auto& p : mesh.points) p += tr; + } +}; + +struct Pillar { + Contour3D mesh; + Contour3D base; + double r = 1; + size_t steps = 0; + Vec3d endpt; + double height = 0; + + long id = ID_UNSET; + + // If the pillar connects to a head, this is the id of that head + bool starts_from_head = true; // Could start from a junction as well + long start_junction_id = ID_UNSET; + + // How many bridges are connected to this pillar + unsigned bridges = 0; + + // How many pillars are cascaded with this one + unsigned links = 0; + + Pillar(const Vec3d& jp, const Vec3d& endp, + double radius = 1, size_t st = 45); + + Pillar(const Junction &junc, const Vec3d &endp) + : Pillar(junc.pos, endp, junc.r, junc.steps) + {} + + Pillar(const Head &head, const Vec3d &endp, double radius = 1) + : Pillar(head.junction_point(), endp, + head.request_pillar_radius(radius), head.steps) + {} + + inline Vec3d startpoint() const + { + return {endpt(X), endpt(Y), endpt(Z) + height}; + } + + inline const Vec3d& endpoint() const { return endpt; } + + Pillar& add_base(double baseheight = 3, double radius = 2); +}; + +// A Bridge between two pillars (with junction endpoints) +struct Bridge { + Contour3D mesh; + double r = 0.8; + long id = ID_UNSET; + Vec3d startp = Vec3d::Zero(), endp = Vec3d::Zero(); + + Bridge(const Vec3d &j1, + const Vec3d &j2, + double r_mm = 0.8, + size_t steps = 45); +}; + +// A bridge that spans from model surface to model surface with small connecting +// edges on the endpoints. Used for headless support points. +struct CompactBridge { + Contour3D mesh; + long id = ID_UNSET; + + CompactBridge(const Vec3d& sp, + const Vec3d& ep, + const Vec3d& n, + double r, + bool endball = true, + size_t steps = 45); +}; + +// A wrapper struct around the pad +struct Pad { + TriangleMesh tmesh; + PadConfig cfg; + double zlevel = 0; + + Pad() = default; + + Pad(const TriangleMesh &support_mesh, + const ExPolygons & model_contours, + double ground_level, + const PadConfig & pcfg, + ThrowOnCancel thr); + + bool empty() const { return tmesh.facets_count() == 0; } +}; + +// This class will hold the support tree meshes with some additional +// bookkeeping as well. Various parts of the support geometry are stored +// separately and are merged when the caller queries the merged mesh. The +// merged result is cached for fast subsequent delivery of the merged mesh +// which can be quite complex. The support tree creation algorithm can use an +// instance of this class as a somewhat higher level tool for crafting the 3D +// support mesh. Parts can be added with the appropriate methods such as +// add_head or add_pillar which forwards the constructor arguments and fills +// the IDs of these substructures. The IDs are basically indices into the +// arrays of the appropriate type (heads, pillars, etc...). One can later query +// e.g. a pillar for a specific head... +// +// The support pad is considered an auxiliary geometry and is not part of the +// merged mesh. It can be retrieved using a dedicated method (pad()) +class SupportTreeBuilder: public SupportTree { + // For heads it is beneficial to use the same IDs as for the support points. + std::vector m_heads; + std::vector m_head_indices; + std::vector m_pillars; + std::vector m_junctions; + std::vector m_bridges; + std::vector m_crossbridges; + std::vector m_compact_bridges; + Pad m_pad; + + using Mutex = ccr::SpinningMutex; + + mutable TriangleMesh m_meshcache; + mutable Mutex m_mutex; + mutable bool m_meshcache_valid = false; + mutable double m_model_height = 0; // the full height of the model + + template + const Bridge& _add_bridge(std::vector &br, Args&&... args) + { + std::lock_guard lk(m_mutex); + br.emplace_back(std::forward(args)...); + br.back().id = long(br.size() - 1); + m_meshcache_valid = false; + return br.back(); + } + +public: + double ground_level = 0; + + SupportTreeBuilder() = default; + SupportTreeBuilder(SupportTreeBuilder &&o); + SupportTreeBuilder(const SupportTreeBuilder &o); + SupportTreeBuilder& operator=(SupportTreeBuilder &&o); + SupportTreeBuilder& operator=(const SupportTreeBuilder &o); + + template Head& add_head(unsigned id, Args&&... args) + { + std::lock_guard lk(m_mutex); + m_heads.emplace_back(std::forward(args)...); + m_heads.back().id = id; + + if (id >= m_head_indices.size()) m_head_indices.resize(id + 1); + m_head_indices[id] = m_heads.size() - 1; + + m_meshcache_valid = false; + return m_heads.back(); + } + + template long add_pillar(long headid, Args&&... args) + { + std::lock_guard lk(m_mutex); + if (m_pillars.capacity() < m_heads.size()) + m_pillars.reserve(m_heads.size() * 10); + + assert(headid >= 0 && size_t(headid) < m_head_indices.size()); + Head &head = m_heads[m_head_indices[size_t(headid)]]; + + m_pillars.emplace_back(head, std::forward(args)...); + Pillar& pillar = m_pillars.back(); + pillar.id = long(m_pillars.size() - 1); + head.pillar_id = pillar.id; + pillar.start_junction_id = head.id; + pillar.starts_from_head = true; + + m_meshcache_valid = false; + return pillar.id; + } + + void add_pillar_base(long pid, double baseheight = 3, double radius = 2) + { + std::lock_guard lk(m_mutex); + assert(pid >= 0 && size_t(pid) < m_pillars.size()); + m_pillars[size_t(pid)].add_base(baseheight, radius); + } + + void increment_bridges(const Pillar& pillar) + { + std::lock_guard lk(m_mutex); + assert(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size()); + + if(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size()) + m_pillars[size_t(pillar.id)].bridges++; + } + + void increment_links(const Pillar& pillar) + { + std::lock_guard lk(m_mutex); + assert(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size()); + + if(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size()) + m_pillars[size_t(pillar.id)].links++; + } + + unsigned bridgecount(const Pillar &pillar) const { + std::lock_guard lk(m_mutex); + assert(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size()); + return pillar.bridges; + } + + template long add_pillar(Args&&...args) + { + std::lock_guard lk(m_mutex); + if (m_pillars.capacity() < m_heads.size()) + m_pillars.reserve(m_heads.size() * 10); + + m_pillars.emplace_back(std::forward(args)...); + Pillar& pillar = m_pillars.back(); + pillar.id = long(m_pillars.size() - 1); + pillar.starts_from_head = false; + m_meshcache_valid = false; + return pillar.id; + } + + const Pillar& head_pillar(unsigned headid) const + { + std::lock_guard lk(m_mutex); + assert(headid < m_head_indices.size()); + + const Head& h = m_heads[m_head_indices[headid]]; + assert(h.pillar_id >= 0 && h.pillar_id < long(m_pillars.size())); + + return m_pillars[size_t(h.pillar_id)]; + } + + template const Junction& add_junction(Args&&... args) + { + std::lock_guard lk(m_mutex); + m_junctions.emplace_back(std::forward(args)...); + m_junctions.back().id = long(m_junctions.size() - 1); + m_meshcache_valid = false; + return m_junctions.back(); + } + + const Bridge& add_bridge(const Vec3d &s, const Vec3d &e, double r, size_t n = 45) + { + return _add_bridge(m_bridges, s, e, r, n); + } + + const Bridge& add_bridge(long headid, const Vec3d &endp, size_t s = 45) + { + std::lock_guard lk(m_mutex); + assert(headid >= 0 && size_t(headid) < m_head_indices.size()); + + Head &h = m_heads[m_head_indices[size_t(headid)]]; + m_bridges.emplace_back(h.junction_point(), endp, h.r_back_mm, s); + m_bridges.back().id = long(m_bridges.size() - 1); + + h.bridge_id = m_bridges.back().id; + m_meshcache_valid = false; + return m_bridges.back(); + } + + template const Bridge& add_crossbridge(Args&&... args) + { + return _add_bridge(m_crossbridges, std::forward(args)...); + } + + template const CompactBridge& add_compact_bridge(Args&&...args) + { + std::lock_guard lk(m_mutex); + m_compact_bridges.emplace_back(std::forward(args)...); + m_compact_bridges.back().id = long(m_compact_bridges.size() - 1); + m_meshcache_valid = false; + return m_compact_bridges.back(); + } + + Head &head(unsigned id) + { + std::lock_guard lk(m_mutex); + assert(id < m_head_indices.size()); + + m_meshcache_valid = false; + return m_heads[m_head_indices[id]]; + } + + inline size_t pillarcount() const { + std::lock_guard lk(m_mutex); + return m_pillars.size(); + } + + inline const std::vector &pillars() const { return m_pillars; } + inline const std::vector &heads() const { return m_heads; } + inline const std::vector &bridges() const { return m_bridges; } + inline const std::vector &crossbridges() const { return m_crossbridges; } + + template inline IntegerOnly pillar(T id) const + { + std::lock_guard lk(m_mutex); + assert(id >= 0 && size_t(id) < m_pillars.size() && + size_t(id) < std::numeric_limits::max()); + + return m_pillars[size_t(id)]; + } + + template inline IntegerOnly pillar(T id) + { + std::lock_guard lk(m_mutex); + assert(id >= 0 && size_t(id) < m_pillars.size() && + size_t(id) < std::numeric_limits::max()); + + return m_pillars[size_t(id)]; + } + + const Pad& pad() const { return m_pad; } + + // WITHOUT THE PAD!!! + const TriangleMesh &merged_mesh() const; + + // WITH THE PAD + double full_height() const; + + // WITHOUT THE PAD!!! + inline double mesh_height() const + { + if (!m_meshcache_valid) merged_mesh(); + return m_model_height; + } + + // Intended to be called after the generation is fully complete + const TriangleMesh & merge_and_cleanup(); + + // Implement SupportTree interface: + + const TriangleMesh &add_pad(const ExPolygons &modelbase, + const PadConfig & pcfg) override; + + void remove_pad() override { m_pad = Pad(); } + + virtual const TriangleMesh &retrieve_mesh( + MeshType meshtype = MeshType::Support) const override; + + bool build(const SupportableMesh &supportable_mesh); +}; + +}} // namespace Slic3r::sla + +#endif // SUPPORTTREEBUILDER_HPP diff --git a/src/libslic3r/SLA/SLASupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SLASupportTreeBuildsteps.cpp new file mode 100644 index 000000000..392a963e1 --- /dev/null +++ b/src/libslic3r/SLA/SLASupportTreeBuildsteps.cpp @@ -0,0 +1,1387 @@ +#include "SLASupportTreeBuildsteps.hpp" + +#include +#include +#include + +namespace Slic3r { +namespace sla { + +SupportTreeBuildsteps::SupportTreeBuildsteps(SupportTreeBuilder & builder, + const SupportableMesh &sm) + : m_cfg(sm.cfg) + , m_mesh(sm.emesh) + , m_support_pts(sm.pts) + , m_support_nmls(sm.pts.size(), 3) + , m_builder(builder) + , m_points(sm.pts.size(), 3) + , m_thr(builder.ctl().cancelfn) +{ + // Prepare the support points in Eigen/IGL format as well, we will use + // it mostly in this form. + + long i = 0; + for (const SupportPoint &sp : m_support_pts) { + m_points.row(i)(X) = double(sp.pos(X)); + m_points.row(i)(Y) = double(sp.pos(Y)); + m_points.row(i)(Z) = double(sp.pos(Z)); + ++i; + } +} + +bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder, + const SupportableMesh &sm) +{ + if(sm.pts.empty()) return false; + + SupportTreeBuildsteps alg(builder, sm); + + // Let's define the individual steps of the processing. We can experiment + // later with the ordering and the dependencies between them. + enum Steps { + BEGIN, + FILTER, + PINHEADS, + CLASSIFY, + ROUTING_GROUND, + ROUTING_NONGROUND, + CASCADE_PILLARS, + HEADLESS, + MERGE_RESULT, + DONE, + ABORT, + NUM_STEPS + //... + }; + + // Collect the algorithm steps into a nice sequence + std::array, NUM_STEPS> program = { + [] () { + // Begin... + // Potentially clear up the shared data (not needed for now) + }, + + std::bind(&SupportTreeBuildsteps::filter, &alg), + + std::bind(&SupportTreeBuildsteps::add_pinheads, &alg), + + std::bind(&SupportTreeBuildsteps::classify, &alg), + + std::bind(&SupportTreeBuildsteps::routing_to_ground, &alg), + + std::bind(&SupportTreeBuildsteps::routing_to_model, &alg), + + std::bind(&SupportTreeBuildsteps::interconnect_pillars, &alg), + + std::bind(&SupportTreeBuildsteps::routing_headless, &alg), + + std::bind(&SupportTreeBuildsteps::merge_result, &alg), + + [] () { + // Done + }, + + [] () { + // Abort + } + }; + + Steps pc = BEGIN; + + if(sm.cfg.ground_facing_only) { + program[ROUTING_NONGROUND] = []() { + BOOST_LOG_TRIVIAL(info) + << "Skipping model-facing supports as requested."; + }; + program[HEADLESS] = []() { + BOOST_LOG_TRIVIAL(info) << "Skipping headless stick generation as" + " requested."; + }; + } + + // Let's define a simple automaton that will run our program. + auto progress = [&builder, &pc] () { + static const std::array stepstr { + "Starting", + "Filtering", + "Generate pinheads", + "Classification", + "Routing to ground", + "Routing supports to model surface", + "Interconnecting pillars", + "Processing small holes", + "Merging support mesh", + "Done", + "Abort" + }; + + static const std::array stepstate { + 0, + 10, + 30, + 50, + 60, + 70, + 80, + 85, + 99, + 100, + 0 + }; + + if(builder.ctl().stopcondition()) pc = ABORT; + + switch(pc) { + case BEGIN: pc = FILTER; break; + case FILTER: pc = PINHEADS; break; + case PINHEADS: pc = CLASSIFY; break; + case CLASSIFY: pc = ROUTING_GROUND; break; + case ROUTING_GROUND: pc = ROUTING_NONGROUND; break; + case ROUTING_NONGROUND: pc = CASCADE_PILLARS; break; + case CASCADE_PILLARS: pc = HEADLESS; break; + case HEADLESS: pc = MERGE_RESULT; break; + case MERGE_RESULT: pc = DONE; break; + case DONE: + case ABORT: break; + default: ; + } + + builder.ctl().statuscb(stepstate[pc], stepstr[pc]); + }; + + // Just here we run the computation... + while(pc < DONE) { + progress(); + program[pc](); + } + + return pc == ABORT; +} + +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 + + // 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; + + // 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; + + // 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(); + + auto& m = m_mesh; + using HitResult = EigenMesh3D::hit_result; + + // 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; +} + +EigenMesh3D::hit_result SupportTreeBuildsteps::bridge_mesh_intersect( + const Vec3d &s, const Vec3d &dir, double r, bool ins_check) +{ + static const size_t SAMPLES = 8; + + // 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; + + // Hit results + 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; + }); + + auto mit = std::min_element(hits.begin(), hits.end()); + + return *mit; +} + +bool SupportTreeBuildsteps::interconnect(const Pillar &pillar, + const Pillar &nextpillar) +{ + // We need to get the starting point of the zig-zag pattern. We have to + // be aware that the two head junctions are at different heights. We + // may start from the lowest junction and call it a day but this + // strategy would leave unconnected a lot of pillar duos where the + // shorter pillar is too short to start a new bridge but the taller + // pillar could still be bridged with the shorter one. + bool was_connected = false; + + Vec3d supper = pillar.startpoint(); + Vec3d slower = nextpillar.startpoint(); + Vec3d eupper = pillar.endpoint(); + Vec3d elower = nextpillar.endpoint(); + + double zmin = m_builder.ground_level + m_cfg.base_height_mm; + eupper(Z) = std::max(eupper(Z), zmin); + elower(Z) = std::max(elower(Z), zmin); + + // The usable length of both pillars should be positive + if(slower(Z) - elower(Z) < 0) return false; + if(supper(Z) - eupper(Z) < 0) return false; + + double pillar_dist = distance(Vec2d{slower(X), slower(Y)}, + Vec2d{supper(X), supper(Y)}); + double bridge_distance = pillar_dist / std::cos(-m_cfg.bridge_slope); + double zstep = pillar_dist * std::tan(-m_cfg.bridge_slope); + + if(pillar_dist < 2 * m_cfg.head_back_radius_mm || + pillar_dist > m_cfg.max_pillar_link_distance_mm) return false; + + if(supper(Z) < slower(Z)) supper.swap(slower); + if(eupper(Z) < elower(Z)) eupper.swap(elower); + + double startz = 0, endz = 0; + + startz = slower(Z) - zstep < supper(Z) ? slower(Z) - zstep : slower(Z); + endz = eupper(Z) + zstep > elower(Z) ? eupper(Z) + zstep : eupper(Z); + + if(slower(Z) - eupper(Z) < std::abs(zstep)) { + // no space for even one cross + + // Get max available space + startz = std::min(supper(Z), slower(Z) - zstep); + endz = std::max(eupper(Z) + zstep, elower(Z)); + + // Align to center + double available_dist = (startz - endz); + double rounds = std::floor(available_dist / std::abs(zstep)); + startz -= 0.5 * (available_dist - rounds * std::abs(zstep)); + } + + auto pcm = m_cfg.pillar_connection_mode; + bool docrosses = + pcm == PillarConnectionMode::cross || + (pcm == PillarConnectionMode::dynamic && + pillar_dist > 2*m_cfg.base_radius_mm); + + // 'sj' means starting junction, 'ej' is the end junction of a bridge. + // They will be swapped in every iteration thus the zig-zag pattern. + // According to a config parameter, a second bridge may be added which + // results in a cross connection between the pillars. + Vec3d sj = supper, ej = slower; sj(Z) = startz; ej(Z) = sj(Z) + zstep; + + // 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) + { + m_builder.add_crossbridge(sj, ej, pillar.r); + was_connected = true; + } + + // double bridging: (crosses) + if(docrosses) { + 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), + pillar.r) >= bridge_distance) { + // need to check collision for the cross stick + m_builder.add_crossbridge(sjback, ejback, pillar.r); + was_connected = true; + } + } + + sj.swap(ej); + ej(Z) = sj(Z) + zstep; + } + + return was_connected; +} + +bool SupportTreeBuildsteps::connect_to_nearpillar(const Head &head, + long nearpillar_id) +{ + auto nearpillar = [this, nearpillar_id]() -> const Pillar& { + return m_builder.pillar(nearpillar_id); + }; + + if (m_builder.bridgecount(nearpillar()) > m_cfg.max_bridges_on_pillar) + return false; + + Vec3d headjp = head.junction_point(); + Vec3d nearjp_u = nearpillar().startpoint(); + Vec3d nearjp_l = nearpillar().endpoint(); + + double r = head.r_back_mm; + double d2d = distance(to_2d(headjp), to_2d(nearjp_u)); + double d3d = distance(headjp, nearjp_u); + + double hdiff = nearjp_u(Z) - headjp(Z); + double slope = std::atan2(hdiff, d2d); + + Vec3d bridgestart = headjp; + Vec3d bridgeend = nearjp_u; + double max_len = m_cfg.max_bridge_length_mm; + double max_slope = m_cfg.bridge_slope; + double zdiff = 0.0; + + // check the default situation if feasible for a bridge + if(d3d > max_len || slope > -max_slope) { + // not feasible to connect the two head junctions. We have to search + // for a suitable touch point. + + double Zdown = headjp(Z) + d2d * std::tan(-max_slope); + Vec3d touchjp = bridgeend; touchjp(Z) = Zdown; + double D = distance(headjp, touchjp); + zdiff = Zdown - nearjp_u(Z); + + if(zdiff > 0) { + Zdown -= zdiff; + bridgestart(Z) -= zdiff; + touchjp(Z) = Zdown; + + double t = bridge_mesh_intersect(headjp, {0,0,-1}, r); + + // We can't insert a pillar under the source head to connect + // with the nearby pillar's starting junction + if(t < zdiff) return false; + } + + if(Zdown <= nearjp_u(Z) && Zdown >= nearjp_l(Z) && D < max_len) + bridgeend(Z) = Zdown; + else + return false; + } + + // There will be a minimum distance from the ground where the + // bridge is allowed to connect. This is an empiric value. + 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); + + // Cannot insert the bridge. (further search might not worth the hassle) + if(t < distance(bridgestart, bridgeend)) return false; + + std::lock_guard lk(m_bridge_mutex); + + if (m_builder.bridgecount(nearpillar()) < m_cfg.max_bridges_on_pillar) { + // A partial pillar is needed under the starting head. + if(zdiff > 0) { + m_builder.add_pillar(head.id, bridgestart, r); + m_builder.add_junction(bridgestart, r); + m_builder.add_bridge(bridgestart, bridgeend, head.r_back_mm); + } else { + m_builder.add_bridge(head.id, bridgeend); + } + + m_builder.increment_bridges(nearpillar()); + } else return false; + + return true; +} + +bool SupportTreeBuildsteps::search_pillar_and_connect(const Head &head) +{ + PointIndex spindex = m_pillar_index.guarded_clone(); + + long nearest_id = ID_UNSET; + + Vec3d querypoint = head.junction_point(); + + while(nearest_id < 0 && !spindex.empty()) { m_thr(); + // loop until a suitable head is not found + // if there is a pillar closer than the cluster center + // (this may happen as the clustering is not perfect) + // than we will bridge to this closer pillar + + Vec3d qp(querypoint(X), querypoint(Y), m_builder.ground_level); + auto qres = spindex.nearest(qp, 1); + if(qres.empty()) break; + + auto ne = qres.front(); + nearest_id = ne.second; + + if(nearest_id >= 0) { + if(size_t(nearest_id) < m_builder.pillarcount()) { + if(!connect_to_nearpillar(head, nearest_id)) { + nearest_id = ID_UNSET; // continue searching + spindex.remove(ne); // without the current pillar + } + } + } + } + + return nearest_id >= 0; +} + +void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, + const Vec3d &sourcedir, + 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}; + + double gndlvl = m_builder.ground_level; + Vec3d endp = {jp(X), jp(Y), gndlvl}; + double sd = m_cfg.pillar_base_safety_distance_mm; + long pillar_id = ID_UNSET; + double min_dist = sd + m_cfg.base_radius_mm + EPSILON; + double dist = 0; + bool can_add_base = true; + bool normal_mode = true; + + 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; + 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); + + auto result = solver.optimize_max( + [this, dir, jp, gndlvl](double mv) { + Vec3d endpt = jp + SQR2 * mv * dir; + endpt(Z) = gndlvl; + return std::sqrt(m_mesh.squared_distance(endpt)); + }, + initvals(mind), bound(0.0, 2 * min_dist)); + + mind = std::get<0>(result.optimum); + endp = jp + SQR2 * mind * dir; + Vec3d pgnd = {endp(X), endp(Y), gndlvl}; + can_add_base = result.score > min_dist; + + double gnd_offs = m_mesh.ground_level_offset(); + auto abort_in_shame = + [gnd_offs, &normal_mode, &can_add_base, &endp, jp, gndlvl]() + { + normal_mode = true; + can_add_base = false; // Nothing left to do, hope for the best + endp = {jp(X), jp(Y), gndlvl - gnd_offs }; + }; + + // We have to check if the bridge is feasible. + if (bridge_mesh_intersect(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 + else { + + auto hit = bridge_mesh_intersect(endp, DOWN, radius); + if (!std::isinf(hit.distance())) abort_in_shame(); + + pillar_id = m_builder.add_pillar(endp, pgnd, radius); + + if (can_add_base) + m_builder.add_pillar_base(pillar_id, m_cfg.base_height_mm, + m_cfg.base_radius_mm); + } + + m_builder.add_bridge(jp, endp, radius); + m_builder.add_junction(endp, radius); + + // Add a degenerated pillar and the bridge. + // The degenerate pillar will have zero length and it will + // prevent from queries of head_pillar() to have non-existing + // pillar when the head should have one. + if (head_id >= 0) + m_builder.add_pillar(head_id, jp, radius); + } + } + + if (normal_mode) { + pillar_id = head_id >= 0 ? m_builder.add_pillar(head_id, endp, radius) : + m_builder.add_pillar(jp, endp, radius); + + if (can_add_base) + m_builder.add_pillar_base(pillar_id, m_cfg.base_height_mm, + m_cfg.base_radius_mm); + } + + if(pillar_id >= 0) // Save the pillar endpoint in the spatial index + m_pillar_index.guarded_insert(endp, unsigned(pillar_id)); +} + +void SupportTreeBuildsteps::filter() +{ + // Get the points that are too close to each other and keep only the + // first one + auto aliases = cluster(m_points, D_SP, 2); + + PtIndices filtered_indices; + filtered_indices.reserve(aliases.size()); + m_iheads.reserve(aliases.size()); + m_iheadless.reserve(aliases.size()); + for(auto& a : aliases) { + // Here we keep only the front point of the cluster. + filtered_indices.emplace_back(a.front()); + } + + // calculate the normals to the triangles for filtered points + auto nmls = sla::normals(m_points, m_mesh, m_cfg.head_front_radius_mm, + m_thr, filtered_indices); + + // Not all of the support points have to be a valid position for + // support creation. The angle may be inappropriate or there may + // 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); + container.emplace_back(val); + }; + + auto filterfn = [this, &nmls, addfn](unsigned fidx, size_t i) { + m_thr(); + + auto n = nmls.row(Eigen::Index(i)); + + // for all normals we generate the spherical coordinates and + // saturate the polar angle to 45 degrees from the bottom then + // convert back to standard coordinates to get the new normal. + // Then we just create a quaternion from the two normals + // (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)); + + // skip if the tilt is not sane + if(polar >= PI - m_cfg.normal_cutoff_angle) { + + // We saturate the polar angle to 3pi/4 + polar = std::max(polar, 3*PI / 4); + + // save the head (pinpoint) position + Vec3d hp = m_points.row(fidx); + + double w = m_cfg.head_width_mm + + m_cfg.head_back_radius_mm + + 2*m_cfg.head_front_radius_mm; + + 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(); + + // check available distance + EigenMesh3D::hit_result t + = pinhead_mesh_intersect(hp, // touching point + nn, // normal + pin_r, + m_cfg.head_back_radius_mm, + w); + + if(t.distance() <= w) { + + // Let's try to optimize this angle, there might be a + // viable normal that doesn't collide with the model + // geometry and its very close to the default. + + StopCriteria stc; + stc.max_iterations = m_cfg.optimizer_max_iterations; + stc.relative_score_difference = m_cfg.optimizer_rel_score_diff; + stc.stop_score = w; // space greater than w is enough + GeneticOptimizer solver(stc); + solver.seed(0); // we want deterministic behavior + + 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(); + + double score = pinhead_mesh_intersect( + 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(-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; + } + } + + // save the verified and corrected normal + m_support_nmls.row(fidx) = nn; + + if (t.distance() > w) { + // Check distance from ground, we might have zero elevation. + if (hp(Z) + w * nn(Z) < m_builder.ground_level) { + addfn(m_iheadless, fidx); + } else { + // mark the point for needing a head. + addfn(m_iheads, fidx); + } + } else if (polar >= 3 * PI / 4) { + // Headless supports do not tilt like the headed ones + // so the normal should point almost to the ground. + addfn(m_iheadless, fidx); + } + } + }; + + ccr::enumerate(filtered_indices.begin(), filtered_indices.end(), filterfn); + + m_thr(); +} + +void SupportTreeBuildsteps::add_pinheads() +{ + for (unsigned i : m_iheads) { + m_thr(); + m_builder.add_head( + i, + m_cfg.head_back_radius_mm, + m_support_pts[i].head_front_radius, + m_cfg.head_width_mm, + m_cfg.head_penetration_mm, + m_support_nmls.row(i), // dir + m_support_pts[i].pos.cast() // displacement + ); + } +} + +void SupportTreeBuildsteps::classify() +{ + // We should first get the heads that reach the ground directly + PtIndices ground_head_indices; + ground_head_indices.reserve(m_iheads.size()); + m_iheads_onmodel.reserve(m_iheads.size()); + + // First we decide which heads reach the ground and can be full + // pillars and which shall be connected to the model surface (or + // search a suitable path around the surface that leads to the + // ground -- TODO) + for(unsigned i : m_iheads) { + 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); + + 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)); + } + + // We want to search for clusters of points that are far enough + // from each other in the XY plane to not cross their pillar bases + // These clusters of support points will join in one pillar, + // possibly in their centroid support point. + + auto pointfn = [this](unsigned i) { + return m_builder.head(i).junction_point(); + }; + + auto predicate = [this](const PointIndexEl &e1, + const PointIndexEl &e2) { + double d2d = distance(to_2d(e1.first), to_2d(e2.first)); + double d3d = distance(e1.first, e2.first); + return d2d < 2 * m_cfg.base_radius_mm + && d3d < m_cfg.max_bridge_length_mm; + }; + + m_pillar_clusters = cluster(ground_head_indices, pointfn, predicate, + m_cfg.max_bridges_on_pillar); +} + +void SupportTreeBuildsteps::routing_to_ground() +{ + const double pradius = m_cfg.head_back_radius_mm; + + ClusterEl cl_centroids; + cl_centroids.reserve(m_pillar_clusters.size()); + + for (auto &cl : m_pillar_clusters) { + m_thr(); + + // place all the centroid head positions into the index. We + // will query for alternative pillar positions. If a sidehead + // cannot connect to the cluster centroid, we have to search + // for another head with a full pillar. Also when there are two + // elements in the cluster, the centroid is arbitrary and the + // sidehead is allowed to connect to a nearby pillar to + // increase structural stability. + + if (cl.empty()) continue; + + // get the current cluster centroid + auto & thr = m_thr; + const auto &points = m_points; + 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 + + cl_centroids.emplace_back(hid); + + Head &h = m_builder.head(hid); + h.transform(); + + create_ground_pillar(h.junction_point(), h.dir, h.r_back_mm, h.id); + } + + // now we will go through the clusters ones again and connect the + // sidepoints with the cluster centroid (which is a ground pillar) + // or a nearby pillar if the centroid is unreachable. + size_t ci = 0; + for (auto cl : m_pillar_clusters) { + m_thr(); + + auto cidx = cl_centroids[ci++]; + + // TODO: don't consider the cluster centroid but calculate a + // central position where the pillar can be placed. this way + // the weight is distributed more effectively on the pillar. + + auto centerpillarID = m_builder.head_pillar(cidx).id; + + for (auto c : cl) { + m_thr(); + if (c == cidx) continue; + + auto &sidehead = m_builder.head(c); + sidehead.transform(); + + if (!connect_to_nearpillar(sidehead, centerpillarID) && + !search_pillar_and_connect(sidehead)) { + Vec3d pstart = sidehead.junction_point(); + // Vec3d pend = Vec3d{pstart(X), pstart(Y), gndlvl}; + // Could not find a pillar, create one + create_ground_pillar(pstart, sidehead.dir, pradius, sidehead.id); + } + } + } +} + +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) + { + 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. + + 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; + } + + // We have failed to route this head. + BOOST_LOG_TRIVIAL(warning) + << "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() +{ + // Now comes the algorithm that connects pillars with each other. + // Ideally every pillar should be connected with at least one of its + // neighbors if that neighbor is within max_pillar_link_distance + + // Pillars with height exceeding H1 will require at least one neighbor + // to connect with. Height exceeding H2 require two neighbors. + double H1 = m_cfg.max_solo_pillar_height_mm; + double H2 = m_cfg.max_dual_pillar_height_mm; + double d = m_cfg.max_pillar_link_distance_mm; + + //A connection between two pillars only counts if the height ratio is + // bigger than 50% + double min_height_ratio = 0.5; + + std::set pairs; + + // A function to connect one pillar with its neighbors. THe number of + // neighbors is given in the configuration. This function if called + // for every pillar in the pillar index. A pair of pillar will not + // be connected multiple times this is ensured by the 'pairs' set which + // remembers the processed pillar pairs + auto cascadefn = + [this, d, &pairs, min_height_ratio, H1] (const PointIndexEl& el) + { + Vec3d qp = el.first; // endpoint of the pillar + + const Pillar& pillar = m_builder.pillar(el.second); // actual pillar + + // Get the max number of neighbors a pillar should connect to + unsigned neighbors = m_cfg.pillar_cascade_neighbors; + + // connections are already enough for the pillar + if(pillar.links >= neighbors) return; + + // Query all remaining points within reach + auto qres = m_pillar_index.query([qp, d](const PointIndexEl& e){ + return distance(e.first, qp) < d; + }); + + // sort the result by distance (have to check if this is needed) + std::sort(qres.begin(), qres.end(), + [qp](const PointIndexEl& e1, const PointIndexEl& e2){ + return distance(e1.first, qp) < distance(e2.first, qp); + }); + + for(auto& re : qres) { // process the queried neighbors + + if(re.second == el.second) continue; // Skip self + + auto a = el.second, b = re.second; + + // Get unique hash for the given pair (order doesn't matter) + auto hashval = pairhash(a, b); + + // Search for the pair amongst the remembered pairs + if(pairs.find(hashval) != pairs.end()) continue; + + const Pillar& neighborpillar = m_builder.pillar(re.second); + + // this neighbor is occupied, skip + if(neighborpillar.links >= neighbors) continue; + + if(interconnect(pillar, neighborpillar)) { + pairs.insert(hashval); + + // If the interconnection length between the two pillars is + // less than 50% of the longer pillar's height, don't count + if(pillar.height < H1 || + neighborpillar.height / pillar.height > min_height_ratio) + m_builder.increment_links(pillar); + + if(neighborpillar.height < H1 || + pillar.height / neighborpillar.height > min_height_ratio) + m_builder.increment_links(neighborpillar); + + } + + // connections are enough for one pillar + if(pillar.links >= neighbors) break; + } + }; + + // Run the cascade for the pillars in the index + m_pillar_index.foreach(cascadefn); + + // We would be done here if we could allow some pillars to not be + // connected with any neighbors. But this might leave the support tree + // unprintable. + // + // The current solution is to insert additional pillars next to these + // lonely pillars. One or even two additional pillar might get inserted + // depending on the length of the lonely pillar. + + size_t pillarcount = m_builder.pillarcount(); + + // Again, go through all pillars, this time in the whole support tree + // not just the index. + for(size_t pid = 0; pid < pillarcount; pid++) { + auto pillar = [this, pid]() { return m_builder.pillar(pid); }; + + // Decide how many additional pillars will be needed: + + unsigned needpillars = 0; + if (pillar().bridges > m_cfg.max_bridges_on_pillar) + needpillars = 3; + else if (pillar().links < 2 && pillar().height > H2) { + // Not enough neighbors to support this pillar + needpillars = 2; + } else if (pillar().links < 1 && pillar().height > H1) { + // No neighbors could be found and the pillar is too long. + needpillars = 1; + } + + needpillars = std::max(pillar().links, needpillars) - pillar().links; + if (needpillars == 0) continue; + + // Search for new pillar locations: + + bool found = false; + double alpha = 0; // goes to 2Pi + double r = 2 * m_cfg.base_radius_mm; + Vec3d pillarsp = pillar().startpoint(); + + // temp value for starting point detection + Vec3d sp(pillarsp(X), pillarsp(Y), pillarsp(Z) - r); + + // A vector of bool for placement feasbility + std::vector canplace(needpillars, false); + std::vector spts(needpillars); // vector of starting points + + double gnd = m_builder.ground_level; + double min_dist = m_cfg.pillar_base_safety_distance_mm + + m_cfg.base_radius_mm + EPSILON; + + while(!found && alpha < 2*PI) { + for (unsigned n = 0; + n < needpillars && (!n || canplace[n - 1]); + n++) + { + double a = alpha + n * PI / 3; + Vec3d s = sp; + s(X) += std::cos(a) * r; + s(Y) += std::sin(a) * r; + spts[n] = s; + + // Check the path vertically down + auto hr = bridge_mesh_intersect(s, {0, 0, -1}, pillar().r); + Vec3d gndsp{s(X), s(Y), gnd}; + + // If the path is clear, check for pillar base collisions + canplace[n] = std::isinf(hr.distance()) && + std::sqrt(m_mesh.squared_distance(gndsp)) > + min_dist; + } + + found = std::all_of(canplace.begin(), canplace.end(), + [](bool v) { return v; }); + + // 20 angles will be tried... + alpha += 0.1 * PI; + } + + std::vector newpills; + newpills.reserve(needpillars); + + if (found) + for (unsigned n = 0; n < needpillars; n++) { + Vec3d s = spts[n]; + Pillar p(s, Vec3d(s(X), s(Y), gnd), pillar().r); + p.add_base(m_cfg.base_height_mm, m_cfg.base_radius_mm); + + if (interconnect(pillar(), p)) { + Pillar &pp = m_builder.pillar(m_builder.add_pillar(p)); + 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); + if (distance(pillarsp, s) < t) + m_builder.add_bridge(pillarsp, s, pillar().r); + + if (pillar().endpoint()(Z) > m_builder.ground_level) + m_builder.add_junction(pillar().endpoint(), + pillar().r); + + newpills.emplace_back(pp.id); + m_builder.increment_links(pillar()); + m_builder.increment_links(pp); + } + } + + if(!newpills.empty()) { + for(auto it = newpills.begin(), nx = std::next(it); + nx != newpills.end(); ++it, ++nx) { + const Pillar& itpll = m_builder.pillar(*it); + const Pillar& nxpll = m_builder.pillar(*nx); + if(interconnect(itpll, nxpll)) { + m_builder.increment_links(itpll); + m_builder.increment_links(nxpll); + } + } + + m_pillar_index.foreach(cascadefn); + } + } +} + +void SupportTreeBuildsteps::routing_headless() +{ + // For now we will just generate smaller headless sticks with a sharp + // ending point that connects to the mesh surface. + + // We will sink the pins into the model surface for a distance of 1/3 of + // the pin radius + for(unsigned i : m_iheadless) { + m_thr(); + + const auto R = double(m_support_pts[i].head_front_radius); + const double HWIDTH_MM = m_cfg.head_penetration_mm; + + // Exact support position + Vec3d sph = m_support_pts[i].pos.cast(); + 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 dist = realdist; + + if (std::isinf(dist)) dist = sph(Z) - m_builder.ground_level; + + if(std::isnan(idist) || idist < 2*R || std::isnan(dist) || dist < 2*R) { + BOOST_LOG_TRIVIAL(warning) << "Can not find route for headless" + << " support stick at: " + << sj.transpose(); + continue; + } + + bool use_endball = !std::isinf(realdist); + Vec3d ej = sj + (dist + HWIDTH_MM) * dir; + m_builder.add_compact_bridge(sp, ej, n, R, use_endball); + } +} + +} +} diff --git a/src/libslic3r/SLA/SLASupportTreeBuildsteps.hpp b/src/libslic3r/SLA/SLASupportTreeBuildsteps.hpp new file mode 100644 index 000000000..e953cc136 --- /dev/null +++ b/src/libslic3r/SLA/SLASupportTreeBuildsteps.hpp @@ -0,0 +1,289 @@ +#ifndef SLASUPPORTTREEALGORITHM_H +#define SLASUPPORTTREEALGORITHM_H + +#include + +#include "SLASupportTreeBuilder.hpp" + +namespace Slic3r { +namespace sla { + +// The minimum distance for two support points to remain valid. +const double /*constexpr*/ D_SP = 0.1; + +enum { // For indexing Eigen vectors as v(X), v(Y), v(Z) instead of numbers + X, Y, Z +}; + +inline Vec2d to_vec2(const Vec3d& v3) { + return {v3(X), v3(Y)}; +} + +// This function returns the position of the centroid in the input 'clust' +// vector of point indices. +template +long cluster_centroid(const ClusterEl& clust, + const std::function &pointfn, + DistFn df) +{ + switch(clust.size()) { + case 0: /* empty cluster */ return ID_UNSET; + case 1: /* only one element */ return 0; + case 2: /* if two elements, there is no center */ return 0; + default: ; + } + + // The function works by calculating for each point the average distance + // from all the other points in the cluster. We create a selector bitmask of + // the same size as the cluster. The bitmask will have two true bits and + // false bits for the rest of items and we will loop through all the + // permutations of the bitmask (combinations of two points). Get the + // distance for the two points and add the distance to the averages. + // The point with the smallest average than wins. + + // The complexity should be O(n^2) but we will mostly apply this function + // for small clusters only (cca 3 elements) + + std::vector sel(clust.size(), false); // create full zero bitmask + std::fill(sel.end() - 2, sel.end(), true); // insert the two ones + std::vector avgs(clust.size(), 0.0); // store the average distances + + do { + std::array idx; + for(size_t i = 0, j = 0; i < clust.size(); i++) if(sel[i]) idx[j++] = i; + + double d = df(pointfn(clust[idx[0]]), + pointfn(clust[idx[1]])); + + // add the distance to the sums for both associated points + for(auto i : idx) avgs[i] += d; + + // now continue with the next permutation of the bitmask with two 1s + } while(std::next_permutation(sel.begin(), sel.end())); + + // Divide by point size in the cluster to get the average (may be redundant) + for(auto& a : avgs) a /= clust.size(); + + // get the lowest average distance and return the index + auto minit = std::min_element(avgs.begin(), avgs.end()); + return long(minit - avgs.begin()); +} + +inline Vec3d dirv(const Vec3d& startp, const Vec3d& endp) { + return (endp - startp).normalized(); +} + +class PillarIndex { + PointIndex m_index; + using Mutex = ccr::BlockingMutex; + mutable Mutex m_mutex; + +public: + + template inline void guarded_insert(Args&&...args) + { + std::lock_guard lck(m_mutex); + m_index.insert(std::forward(args)...); + } + + template + inline std::vector guarded_query(Args&&...args) const + { + std::lock_guard lck(m_mutex); + return m_index.query(std::forward(args)...); + } + + template inline void insert(Args&&...args) + { + m_index.insert(std::forward(args)...); + } + + template + inline std::vector query(Args&&...args) const + { + return m_index.query(std::forward(args)...); + } + + template inline void foreach(Fn fn) { m_index.foreach(fn); } + template inline void guarded_foreach(Fn fn) + { + std::lock_guard lck(m_mutex); + m_index.foreach(fn); + } + + PointIndex guarded_clone() + { + std::lock_guard lck(m_mutex); + return m_index; + } +}; + +// Helper function for pillar interconnection where pairs of already connected +// pillars should be checked for not to be processed again. This can be done +// in constant time with a set of hash values uniquely representing a pair of +// integers. The order of numbers within the pair should not matter, it has +// the same unique hash. The hash value has to have twice as many bits as the +// arguments need. If the same integral type is used for args and return val, +// make sure the arguments use only the half of the type's bit depth. +template> +IntegerOnly pairhash(I a, I b) +{ + using std::ceil; using std::log2; using std::max; using std::min; + static const auto constexpr Ibits = int(sizeof(I) * CHAR_BIT); + static const auto constexpr DoubleIbits = int(sizeof(DoubleI) * CHAR_BIT); + static const auto constexpr shift = DoubleIbits / 2 < Ibits ? Ibits / 2 : Ibits; + + I g = min(a, b), l = max(a, b); + + // Assume the hash will fit into the output variable + assert((g ? (ceil(log2(g))) : 0) <= shift); + assert((l ? (ceil(log2(l))) : 0) <= shift); + + return (DoubleI(g) << shift) + l; +} + +class SupportTreeBuildsteps { + const SupportConfig& m_cfg; + const EigenMesh3D& m_mesh; + const std::vector& m_support_pts; + + using PtIndices = std::vector; + + PtIndices m_iheads; // support points with pinhead + PtIndices m_iheadless; // headless support points + + // supp. pts. connecting to model: point index and the ray hit data + std::vector> m_iheads_onmodel; + + // normals for support points from model faces. + PointSet m_support_nmls; + + // Clusters of points which can reach the ground directly and can be + // bridged to one central pillar + std::vector m_pillar_clusters; + + // This algorithm uses the SupportTreeBuilder class to fill gradually + // the support elements (heads, pillars, bridges, ...) + SupportTreeBuilder& m_builder; + + // support points in Eigen/IGL format + PointSet m_points; + + // throw if canceled: It will be called many times so a shorthand will + // come in handy. + ThrowOnCancel m_thr; + + // A spatial index to easily find strong pillars to connect to. + PillarIndex m_pillar_index; + + // 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) + { + return m_mesh.query_ray_hit(s, dir).distance(); + } + + // This function will test if a future pinhead would not collide with the + // model geometry. It does not take a 'Head' object because those are + // created after this test. Parameters: s: The touching point on the model + // surface. dir: This is the direction of the head from the pin to the back + // r_pin, r_back: the radiuses of the pin and the back sphere width: This + // is the full width from the pin center to the back center m: The object + // mesh. + // The return value is the hit result from the ray casting. If the starting + // point was inside the model, an "invalid" hit_result will be returned + // with a zero distance value instead of a NAN. This way the result can + // be used safely for comparison with other distances. + EigenMesh3D::hit_result pinhead_mesh_intersect( + const Vec3d& s, + const Vec3d& dir, + double r_pin, + double r_back, + double width); + + // Checking bridge (pillar and stick as well) intersection with the model. + // If the function is used for headless sticks, the ins_check parameter + // have to be true as the beginning of the stick might be inside the model + // geometry. + // The return value is the hit result from the ray casting. If the starting + // point was inside the model, an "invalid" hit_result will be returned + // with a zero distance value instead of a NAN. This way the result can + // be used safely for comparison with other distances. + EigenMesh3D::hit_result bridge_mesh_intersect( + const Vec3d& s, + const Vec3d& dir, + double r, + bool ins_check = false); + + // 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); + + 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. + void create_ground_pillar(const Vec3d &jp, + const Vec3d &sourcedir, + double radius, + long head_id = ID_UNSET); +public: + SupportTreeBuildsteps(SupportTreeBuilder & builder, const SupportableMesh &sm); + + // Now let's define the individual steps of the support generation algorithm + + // Filtering step: here we will discard inappropriate support points + // and decide the future of the appropriate ones. We will check if a + // pinhead is applicable and adjust its angle at each support point. We + // will also merge the support points that are just too close and can + // be considered as one. + void filter(); + + // Pinhead creation: based on the filtering results, the Head objects + // will be constructed (together with their triangle meshes). + void add_pinheads(); + + // Further classification of the support points with pinheads. If the + // ground is directly reachable through a vertical line parallel to the + // Z axis we consider a support point as pillar candidate. If touches + // the model geometry, it will be marked as non-ground facing and + // further steps will process it. Also, the pillars will be grouped + // into clusters that can be interconnected with bridges. Elements of + // these groups may or may not be interconnected. Here we only run the + // clustering algorithm. + void classify(); + + // Step: Routing the ground connected pinheads, and interconnecting + // them with additional (angled) bridges. Not all of these pinheads + // will be a full pillar (ground connected). Some will connect to a + // nearby pillar using a bridge. The max number of such side-heads for + // a central pillar is limited to avoid bad weight distribution. + void routing_to_ground(); + + // Step: routing the pinheads that would connect to the model surface + // along the Z axis downwards. For now these will actually be connected with + // the model surface with a flipped pinhead. In the future here we could use + // some smart algorithms to search for a safe path to the ground or to a + // nearby pillar that can hold the supported weight. + void routing_to_model(); + + void interconnect_pillars(); + + // Step: process the support points where there is not enough space for a + // full pinhead. In this case we will use a rounded sphere as a touching + // point and use a thinner bridge (let's call it a stick). + void routing_headless (); + + inline void merge_result() { m_builder.merged_mesh(); } + + static bool execute(SupportTreeBuilder & builder, const SupportableMesh &sm); +}; + +} +} + +#endif // SLASUPPORTTREEALGORITHM_H diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index a5aede210..072060271 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -77,7 +77,7 @@ bool PointIndex::remove(const PointIndexEl& el) } std::vector -PointIndex::query(std::function fn) +PointIndex::query(std::function fn) const { namespace bgi = boost::geometry::index; @@ -86,7 +86,7 @@ PointIndex::query(std::function fn) return ret; } -std::vector PointIndex::nearest(const Vec3d &el, unsigned k = 1) +std::vector PointIndex::nearest(const Vec3d &el, unsigned k = 1) const { namespace bgi = boost::geometry::index; std::vector ret; ret.reserve(k); @@ -104,6 +104,11 @@ 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 * ************************************************************************** */ @@ -274,6 +279,8 @@ double EigenMesh3D::squared_distance(const Vec3d &p, int& i, Vec3d& c) const { * Misc functions * ****************************************************************************/ +namespace { + bool point_on_edge(const Vec3d& p, const Vec3d& e1, const Vec3d& e2, double eps = 0.05) { @@ -289,11 +296,13 @@ template double distance(const Vec& pp1, const Vec& pp2) { 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 = {}) + const std::vector& pt_indices) { if(points.rows() == 0 || mesh.V().rows() == 0 || mesh.F().rows() == 0) return {}; @@ -419,9 +428,17 @@ PointSet normals(const PointSet& points, 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( @@ -433,25 +450,22 @@ ClusteredPoints cluster(Index3D &sindex, // each other std::function group = [&sindex, &group, max_points, qfn](Elems& pts, Elems& cluster) - { + { for(auto& p : pts) { std::vector tmp = qfn(sindex, p); - auto cmp = [](const PointIndexEl& e1, const PointIndexEl& e2){ - return e1.second < e2.second; - }; - - std::sort(tmp.begin(), tmp.end(), cmp); + + 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); + 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); + std::sort(cluster.begin(), cluster.end(), cmp_ptidx_elements); if(!newpts.empty() && (!max_points || cluster.size() < max_points)) group(newpts, cluster); @@ -479,7 +493,6 @@ ClusteredPoints cluster(Index3D &sindex, return result; } -namespace { std::vector distance_queryfn(const Index3D& sindex, const PointIndexEl& p, double dist, @@ -496,7 +509,8 @@ std::vector distance_queryfn(const Index3D& sindex, return tmp; } -} + +} // namespace // Clustering a set of points by the given criteria ClusteredPoints cluster( @@ -558,5 +572,5 @@ ClusteredPoints cluster(const PointSet& pts, double dist, unsigned max_points) }); } -} -} +} // namespace sla +} // namespace Slic3r diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index e67cbb4a8..22192d814 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1,6 +1,6 @@ #include "SLAPrint.hpp" #include "SLA/SLASupportTree.hpp" -#include "SLA/SLABasePool.hpp" +#include "SLA/SLAPad.hpp" #include "SLA/SLAAutoSupports.hpp" #include "ClipperUtils.hpp" #include "Geometry.hpp" @@ -32,17 +32,19 @@ namespace Slic3r { -using SupportTreePtr = std::unique_ptr; - -class SLAPrintObject::SupportData +class SLAPrintObject::SupportData : public sla::SupportableMesh { public: - sla::EigenMesh3D emesh; // index-triangle representation - std::vector support_points; // all the support points (manual/auto) - SupportTreePtr support_tree_ptr; // the supports + sla::SupportTree::UPtr support_tree_ptr; // the supports std::vector support_slices; // sliced supports - - inline SupportData(const TriangleMesh &trmesh) : emesh(trmesh) {} + + 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; + } }; namespace { @@ -53,7 +55,7 @@ const std::array OBJ_STEP_LEVELS = 30, // slaposObjectSlice, 20, // slaposSupportPoints, 10, // slaposSupportTree, - 10, // slaposBasePool, + 10, // slaposPad, 30, // slaposSliceSupports, }; @@ -64,7 +66,7 @@ std::string OBJ_STEP_LABELS(size_t idx) case slaposObjectSlice: return L("Slicing model"); case slaposSupportPoints: return L("Generating support points"); case slaposSupportTree: return L("Generating support tree"); - case slaposBasePool: return L("Generating pad"); + case slaposPad: return L("Generating pad"); case slaposSliceSupports: return L("Slicing supports"); default:; } @@ -583,7 +585,8 @@ bool is_zero_elevation(const SLAPrintObjectConfig &c) { // 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(); @@ -612,12 +615,13 @@ sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) { return scfg; } -sla::PoolConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c) { - sla::PoolConfig::EmbedObject ret; +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(); @@ -628,24 +632,29 @@ sla::PoolConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c) { return ret; } -sla::PoolConfig make_pool_config(const SLAPrintObjectConfig& c) { - sla::PoolConfig pcfg; +sla::PadConfig make_pad_cfg(const SLAPrintObjectConfig& c) { + sla::PadConfig pcfg; - pcfg.min_wall_thickness_mm = c.pad_wall_thickness.getFloat(); + pcfg.wall_thickness_mm = c.pad_wall_thickness.getFloat(); pcfg.wall_slope = c.pad_wall_slope.getFloat() * PI / 180.0; - // We do not support radius for now - pcfg.edge_radius_mm = 0.0; //c.pad_edge_radius.getFloat(); + 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(); - pcfg.max_merge_distance_mm = c.pad_max_merge_distance.getFloat(); - pcfg.min_wall_height_mm = c.pad_wall_height.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 @@ -663,17 +672,12 @@ std::string SLAPrint::validate() const sla::SupportConfig cfg = make_support_cfg(po->config()); - double pinhead_width = - 2 * cfg.head_front_radius_mm + - cfg.head_width_mm + - 2 * cfg.head_back_radius_mm - - cfg.head_penetration_mm; - double elv = cfg.object_elevation_mm; - - sla::PoolConfig::EmbedObject builtinpad = builtin_pad_cfg(po->config()); - if(supports_en && !builtinpad.enabled && elv < pinhead_width ) + sla::PadConfig padcfg = make_pad_cfg(po->config()); + sla::PadConfig::EmbedObject &builtinpad = padcfg.embed_object; + + if(supports_en && !builtinpad.enabled && elv < cfg.head_fullwidth()) return L( "Elevation is too low for object. Use the \"Pad around " "object\" feature to print the object without elevation."); @@ -686,6 +690,9 @@ std::string SLAPrint::validate() const "distance' has to be greater than the 'Pad object gap' " "parameter to avoid this."); } + + std::string pval = padcfg.validate(); + if (!pval.empty()) return pval; } double expt_max = m_printer_config.max_exposure_time.getFloat(); @@ -876,8 +883,7 @@ void SLAPrint::process() // Construction of this object does the calculation. this->throw_if_canceled(); - SLAAutoSupports auto_supports(po.transformed_mesh(), - po.m_supportdata->emesh, + SLAAutoSupports auto_supports(po.m_supportdata->emesh, po.get_model_slices(), heights, config, @@ -887,10 +893,10 @@ void SLAPrint::process() // Now let's extract the result. const std::vector& points = auto_supports.output(); this->throw_if_canceled(); - po.m_supportdata->support_points = points; + po.m_supportdata->pts = points; BOOST_LOG_TRIVIAL(debug) << "Automatic support points: " - << po.m_supportdata->support_points.size(); + << po.m_supportdata->pts.size(); // Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass // the update status to GLGizmoSlaSupports @@ -902,29 +908,19 @@ void SLAPrint::process() 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->support_points = po.transformed_support_points(); + 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 gnd = po.m_supportdata->emesh.ground_level(); - auto & pts = po.m_supportdata->support_points; double tolerance = po.config().pad_enable.getBool() ? po.m_config.pad_wall_thickness.getFloat() : po.m_config.support_base_height.getFloat(); - // get iterator to the reorganized vector end - auto endit = std::remove_if( - pts.begin(), - pts.end(), - [tolerance, gnd](const sla::SupportPoint &sp) { - double diff = std::abs(gnd - double(sp.pos(Z))); - return diff <= tolerance; - }); - - // erase all elements after the new end - pts.erase(endit, pts.end()); + remove_bottom_points(po.m_supportdata->pts, + po.m_supportdata->emesh.ground_level(), + tolerance); } }; @@ -933,45 +929,31 @@ void SLAPrint::process() { if(!po.m_supportdata) return; - sla::PoolConfig pcfg = make_pool_config(po.m_config); + sla::PadConfig pcfg = make_pad_cfg(po.m_config); if (pcfg.embed_object) - po.m_supportdata->emesh.ground_level_offset( - pcfg.min_wall_thickness_mm); - - if(!po.m_config.supports_enable.getBool()) { - - // Generate empty support tree. It can still host a pad - po.m_supportdata->support_tree_ptr.reset( - new SLASupportTree(po.m_supportdata->emesh.ground_level())); - - return; - } - - sla::SupportConfig scfg = make_support_cfg(po.m_config); - sla::Controller ctl; - + 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) - { + 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)) + if (std::round(m_report_status.status()) < std::round(current)) m_report_status(*this, current, OBJ_STEP_LABELS(slaposSupportTree), - SlicingStatus::DEFAULT, - logmsg); - + SlicingStatus::DEFAULT, logmsg); }; - - ctl.stopcondition = [this](){ return canceled(); }; + ctl.stopcondition = [this]() { return canceled(); }; ctl.cancelfn = [this]() { throw_if_canceled(); }; - po.m_supportdata->support_tree_ptr.reset( - new SLASupportTree(po.m_supportdata->support_points, - po.m_supportdata->emesh, scfg, ctl)); + po.m_supportdata->create_support_tree(ctl); + + if (!po.m_config.supports_enable.getBool()) return; throw_if_canceled(); @@ -980,10 +962,9 @@ void SLAPrint::process() // This is to prevent "Done." being displayed during merged_mesh() m_report_status(*this, -1, L("Visualizing supports")); - po.m_supportdata->support_tree_ptr->merged_mesh(); BOOST_LOG_TRIVIAL(debug) << "Processed support point count " - << po.m_supportdata->support_points.size(); + << po.m_supportdata->pts.size(); // Check the mesh for later troubleshooting. if(po.support_mesh().empty()) @@ -993,7 +974,7 @@ void SLAPrint::process() }; // This step generates the sla base pad - auto base_pool = [this](SLAPrintObject& po) { + 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) @@ -1001,10 +982,10 @@ void SLAPrint::process() if(po.m_config.pad_enable.getBool()) { // Get the distilled pad configuration from the config - sla::PoolConfig pcfg = make_pool_config(po.m_config); + sla::PadConfig pcfg = make_pad_cfg(po.m_config); ExPolygons bp; // This will store the base plate of the pad. - double pad_h = sla::get_pad_fullheight(pcfg); + double pad_h = pcfg.full_height(); const TriangleMesh &trmesh = po.transformed_mesh(); // This call can get pretty time consuming @@ -1015,15 +996,19 @@ void SLAPrint::process() // 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::base_plate(trmesh, - bp, - float(pad_h), - float(po.m_config.layer_height.getFloat()), - thrfn); + sla::pad_blueprint(trmesh, bp, float(pad_h), + float(po.m_config.layer_height.getFloat()), + thrfn); } - pcfg.throw_on_cancel = 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(); } @@ -1191,9 +1176,8 @@ void SLAPrint::process() { ClipperPolygon poly; - // We need to reverse if flpXY OR is_lefthanded is true but - // not if both are true which is a logical inequality (XOR) - bool needreverse = /*flpXY !=*/ is_lefthanded; + // 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); @@ -1388,7 +1372,12 @@ void SLAPrint::process() 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 @@ -1396,7 +1385,7 @@ void SLAPrint::process() if(canceled()) return; // Set up the printer, allocate space for all the layers - sla::SLARasterWriter &printer = init_printer(); + sla::RasterWriter &printer = init_printer(); auto lvlcnt = unsigned(m_printer_input.size()); printer.layers(lvlcnt); @@ -1416,11 +1405,9 @@ void SLAPrint::process() SpinMutex slck; - auto orientation = get_printer_orientation(); - // procedure to process one height level. This will run in parallel auto lvlfn = - [this, &slck, &printer, increment, &dstatus, &pst, orientation] + [this, &slck, &printer, increment, &dstatus, &pst] (unsigned level_id) { if(canceled()) return; @@ -1431,7 +1418,7 @@ void SLAPrint::process() printer.begin_layer(level_id); for(const ClipperLib::Polygon& poly : printlayer.transformed_slices()) - printer.draw_polygon(poly, level_id, orientation); + printer.draw_polygon(poly, level_id); // Finish the layer for later saving it. printer.finish_layer(level_id); @@ -1459,7 +1446,7 @@ void SLAPrint::process() tbb::parallel_for(0, lvlcnt, lvlfn); // Set statistics values to the printer - sla::SLARasterWriter::PrintStatistics stats; + sla::RasterWriter::PrintStatistics stats; stats.used_material = (m_print_statistics.objects_used_material + m_print_statistics.support_used_material) / 1000; @@ -1478,12 +1465,12 @@ void SLAPrint::process() slaposFn pobj_program[] = { - slice_model, support_points, support_tree, base_pool, slice_supports + slice_model, support_points, support_tree, generate_pad, slice_supports }; // We want to first process all objects... std::vector level1_obj_steps = { - slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposBasePool + slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad }; // and then slice all supports to allow preview to be displayed ASAP @@ -1620,7 +1607,11 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector steps; @@ -1654,12 +1645,11 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector mirror; - double gamma; double w = m_printer_config.display_width.getFloat(); double h = m_printer_config.display_height.getFloat(); @@ -1669,20 +1659,18 @@ sla::SLARasterWriter & SLAPrint::init_printer() mirror[X] = m_printer_config.display_mirror_x.getBool(); mirror[Y] = m_printer_config.display_mirror_y.getBool(); - if (get_printer_orientation() == sla::SLARasterWriter::roPortrait) { + auto orientation = get_printer_orientation(); + if (orientation == sla::Raster::roPortrait) { std::swap(w, h); std::swap(pw, ph); - - // XY flipping implicitly does an X mirror - mirror[X] = !mirror[X]; } res = sla::Raster::Resolution{pw, ph}; pxdim = sla::Raster::PixelDim{w / pw, h / ph}; - - gamma = m_printer_config.gamma_correction.getFloat(); - - m_printer.reset(new sla::SLARasterWriter(res, pxdim, mirror, gamma)); + sla::Raster::Trafo tr{orientation, mirror}; + tr.gamma = m_printer_config.gamma_correction.getFloat(); + + m_printer.reset(new sla::RasterWriter(res, pxdim, tr)); m_printer->set_config(m_full_print_config); return *m_printer; } @@ -1730,6 +1718,7 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vectorinvalidate_all_steps(); } else if (step == slaposSupportPoints) { - invalidated |= this->invalidate_steps({ slaposSupportTree, slaposBasePool, slaposSliceSupports }); + invalidated |= this->invalidate_steps({ slaposSupportTree, slaposPad, slaposSliceSupports }); invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval); } else if (step == slaposSupportTree) { - invalidated |= this->invalidate_steps({ slaposBasePool, slaposSliceSupports }); + invalidated |= this->invalidate_steps({ slaposPad, slaposSliceSupports }); invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval); - } else if (step == slaposBasePool) { + } else if (step == slaposPad) { invalidated |= this->invalidate_steps({slaposSliceSupports}); invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval); } else if (step == slaposSliceSupports) { @@ -1813,8 +1803,8 @@ double SLAPrintObject::get_elevation() const { // its walls but currently it is half of its thickness. Whatever it // will be in the future, we provide the config to the get_pad_elevation // method and we will have the correct value - sla::PoolConfig pcfg = make_pool_config(m_config); - if(!pcfg.embed_object) ret += sla::get_pad_elevation(pcfg); + sla::PadConfig pcfg = make_pad_cfg(m_config); + if(!pcfg.embed_object) ret += pcfg.required_elevation(); } return ret; @@ -1825,7 +1815,7 @@ double SLAPrintObject::get_current_elevation() const if (is_zero_elevation(m_config)) return 0.; bool has_supports = is_step_done(slaposSupportTree); - bool has_pad = is_step_done(slaposBasePool); + bool has_pad = is_step_done(slaposPad); if(!has_supports && !has_pad) return 0; @@ -1866,7 +1856,7 @@ const SliceRecord SliceRecord::EMPTY(0, std::nanf(""), 0.f); const std::vector& SLAPrintObject::get_support_points() const { - return m_supportdata? m_supportdata->support_points : EMPTY_SUPPORT_POINTS; + return m_supportdata? m_supportdata->pts : EMPTY_SUPPORT_POINTS; } const std::vector &SLAPrintObject::get_support_slices() const @@ -1896,7 +1886,7 @@ bool SLAPrintObject::has_mesh(SLAPrintObjectStep step) const switch (step) { case slaposSupportTree: return ! this->support_mesh().empty(); - case slaposBasePool: + case slaposPad: return ! this->pad_mesh().empty(); default: return false; @@ -1908,7 +1898,7 @@ TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const switch (step) { case slaposSupportTree: return this->support_mesh(); - case slaposBasePool: + case slaposPad: return this->pad_mesh(); default: return TriangleMesh(); @@ -1917,18 +1907,20 @@ TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const const TriangleMesh& SLAPrintObject::support_mesh() const { - if(m_config.supports_enable.getBool() && m_supportdata && - m_supportdata->support_tree_ptr) { - return m_supportdata->support_tree_ptr->merged_mesh(); - } - + sla::SupportTree::UPtr &stree = m_supportdata->support_tree_ptr; + + if(m_config.supports_enable.getBool() && m_supportdata && stree) + return stree->retrieve_mesh(sla::MeshType::Support); + return EMPTY_MESH; } const TriangleMesh& SLAPrintObject::pad_mesh() const { - if(m_config.pad_enable.getBool() && m_supportdata && m_supportdata->support_tree_ptr) - return m_supportdata->support_tree_ptr->get_pad(); + sla::SupportTree::UPtr &stree = m_supportdata->support_tree_ptr; + + if(m_config.pad_enable.getBool() && m_supportdata && stree) + return stree->retrieve_mesh(sla::MeshType::Pad); return EMPTY_MESH; } diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index a2bc1325a..8f386d407 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -7,6 +7,7 @@ #include "SLA/SLARasterWriter.hpp" #include "Point.hpp" #include "MTUtils.hpp" +#include "Zipper.hpp" #include namespace Slic3r { @@ -21,7 +22,7 @@ enum SLAPrintObjectStep : unsigned int { slaposObjectSlice, slaposSupportPoints, slaposSupportTree, - slaposBasePool, + slaposPad, slaposSliceSupports, slaposCount }; @@ -54,7 +55,7 @@ public: bool is_left_handed() const { return m_left_handed; } struct Instance { - Instance(ObjectID instance_id, const Point &shift, float rotation) : instance_id(instance_id), shift(shift), rotation(rotation) {} + Instance(ObjectID inst_id, const Point &shft, float rot) : instance_id(inst_id), shift(shft), rotation(rot) {} bool operator==(const Instance &rhs) const { return this->instance_id == rhs.instance_id && this->shift == rhs.shift && this->rotation == rhs.rotation; } // ID of the corresponding ModelInstance. ObjectID instance_id; @@ -364,6 +365,12 @@ public: if(m_printer) m_printer->save(fpath, projectname); } + inline void export_raster(Zipper &zipper, + const std::string& projectname = "") + { + if(m_printer) m_printer->save(zipper, projectname); + } + const PrintObjects& objects() const { return m_objects; } const SLAPrintConfig& print_config() const { return m_print_config; } @@ -440,7 +447,7 @@ private: std::vector m_printer_input; // The printer itself - std::unique_ptr m_printer; + std::unique_ptr m_printer; // Estimated print time, material consumed. SLAPrintStatistics m_print_statistics; @@ -459,14 +466,13 @@ private: double status() const { return m_st; } } m_report_status; - sla::SLARasterWriter &init_printer(); + sla::RasterWriter &init_printer(); - inline sla::SLARasterWriter::Orientation get_printer_orientation() const + inline sla::Raster::Orientation get_printer_orientation() const { auto ro = m_printer_config.display_orientation.getInt(); - return ro == sla::SLARasterWriter::roPortrait ? - sla::SLARasterWriter::roPortrait : - sla::SLARasterWriter::roLandscape; + return ro == sla::Raster::roPortrait ? sla::Raster::roPortrait : + sla::Raster::roLandscape; } friend SLAPrintObject; diff --git a/src/libslic3r/SVG.cpp b/src/libslic3r/SVG.cpp index 03f55802e..6e4b973ea 100644 --- a/src/libslic3r/SVG.cpp +++ b/src/libslic3r/SVG.cpp @@ -368,6 +368,10 @@ void SVG::export_expolygons(const char *path, const std::vector 0) + for (const ExPolygon &expoly : exp_with_attr.first) + svg.draw((Points)expoly, exp_with_attr.second.color_points, exp_with_attr.second.radius_points); svg.Close(); } diff --git a/src/libslic3r/SVG.hpp b/src/libslic3r/SVG.hpp index 3a5602196..c1b387554 100644 --- a/src/libslic3r/SVG.hpp +++ b/src/libslic3r/SVG.hpp @@ -105,19 +105,25 @@ public: const std::string &color_contour, const std::string &color_holes, const coord_t outline_width = scale_(0.05), - const float fill_opacity = 0.5f) : + const float fill_opacity = 0.5f, + const std::string &color_points = "black", + const coord_t radius_points = 0) : color_fill (color_fill), color_contour (color_contour), color_holes (color_holes), outline_width (outline_width), - fill_opacity (fill_opacity) + fill_opacity (fill_opacity), + color_points (color_points), + radius_points (radius_points) {} std::string color_fill; std::string color_contour; std::string color_holes; + std::string color_points; coord_t outline_width; float fill_opacity; + coord_t radius_points; }; static void export_expolygons(const char *path, const std::vector> &expolygons_with_attributes); diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp new file mode 100644 index 000000000..8b5cbf483 --- /dev/null +++ b/src/libslic3r/ShortestPath.cpp @@ -0,0 +1,1969 @@ +#if 0 + #pragma optimize("", off) + #undef NDEBUG + #undef assert +#endif + +#include "clipper.hpp" +#include "ShortestPath.hpp" +#include "KDTreeIndirect.hpp" +#include "MutablePriorityQueue.hpp" +#include "Print.hpp" + +#include +#include + +namespace Slic3r { + +// Naive implementation of the Traveling Salesman Problem, it works by always taking the next closest neighbor. +// This implementation will always produce valid result even if some segments cannot reverse. +template +std::vector> chain_segments_closest_point(std::vector &end_points, KDTreeType &kdtree, CouldReverseFunc &could_reverse_func, EndPointType &first_point) +{ + assert((end_points.size() & 1) == 0); + size_t num_segments = end_points.size() / 2; + assert(num_segments >= 2); + for (EndPointType &ep : end_points) + ep.chain_id = 0; + std::vector> out; + out.reserve(num_segments); + size_t first_point_idx = &first_point - end_points.data(); + out.emplace_back(first_point_idx / 2, (first_point_idx & 1) != 0); + first_point.chain_id = 1; + size_t this_idx = first_point_idx ^ 1; + for (int iter = (int)num_segments - 2; iter >= 0; -- iter) { + EndPointType &this_point = end_points[this_idx]; + this_point.chain_id = 1; + // Find the closest point to this end_point, which lies on a different extrusion path (filtered by the lambda). + // Ignore the starting point as the starting point is considered to be occupied, no end point coud connect to it. + size_t next_idx = find_closest_point(kdtree, this_point.pos, + [this_idx, &end_points, &could_reverse_func](size_t idx) { + return (idx ^ this_idx) > 1 && end_points[idx].chain_id == 0 && ((idx ^ 1) == 0 || could_reverse_func(idx >> 1)); + }); + assert(next_idx < end_points.size()); + EndPointType &end_point = end_points[next_idx]; + end_point.chain_id = 1; + this_idx = next_idx ^ 1; + } +#ifndef NDEBUG + assert(end_points[this_idx].chain_id == 0); + for (EndPointType &ep : end_points) + assert(&ep == &end_points[this_idx] || ep.chain_id == 1); +#endif /* NDEBUG */ + return out; +} + +// Chain perimeters (always closed) and thin fills (closed or open) using a greedy algorithm. +// Solving a Traveling Salesman Problem (TSP) with the modification, that the sites are not always points, but points and segments. +// Solving using a greedy algorithm, where a shortest edge is added to the solution if it does not produce a bifurcation or a cycle. +// Return index and "reversed" flag. +// https://en.wikipedia.org/wiki/Multi-fragment_algorithm +// The algorithm builds a tour for the traveling salesman one edge at a time and thus maintains multiple tour fragments, each of which +// is a simple path in the complete graph of cities. At each stage, the algorithm selects the edge of minimal cost that either creates +// a new fragment, extends one of the existing paths or creates a cycle of length equal to the number of cities. +template +std::vector> chain_segments_greedy_constrained_reversals_(SegmentEndPointFunc end_point_func, CouldReverseFunc could_reverse_func, size_t num_segments, const PointType *start_near) +{ + std::vector> out; + + if (num_segments == 0) { + // Nothing to do. + } + else if (num_segments == 1) + { + // Just sort the end points so that the first point visited is closest to start_near. + out.emplace_back(0, start_near != nullptr && + (end_point_func(0, true) - *start_near).template cast().squaredNorm() < (end_point_func(0, false) - *start_near).template cast().squaredNorm()); + } + else + { + // End points of segments for the KD tree closest point search. + // A single end point is inserted into the search structure for loops, two end points are entered for open paths. + struct EndPoint { + EndPoint(const Vec2d &pos) : pos(pos) {} + Vec2d pos; + // Identifier of the chain, to which this end point belongs. Zero means unassigned. + size_t chain_id = 0; + // Link to the closest currently valid end point. + EndPoint *edge_out = nullptr; + // Distance to the next end point following the link. + // Zero value -> start of the final path. + double distance_out = std::numeric_limits::max(); + size_t heap_idx = std::numeric_limits::max(); + }; + std::vector end_points; + end_points.reserve(num_segments * 2); + for (size_t i = 0; i < num_segments; ++ i) { + end_points.emplace_back(end_point_func(i, true ).template cast()); + end_points.emplace_back(end_point_func(i, false).template cast()); + } + + // Construct the closest point KD tree over end points of segments. + auto coordinate_fn = [&end_points](size_t idx, size_t dimension) -> double { return end_points[idx].pos[dimension]; }; + KDTreeIndirect<2, double, decltype(coordinate_fn)> kdtree(coordinate_fn, end_points.size()); + + // Helper to detect loops in already connected paths. + // Unique chain IDs are assigned to paths. If paths are connected, end points will not have their chain IDs updated, but the chain IDs + // will remember an "equivalent" chain ID, which is the lowest ID of all the IDs in the path, and the lowest ID is equivalent to itself. + class EquivalentChains { + public: + // Zero'th chain ID is invalid. + EquivalentChains(size_t reserve) { m_equivalent_with.reserve(reserve); m_equivalent_with.emplace_back(0); } + // Generate next equivalence class. + size_t next() { + m_equivalent_with.emplace_back(++ m_last_chain_id); + return m_last_chain_id; + } + // Get equivalence class for chain ID. + size_t operator()(size_t chain_id) { + if (chain_id != 0) { + for (size_t last = chain_id;;) { + size_t lower = m_equivalent_with[last]; + if (lower == last) { + m_equivalent_with[chain_id] = lower; + chain_id = lower; + break; + } + last = lower; + } + } + return chain_id; + } + size_t merge(size_t chain_id1, size_t chain_id2) { + size_t chain_id = std::min((*this)(chain_id1), (*this)(chain_id2)); + m_equivalent_with[chain_id1] = chain_id; + m_equivalent_with[chain_id2] = chain_id; + return chain_id; + } + +#ifndef NDEBUG + bool validate() + { + assert(m_last_chain_id >= 0); + assert(m_last_chain_id + 1 == m_equivalent_with.size()); + for (size_t i = 0; i < m_equivalent_with.size(); ++ i) { + for (size_t last = i;;) { + size_t lower = m_equivalent_with[last]; + assert(lower <= last); + if (lower == last) + break; + last = lower; + } + } + return true; + } +#endif /* NDEBUG */ + + private: + // Unique chain ID assigned to chains of end points of segments. + size_t m_last_chain_id = 0; + std::vector m_equivalent_with; + } equivalent_chain(num_segments); + + // Find the first end point closest to start_near. + EndPoint *first_point = nullptr; + size_t first_point_idx = std::numeric_limits::max(); + if (start_near != nullptr) { + size_t idx = find_closest_point(kdtree, start_near->template cast()); + assert(idx < end_points.size()); + first_point = &end_points[idx]; + first_point->distance_out = 0.; + first_point->chain_id = equivalent_chain.next(); + first_point_idx = idx; + } + EndPoint *initial_point = first_point; + EndPoint *last_point = nullptr; + + // Assign the closest point and distance to the end points. + for (EndPoint &end_point : end_points) { + assert(end_point.edge_out == nullptr); + if (&end_point != first_point) { + size_t this_idx = &end_point - &end_points.front(); + // Find the closest point to this end_point, which lies on a different extrusion path (filtered by the lambda). + // Ignore the starting point as the starting point is considered to be occupied, no end point coud connect to it. + size_t next_idx = find_closest_point(kdtree, end_point.pos, + [this_idx, first_point_idx](size_t idx){ return idx != first_point_idx && (idx ^ this_idx) > 1; }); + assert(next_idx < end_points.size()); + EndPoint &end_point2 = end_points[next_idx]; + end_point.edge_out = &end_point2; + end_point.distance_out = (end_point2.pos - end_point.pos).squaredNorm(); + } + } + + // Initialize a heap of end points sorted by the lowest distance to the next valid point of a path. + auto queue = make_mutable_priority_queue( + [](EndPoint *ep, size_t idx){ ep->heap_idx = idx; }, + [](EndPoint *l, EndPoint *r){ return l->distance_out < r->distance_out; }); + queue.reserve(end_points.size() * 2 - 1); + for (EndPoint &ep : end_points) + if (first_point != &ep) + queue.push(&ep); + +#ifndef NDEBUG + auto validate_graph_and_queue = [&equivalent_chain, &end_points, &queue, first_point]() -> bool { + assert(equivalent_chain.validate()); + for (EndPoint &ep : end_points) { + if (ep.heap_idx < queue.size()) { + // End point is on the heap. + assert(*(queue.cbegin() + ep.heap_idx) == &ep); + assert(ep.chain_id == 0); + } else { + // End point is NOT on the heap, therefore it is part of the output path. + assert(ep.heap_idx == std::numeric_limits::max()); + assert(ep.chain_id != 0); + if (&ep == first_point) { + assert(ep.edge_out == nullptr); + } else { + assert(ep.edge_out != nullptr); + // Detect loops. + for (EndPoint *pt = &ep; pt != nullptr;) { + // Out of queue. It is a final point. + assert(pt->heap_idx == std::numeric_limits::max()); + EndPoint *pt_other = &end_points[(pt - &end_points.front()) ^ 1]; + if (pt_other->heap_idx < queue.size()) + // The other side of this segment is undecided yet. + break; + pt = pt_other->edge_out; + } + } + } + } + for (EndPoint *ep : queue) + // Points in the queue are not connected yet. + assert(ep->chain_id == 0); + return true; + }; +#endif /* NDEBUG */ + + // Chain the end points: find (num_segments - 1) shortest links not forming bifurcations or loops. + assert(num_segments >= 2); +#ifndef NDEBUG + double distance_taken_last = 0.; +#endif /* NDEBUG */ + for (int iter = int(num_segments) - 2;; -- iter) { + assert(validate_graph_and_queue()); + // Take the first end point, for which the link points to the currently closest valid neighbor. + EndPoint &end_point1 = *queue.top(); +#ifndef NDEBUG + // Each edge added shall be longer than the previous one taken. + assert(end_point1.distance_out > distance_taken_last - SCALED_EPSILON); + distance_taken_last = end_point1.distance_out; +#endif /* NDEBUG */ + assert(end_point1.edge_out != nullptr); + // No point on the queue may be connected yet. + assert(end_point1.chain_id == 0); + // Take the closest end point to the first end point, + EndPoint &end_point2 = *end_point1.edge_out; + bool valid = true; + size_t end_point1_other_chain_id = 0; + size_t end_point2_other_chain_id = 0; + if (end_point2.chain_id > 0) { + // The other side is part of the output path. Don't connect to end_point2, update end_point1 and try another one. + valid = false; + } else { + // End points of the opposite ends of the segments. + end_point1_other_chain_id = equivalent_chain(end_points[(&end_point1 - &end_points.front()) ^ 1].chain_id); + end_point2_other_chain_id = equivalent_chain(end_points[(&end_point2 - &end_points.front()) ^ 1].chain_id); + if (end_point1_other_chain_id == end_point2_other_chain_id && end_point1_other_chain_id != 0) + // This edge forms a loop. Update end_point1 and try another one. + valid = false; + } + if (valid) { + // Remove the first and second point from the queue. + queue.pop(); + queue.remove(end_point2.heap_idx); + assert(end_point1.edge_out = &end_point2); + end_point2.edge_out = &end_point1; + end_point2.distance_out = end_point1.distance_out; + // Assign chain IDs to the newly connected end points, set equivalent_chain if two chains were merged. + size_t chain_id = + (end_point1_other_chain_id == 0) ? + ((end_point2_other_chain_id == 0) ? equivalent_chain.next() : end_point2_other_chain_id) : + ((end_point2_other_chain_id == 0) ? end_point1_other_chain_id : + (end_point1_other_chain_id == end_point2_other_chain_id) ? + end_point1_other_chain_id : + equivalent_chain.merge(end_point1_other_chain_id, end_point2_other_chain_id)); + end_point1.chain_id = chain_id; + end_point2.chain_id = chain_id; + assert(validate_graph_and_queue()); + if (iter == 0) { + // Last iteration. There shall be exactly one or two end points waiting to be connected. + assert(queue.size() == ((first_point == nullptr) ? 2 : 1)); + if (first_point == nullptr) { + first_point = queue.top(); + queue.pop(); + first_point->edge_out = nullptr; + } + last_point = queue.top(); + last_point->edge_out = nullptr; + queue.pop(); + assert(queue.empty()); + break; + } + } else { + // This edge forms a loop. Update end_point1 and try another one. + ++ iter; + end_point1.edge_out = nullptr; + // Update edge_out and distance. + size_t this_idx = &end_point1 - &end_points.front(); + // Find the closest point to this end_point, which lies on a different extrusion path (filtered by the filter lambda). + size_t next_idx = find_closest_point(kdtree, end_point1.pos, [&end_points, &equivalent_chain, this_idx](size_t idx) { + assert(end_points[this_idx].edge_out == nullptr); + assert(end_points[this_idx].chain_id == 0); + if ((idx ^ this_idx) <= 1 || end_points[idx].chain_id != 0) + // Points of the same segment shall not be connected, + // cannot connect to an already connected point (ideally those would be removed from the KD tree, but the update is difficult). + return false; + size_t chain1 = equivalent_chain(end_points[this_idx ^ 1].chain_id); + size_t chain2 = equivalent_chain(end_points[idx ^ 1].chain_id); + return chain1 != chain2 || chain1 == 0; + }); + assert(next_idx < end_points.size()); + end_point1.edge_out = &end_points[next_idx]; + end_point1.distance_out = (end_points[next_idx].pos - end_point1.pos).squaredNorm(); +#ifndef NDEBUG + // Each edge shall be longer than the last one removed from the queue. + assert(end_point1.distance_out > distance_taken_last - SCALED_EPSILON); +#endif /* NDEBUG */ + // Update position of this end point in the queue based on the distance calculated at the line above. + queue.update(end_point1.heap_idx); + //FIXME Remove the other end point from the KD tree. + // As the KD tree update is expensive, do it only after some larger number of points is removed from the queue. + assert(validate_graph_and_queue()); + } + } + assert(queue.empty()); + + // Now interconnect pairs of segments into a chain. + assert(first_point != nullptr); + out.reserve(num_segments); + bool failed = false; + do { + assert(out.size() < num_segments); + size_t first_point_id = first_point - &end_points.front(); + size_t segment_id = first_point_id >> 1; + bool reverse = (first_point_id & 1) != 0; + EndPoint *second_point = &end_points[first_point_id ^ 1]; + if (REVERSE_COULD_FAIL) { + if (reverse && ! could_reverse_func(segment_id)) { + failed = true; + break; + } + } else { + assert(! reverse || could_reverse_func(segment_id)); + } + out.emplace_back(segment_id, reverse); + first_point = second_point->edge_out; + } while (first_point != nullptr); + if (REVERSE_COULD_FAIL) { + if (failed) { + if (start_near == nullptr) { + // We may try the reverse order. + out.clear(); + first_point = last_point; + failed = false; + do { + assert(out.size() < num_segments); + size_t first_point_id = first_point - &end_points.front(); + size_t segment_id = first_point_id >> 1; + bool reverse = (first_point_id & 1) != 0; + EndPoint *second_point = &end_points[first_point_id ^ 1]; + if (reverse && ! could_reverse_func(segment_id)) { + failed = true; + break; + } + out.emplace_back(segment_id, reverse); + first_point = second_point->edge_out; + } while (first_point != nullptr); + } + } + if (failed) + // As a last resort, try a dumb algorithm, which is not sensitive to edge reversal constraints. + out = chain_segments_closest_point(end_points, kdtree, could_reverse_func, (initial_point != nullptr) ? *initial_point : end_points.front()); + } else { + assert(! failed); + } + } + + assert(out.size() == num_segments); + return out; +} + +template +void update_end_point_in_queue(QueueType &queue, const KDTreeType &kdtree, ChainsType &chains, std::vector &end_points, EndPointType &end_point, size_t first_point_idx, const EndPointType *first_point) +{ + // Updating an end point or a 2nd from an end point. + size_t this_idx = end_point.index(end_points); + // If this segment is not the starting segment, then this end point or the opposite is unconnected. + assert(first_point_idx == this_idx || first_point_idx == (this_idx ^ 1) || end_point.chain_id == 0 || end_point.opposite(end_points).chain_id == 0); + end_point.edge_candidate = nullptr; + if (first_point_idx == this_idx || (end_point.chain_id > 0 && first_point_idx == (this_idx ^ 1))) + { + // One may never flip the 1st edge, don't try it again. + if (! end_point.heap_idx_invalid()) + queue.remove(end_point.heap_idx); + } + else + { + // Update edge_candidate and distance. + size_t chain1a = end_point.chain_id; + size_t chain1b = end_points[this_idx ^ 1].chain_id; + size_t this_chain = chains.equivalent(std::max(chain1a, chain1b)); + // Find the closest point to this end_point, which lies on a different extrusion path (filtered by the filter lambda). + size_t next_idx = find_closest_point(kdtree, end_point.pos, [&end_points, &chains, this_idx, first_point_idx, first_point, this_chain](size_t idx) { + assert(end_points[this_idx].edge_candidate == nullptr); + // Either this end of the edge or the other end of the edge is not yet connected. + assert((end_points[this_idx ].chain_id == 0 && end_points[this_idx ].edge_out == nullptr) || + (end_points[this_idx ^ 1].chain_id == 0 && end_points[this_idx ^ 1].edge_out == nullptr)); + if ((idx ^ this_idx) <= 1 || idx == first_point_idx) + // Points of the same segment shall not be connected. + // Don't connect to the first point, we must not flip the 1st edge. + return false; + size_t chain2a = end_points[idx].chain_id; + size_t chain2b = end_points[idx ^ 1].chain_id; + if (chain2a > 0 && chain2b > 0) + // Only unconnected end point or a point next to an unconnected end point may be connected to. + // Ideally those would be removed from the KD tree, but the update is difficult. + return false; + assert(chain2a == 0 || chain2b == 0); + size_t chain2 = chains.equivalent(std::max(chain2a, chain2b)); + if (this_chain == chain2) + // Don't connect back to the same chain, don't create a loop. + return this_chain == 0; + // Don't connect to a segment requiring flipping if the segment starts or ends with the first point. + if (chain2a > 0) { + // Chain requires flipping. + assert(chain2b == 0); + auto &chain = chains.chain(chain2); + if (chain.begin == first_point || chain.end == first_point) + return false; + } + // Everything is all right, try to connect. + return true; + }); + assert(next_idx < end_points.size()); + assert(chains.equivalent(end_points[next_idx].chain_id) != chains.equivalent(end_points[next_idx ^ 1].chain_id) || end_points[next_idx].chain_id == 0); + end_point.edge_candidate = &end_points[next_idx]; + end_point.distance_out = (end_points[next_idx].pos - end_point.pos).norm(); + if (end_point.chain_id > 0) + end_point.distance_out += chains.chain_flip_penalty(this_chain); + if (end_points[next_idx].chain_id > 0) + // The candidate chain is flipped. + end_point.distance_out += chains.chain_flip_penalty(end_points[next_idx].chain_id); + // Update position of this end point in the queue based on the distance calculated at the line above. + if (end_point.heap_idx_invalid()) + queue.push(&end_point); + else + queue.update(end_point.heap_idx); + } +} + +template +std::vector> chain_segments_greedy_constrained_reversals2_(SegmentEndPointFunc end_point_func, CouldReverseFunc could_reverse_func, size_t num_segments, const PointType *start_near) +{ + std::vector> out; + + if (num_segments == 0) { + // Nothing to do. + } + else if (num_segments == 1) + { + // Just sort the end points so that the first point visited is closest to start_near. + out.emplace_back(0, start_near != nullptr && + (end_point_func(0, true) - *start_near).template cast().squaredNorm() < (end_point_func(0, false) - *start_near).template cast().squaredNorm()); + } + else + { + // End points of segments for the KD tree closest point search. + // A single end point is inserted into the search structure for loops, two end points are entered for open paths. + struct EndPoint { + EndPoint(const Vec2d &pos) : pos(pos) {} + Vec2d pos; + + // Candidate for a new connection link. + EndPoint *edge_candidate = nullptr; + // Distance to the next end point following the link. + // Zero value -> start of the final path. + double distance_out = std::numeric_limits::max(); + + size_t heap_idx = std::numeric_limits::max(); + bool heap_idx_invalid() const { return this->heap_idx == std::numeric_limits::max(); } + + // Identifier of the chain, to which this end point belongs. Zero means unassigned. + size_t chain_id = 0; + // Double linked chain of segment end points in current path. + EndPoint *edge_out = nullptr; + + size_t index(std::vector &endpoints) const { return this - endpoints.data(); } + // Opposite end point of the same segment. + EndPoint& opposite(std::vector &endpoints) { return endpoints[(this - endpoints.data()) ^ 1]; } + const EndPoint& opposite(const std::vector &endpoints) const { return endpoints[(this - endpoints.data()) ^ 1]; } + }; + + std::vector end_points; + end_points.reserve(num_segments * 2); + for (size_t i = 0; i < num_segments; ++ i) { + end_points.emplace_back(end_point_func(i, true ).template cast()); + end_points.emplace_back(end_point_func(i, false).template cast()); + } + + // Construct the closest point KD tree over end points of segments. + auto coordinate_fn = [&end_points](size_t idx, size_t dimension) -> double { return end_points[idx].pos[dimension]; }; + KDTreeIndirect<2, double, decltype(coordinate_fn)> kdtree(coordinate_fn, end_points.size()); + + // Chained segments with their sum of connection lengths. + // The chain supports flipping all the segments, connecting the segments at the opposite ends. + // (this is a very useful path optimization for infill lines). + struct Chain { + size_t num_segments = 0; + double cost = 0.; + double cost_flipped = 0.; + EndPoint *begin = nullptr; + EndPoint *end = nullptr; + size_t equivalent_with = 0; + + // Flipping the chain has a time complexity of O(n). + void flip(std::vector &endpoints) + { + assert(this->num_segments > 1); + assert(this->begin->edge_out == nullptr); + assert(this->end ->edge_out == nullptr); + assert(this->begin->opposite(endpoints).edge_out != nullptr); + assert(this->end ->opposite(endpoints).edge_out != nullptr); + // Start of the current segment processed. + EndPoint *ept = this->begin; + // Previous end point to connect the other side of ept to. + EndPoint *ept_prev = nullptr; + do { + EndPoint *ept_end = &ept->opposite(endpoints); + EndPoint *ept_next = ept_end->edge_out; + assert(ept_next == nullptr || ept_next->edge_out == ept_end); + // Connect to the preceding segment. + ept_end->edge_out = ept_prev; + if (ept_prev != nullptr) + ept_prev->edge_out = ept_end; + ept_prev = ept; + ept = ept_next; + } while (ept != nullptr); + ept_prev->edge_out = nullptr; + // Swap the costs. + std::swap(this->cost, this->cost_flipped); + // Swap the ends. + EndPoint *new_begin = &this->begin->opposite(endpoints); + EndPoint *new_end = &this->end->opposite(endpoints); + std::swap(this->begin->chain_id, new_begin->chain_id); + std::swap(this->end ->chain_id, new_end ->chain_id); + this->begin = new_begin; + this->end = new_end; + assert(this->begin->edge_out == nullptr); + assert(this->end ->edge_out == nullptr); + assert(this->begin->opposite(endpoints).edge_out != nullptr); + assert(this->end ->opposite(endpoints).edge_out != nullptr); + } + + double flip_penalty() const { return this->cost_flipped - this->cost; } + }; + + // Helper to detect loops in already connected paths and to accomodate flipping of chains. + // + // Unique chain IDs are assigned to paths. If paths are connected, end points will not have their chain IDs updated, but the chain IDs + // will remember an "equivalent" chain ID, which is the lowest ID of all the IDs in the path, and the lowest ID is equivalent to itself. + // Chain IDs are indexed starting with 1. + // + // Chains remember their lengths and their lengths when each segment of the chain is flipped. + class Chains { + public: + // Zero'th chain ID is invalid. + Chains(size_t reserve) { + m_chains.reserve(reserve / 2); + // Indexing starts with 1. + m_chains.emplace_back(); + } + + // Generate next equivalence class. + size_t next_id() { + m_chains.emplace_back(); + m_chains.back().equivalent_with = ++ m_last_chain_id; + return m_last_chain_id; + } + + // Get equivalence class for chain ID, update the "equivalent_with" along the equivalence path. + size_t equivalent(size_t chain_id) { + if (chain_id != 0) { + for (size_t last = chain_id;;) { + size_t lower = m_chains[last].equivalent_with; + if (lower == last) { + m_chains[chain_id].equivalent_with = lower; + chain_id = lower; + break; + } + last = lower; + } + } + return chain_id; + } + + // Return a lowest chain ID of the two input chains. + // Produce a new chain ID of both chain IDs are zero. + size_t merge(size_t chain_id1, size_t chain_id2) { + if (chain_id1 == 0) + return (chain_id2 == 0) ? this->next_id() : chain_id2; + if (chain_id2 == 0) + return chain_id1; + assert(m_chains[chain_id1].equivalent_with == chain_id1); + assert(m_chains[chain_id2].equivalent_with == chain_id2); + size_t chain_id = std::min(chain_id1, chain_id2); + m_chains[chain_id1].equivalent_with = chain_id; + m_chains[chain_id2].equivalent_with = chain_id; + return chain_id; + } + + Chain& chain(size_t chain_id) { return m_chains[chain_id]; } + const Chain& chain(size_t chain_id) const { return m_chains[chain_id]; } + + double chain_flip_penalty(size_t chain_id) { + chain_id = this->equivalent(chain_id); + return m_chains[chain_id].flip_penalty(); + } + +#ifndef NDEBUG + bool validate() + { + // Validate that the segments merged chain IDs make up a directed acyclic graph + // with edges oriented towards the lower chain ID, therefore all ending up + // in the lowest chain ID of all of them. + assert(m_last_chain_id >= 0); + assert(m_last_chain_id + 1 == m_chains.size()); + for (size_t i = 0; i < m_chains.size(); ++ i) { + for (size_t last = i;;) { + size_t lower = m_chains[last].equivalent_with; + assert(lower <= last); + if (lower == last) + break; + last = lower; + } + } + return true; + } +#endif /* NDEBUG */ + + private: + std::vector m_chains; + // Unique chain ID assigned to chains of end points of segments. + size_t m_last_chain_id = 0; + } chains(num_segments); + + // Find the first end point closest to start_near. + EndPoint *first_point = nullptr; + size_t first_point_idx = std::numeric_limits::max(); + if (start_near != nullptr) { + size_t idx = find_closest_point(kdtree, start_near->template cast()); + assert(idx < end_points.size()); + first_point = &end_points[idx]; + first_point->distance_out = 0.; + first_point->chain_id = chains.next_id(); + Chain &chain = chains.chain(first_point->chain_id); + chain.begin = first_point; + chain.end = &first_point->opposite(end_points); + first_point_idx = idx; + } + EndPoint *initial_point = first_point; + EndPoint *last_point = nullptr; + + // Assign the closest point and distance to the end points. + for (EndPoint &end_point : end_points) { + assert(end_point.edge_candidate == nullptr); + if (&end_point != first_point) { + size_t this_idx = end_point.index(end_points); + // Find the closest point to this end_point, which lies on a different extrusion path (filtered by the lambda). + // Ignore the starting point as the starting point is considered to be occupied, no end point coud connect to it. + size_t next_idx = find_closest_point(kdtree, end_point.pos, + [this_idx, first_point_idx](size_t idx){ return idx != first_point_idx && (idx ^ this_idx) > 1; }); + assert(next_idx < end_points.size()); + EndPoint &end_point2 = end_points[next_idx]; + end_point.edge_candidate = &end_point2; + end_point.distance_out = (end_point2.pos - end_point.pos).norm(); + } + } + + // Initialize a heap of end points sorted by the lowest distance to the next valid point of a path. + auto queue = make_mutable_priority_queue( + [](EndPoint *ep, size_t idx){ ep->heap_idx = idx; }, + [](EndPoint *l, EndPoint *r){ return l->distance_out < r->distance_out; }); + queue.reserve(end_points.size() * 2); + for (EndPoint &ep : end_points) + if (first_point != &ep) + queue.push(&ep); + +#ifndef NDEBUG + auto validate_graph_and_queue = [&chains, &end_points, &queue, first_point]() -> bool { + assert(chains.validate()); + for (EndPoint &ep : end_points) { + if (ep.heap_idx < queue.size()) { + // End point is on the heap. + assert(*(queue.cbegin() + ep.heap_idx) == &ep); + // One side or the other of the segment is not yet connected. + assert(ep.chain_id == 0 || ep.opposite(end_points).chain_id == 0); + } else { + // End point is NOT on the heap, therefore it must part of the output path. + assert(ep.heap_idx_invalid()); + assert(ep.chain_id != 0); + if (&ep == first_point) { + assert(ep.edge_out == nullptr); + } else { + assert(ep.edge_out != nullptr); + // Detect loops. + for (EndPoint *pt = &ep; pt != nullptr;) { + // Out of queue. It is a final point. + EndPoint *pt_other = &pt->opposite(end_points); + if (pt_other->heap_idx < queue.size()) { + // The other side of this segment is undecided yet. + // assert(pt_other->edge_out == nullptr); + break; + } + pt = pt_other->edge_out; + } + } + } + } + for (EndPoint *ep : queue) + // Points in the queue or the opposites of the same segment are not connected yet. + assert(ep->chain_id == 0 || ep->opposite(end_points).chain_id == 0); + return true; + }; +#endif /* NDEBUG */ + + // Chain the end points: find (num_segments - 1) shortest links not forming bifurcations or loops. + assert(num_segments >= 2); +#ifndef NDEBUG + double distance_taken_last = 0.; +#endif /* NDEBUG */ + // Some links stored onto the priority queue are being invalidated during the calculation and they are not + // updated immediately. If such a situation is detected for an end point pulled from the priority queue, + // the end point is being updated and re-inserted into the priority queue. Therefore the number of iterations + // required is higher than expected (it would be the number of links, num_segments - 1). + // The limit here may not be necessary, but it guards us against an endless loop if something goes wrong. + size_t num_iter = num_segments * 16; + for (size_t num_connections_to_end = num_segments - 1; num_iter > 0; -- num_iter) { + assert(validate_graph_and_queue()); + // Take the first end point, for which the link points to the currently closest valid neighbor. + EndPoint *end_point1 = queue.top(); + assert(end_point1 != first_point); + EndPoint *end_point1_other = &end_point1->opposite(end_points); + // true if end_point1 is not the end of its chain, but the 2nd point. When connecting to the 2nd point, this chain needs + // to be flipped first. + bool chain1_flip = end_point1->chain_id > 0; + // Either this point at the queue is not connected, or it is the 2nd point of a chain. + // If connecting to a 2nd point of a chain, the 1st point shall not yet be connected and this chain will need + // to be flipped. + assert( chain1_flip || (end_point1->chain_id == 0 && end_point1->edge_out == nullptr)); + assert(! chain1_flip || (end_point1_other->chain_id == 0 && end_point1_other->edge_out == nullptr)); + assert(end_point1->edge_candidate != nullptr); +#ifndef NDEBUG + // Each edge added shall be longer than the previous one taken. + //assert(end_point1->distance_out > distance_taken_last - SCALED_EPSILON); + if (end_point1->distance_out < distance_taken_last - SCALED_EPSILON) { +// printf("Warning: taking shorter length than previously is suspicious\n"); + } + distance_taken_last = end_point1->distance_out; +#endif /* NDEBUG */ + // Take the closest end point to the first end point, + EndPoint *end_point2 = end_point1->edge_candidate; + EndPoint *end_point2_other = &end_point2->opposite(end_points); + bool chain2_flip = end_point2->chain_id > 0; + // Is the link from end_point1 to end_point2 still valid? If yes, the link may be taken. Otherwise the link needs to be refreshed. + bool valid = true; + size_t end_point1_chain_id = 0; + size_t end_point2_chain_id = 0; + if (end_point2->chain_id > 0 && end_point2_other->chain_id > 0) { + // The other side is part of the output path. Don't connect to end_point2, update end_point1 and try another one. + valid = false; + } else { + // End points of the opposite ends of the segments. + end_point1_chain_id = chains.equivalent((chain1_flip ? end_point1 : end_point1_other)->chain_id); + end_point2_chain_id = chains.equivalent((chain2_flip ? end_point2 : end_point2_other)->chain_id); + if (end_point1_chain_id == end_point2_chain_id && end_point1_chain_id != 0) + // This edge forms a loop. Update end_point1 and try another one. + valid = false; + else { + // Verify whether end_point1.distance_out still matches the current state of the two end points to be connected and their chains. + // Namely, the other chain may have been flipped in the meantime. + double dist = (end_point2->pos - end_point1->pos).norm(); + if (chain1_flip) + dist += chains.chain_flip_penalty(end_point1_chain_id); + if (chain2_flip) + dist += chains.chain_flip_penalty(end_point2_chain_id); + if (std::abs(dist - end_point1->distance_out) > SCALED_EPSILON) + // The distance changed due to flipping of one of the chains. Refresh this end point in the queue. + valid = false; + } + if (valid && first_point != nullptr) { + // Verify that a chain starting or ending with the first_point does not get flipped. + if (chain1_flip) { + Chain &chain = chains.chain(end_point1_chain_id); + if (chain.begin == first_point || chain.end == first_point) + valid = false; + } + if (valid && chain2_flip) { + Chain &chain = chains.chain(end_point2_chain_id); + if (chain.begin == first_point || chain.end == first_point) + valid = false; + } + } + } + if (valid) { + // Remove the first and second point from the queue. + queue.pop(); + queue.remove(end_point2->heap_idx); + assert(end_point1->edge_candidate == end_point2); + end_point1->edge_candidate = nullptr; + Chain *chain1 = (end_point1_chain_id == 0) ? nullptr : &chains.chain(end_point1_chain_id); + Chain *chain2 = (end_point2_chain_id == 0) ? nullptr : &chains.chain(end_point2_chain_id); + assert(chain1 == nullptr || (chain1_flip ? (chain1->begin == end_point1_other || chain1->end == end_point1_other) : (chain1->begin == end_point1 || chain1->end == end_point1))); + assert(chain2 == nullptr || (chain2_flip ? (chain2->begin == end_point2_other || chain2->end == end_point2_other) : (chain2->begin == end_point2 || chain2->end == end_point2))); + if (chain1_flip) + chain1->flip(end_points); + if (chain2_flip) + chain2->flip(end_points); + assert(chain1 == nullptr || chain1->begin == end_point1 || chain1->end == end_point1); + assert(chain2 == nullptr || chain2->begin == end_point2 || chain2->end == end_point2); + size_t chain_id = chains.merge(end_point1_chain_id, end_point2_chain_id); + Chain &chain = chains.chain(chain_id); + { + Chain chain_dst; + chain_dst.begin = (chain1 == nullptr) ? end_point1_other : (chain1->begin == end_point1) ? chain1->end : chain1->begin; + chain_dst.end = (chain2 == nullptr) ? end_point2_other : (chain2->begin == end_point2) ? chain2->end : chain2->begin; + chain_dst.cost = (chain1 == 0 ? 0. : chain1->cost) + (chain2 == 0 ? 0. : chain2->cost) + (end_point2->pos - end_point1->pos).norm(); + chain_dst.cost_flipped = (chain1 == 0 ? 0. : chain1->cost_flipped) + (chain2 == 0 ? 0. : chain2->cost_flipped) + (end_point2_other->pos - end_point1_other->pos).norm(); + chain_dst.num_segments = (chain1 == 0 ? 1 : chain1->num_segments) + (chain2 == 0 ? 1 : chain2->num_segments); + chain_dst.equivalent_with = chain_id; + chain = chain_dst; + } + if (chain.begin != end_point1_other && ! end_point1_other->heap_idx_invalid()) + queue.remove(end_point1_other->heap_idx); + if (chain.end != end_point2_other && ! end_point2_other->heap_idx_invalid()) + queue.remove(end_point2_other->heap_idx); + end_point1->edge_out = end_point2; + end_point2->edge_out = end_point1; + end_point1->chain_id = chain_id; + end_point2->chain_id = chain_id; + end_point1_other->chain_id = chain_id; + end_point2_other->chain_id = chain_id; + if (chain.begin != first_point) + chain.begin->chain_id = 0; + if (chain.end != first_point) + chain.end->chain_id = 0; + if (-- num_connections_to_end == 0) { + assert(validate_graph_and_queue()); + // Last iteration. There shall be exactly one or two end points waiting to be connected. + assert(queue.size() <= ((first_point == nullptr) ? 4 : 2)); + if (first_point == nullptr) { + // Find the first remaining end point. + do { + first_point = queue.top(); + queue.pop(); + } while (first_point->edge_out != nullptr); + assert(first_point->edge_out == nullptr); + } + // Find the first remaining end point. + do { + last_point = queue.top(); + queue.pop(); + } while (last_point->edge_out != nullptr); + assert(last_point->edge_out == nullptr); +#ifndef NDEBUG + while (! queue.empty()) { + assert(queue.top()->edge_out != nullptr && queue.top()->chain_id > 0); + queue.pop(); + } +#endif /* NDEBUG */ + break; + } else { + //FIXME update the 2nd end points on the queue. + // Update end points of the flipped segments. + update_end_point_in_queue(queue, kdtree, chains, end_points, chain.begin->opposite(end_points), first_point_idx, first_point); + update_end_point_in_queue(queue, kdtree, chains, end_points, chain.end->opposite(end_points), first_point_idx, first_point); + if (chain1_flip) + update_end_point_in_queue(queue, kdtree, chains, end_points, *chain.begin, first_point_idx, first_point); + if (chain2_flip) + update_end_point_in_queue(queue, kdtree, chains, end_points, *chain.end, first_point_idx, first_point); + // End points of chains shall certainly stay in the queue. + assert(chain.begin == first_point || chain.begin->heap_idx < queue.size()); + assert(chain.end == first_point || chain.end ->heap_idx < queue.size()); + assert(&chain.begin->opposite(end_points) != first_point && + (chain.begin == first_point ? chain.begin->opposite(end_points).heap_idx_invalid() : chain.begin->opposite(end_points).heap_idx < queue.size())); + assert(&chain.end ->opposite(end_points) != first_point && + (chain.end == first_point ? chain.end ->opposite(end_points).heap_idx_invalid() : chain.end ->opposite(end_points).heap_idx < queue.size())); + + } + } else { + // This edge forms a loop. Update end_point1 and try another one. + update_end_point_in_queue(queue, kdtree, chains, end_points, *end_point1, first_point_idx, first_point); +#ifndef NDEBUG + // Each edge shall be longer than the last one removed from the queue. + //assert(end_point1->distance_out > distance_taken_last - SCALED_EPSILON); + if (end_point1->distance_out < distance_taken_last - SCALED_EPSILON) { +// printf("Warning: taking shorter length than previously is suspicious\n"); + } +#endif /* NDEBUG */ + //FIXME Remove the other end point from the KD tree. + // As the KD tree update is expensive, do it only after some larger number of points is removed from the queue. + } + assert(validate_graph_and_queue()); + } + assert(queue.empty()); + + // Now interconnect pairs of segments into a chain. + assert(first_point != nullptr); + out.reserve(num_segments); + bool failed = false; + do { + assert(out.size() < num_segments); + size_t first_point_id = first_point - &end_points.front(); + size_t segment_id = first_point_id >> 1; + bool reverse = (first_point_id & 1) != 0; + EndPoint *second_point = &end_points[first_point_id ^ 1]; + if (REVERSE_COULD_FAIL) { + if (reverse && ! could_reverse_func(segment_id)) { + failed = true; + break; + } + } else { + assert(! reverse || could_reverse_func(segment_id)); + } + out.emplace_back(segment_id, reverse); + first_point = second_point->edge_out; + } while (first_point != nullptr); + if (REVERSE_COULD_FAIL) { + if (failed) { + if (start_near == nullptr) { + // We may try the reverse order. + out.clear(); + first_point = last_point; + failed = false; + do { + assert(out.size() < num_segments); + size_t first_point_id = first_point - &end_points.front(); + size_t segment_id = first_point_id >> 1; + bool reverse = (first_point_id & 1) != 0; + EndPoint *second_point = &end_points[first_point_id ^ 1]; + if (reverse && ! could_reverse_func(segment_id)) { + failed = true; + break; + } + out.emplace_back(segment_id, reverse); + first_point = second_point->edge_out; + } while (first_point != nullptr); + } + } + if (failed) + // As a last resort, try a dumb algorithm, which is not sensitive to edge reversal constraints. + out = chain_segments_closest_point(end_points, kdtree, could_reverse_func, (initial_point != nullptr) ? *initial_point : end_points.front()); + } else { + assert(! failed); + } + } + + assert(out.size() == num_segments); + return out; +} + +template +std::vector> chain_segments_greedy_constrained_reversals(SegmentEndPointFunc end_point_func, CouldReverseFunc could_reverse_func, size_t num_segments, const PointType *start_near) +{ + return chain_segments_greedy_constrained_reversals_(end_point_func, could_reverse_func, num_segments, start_near); +} + +template +std::vector> chain_segments_greedy(SegmentEndPointFunc end_point_func, size_t num_segments, const PointType *start_near) +{ + auto could_reverse_func = [](size_t /* idx */) -> bool { return true; }; + return chain_segments_greedy_constrained_reversals_(end_point_func, could_reverse_func, num_segments, start_near); +} + +template +std::vector> chain_segments_greedy_constrained_reversals2(SegmentEndPointFunc end_point_func, CouldReverseFunc could_reverse_func, size_t num_segments, const PointType *start_near) +{ + return chain_segments_greedy_constrained_reversals2_(end_point_func, could_reverse_func, num_segments, start_near); +} + +template +std::vector> chain_segments_greedy2(SegmentEndPointFunc end_point_func, size_t num_segments, const PointType *start_near) +{ + auto could_reverse_func = [](size_t /* idx */) -> bool { return true; }; + return chain_segments_greedy_constrained_reversals2_(end_point_func, could_reverse_func, num_segments, start_near); +} + +std::vector> chain_extrusion_entities(std::vector &entities, const Point *start_near) +{ + auto segment_end_point = [&entities](size_t idx, bool first_point) -> const Point& { return first_point ? entities[idx]->first_point() : entities[idx]->last_point(); }; + auto could_reverse = [&entities](size_t idx) { const ExtrusionEntity *ee = entities[idx]; return ee->is_loop() || ee->can_reverse(); }; + std::vector> out = chain_segments_greedy_constrained_reversals(segment_end_point, could_reverse, entities.size(), start_near); + for (size_t i = 0; i < entities.size(); ++ i) { + ExtrusionEntity *ee = entities[i]; + if (ee->is_loop()) + // Ignore reversals for loops, as the start point equals the end point. + out[i].second = false; + // Is can_reverse() respected by the reversals? + assert(entities[i]->can_reverse() || ! out[i].second); + } + return out; +} + +void reorder_extrusion_entities(std::vector &entities, const std::vector> &chain) +{ + assert(entities.size() == chain.size()); + std::vector out; + out.reserve(entities.size()); + for (const std::pair &idx : chain) { + assert(entities[idx.first] != nullptr); + out.emplace_back(entities[idx.first]); + if (idx.second) + out.back()->reverse(); + } + entities.swap(out); +} + +void chain_and_reorder_extrusion_entities(std::vector &entities, const Point *start_near) +{ + reorder_extrusion_entities(entities, chain_extrusion_entities(entities, start_near)); +} + +std::vector> chain_extrusion_paths(std::vector &extrusion_paths, const Point *start_near) +{ + auto segment_end_point = [&extrusion_paths](size_t idx, bool first_point) -> const Point& { return first_point ? extrusion_paths[idx].first_point() : extrusion_paths[idx].last_point(); }; + return chain_segments_greedy(segment_end_point, extrusion_paths.size(), start_near); +} + +void reorder_extrusion_paths(std::vector &extrusion_paths, const std::vector> &chain) +{ + assert(extrusion_paths.size() == chain.size()); + std::vector out; + out.reserve(extrusion_paths.size()); + for (const std::pair &idx : chain) { + out.emplace_back(std::move(extrusion_paths[idx.first])); + if (idx.second) + out.back().reverse(); + } + extrusion_paths.swap(out); +} + +void chain_and_reorder_extrusion_paths(std::vector &extrusion_paths, const Point *start_near) +{ + reorder_extrusion_paths(extrusion_paths, chain_extrusion_paths(extrusion_paths, start_near)); +} + +std::vector chain_points(const Points &points, Point *start_near) +{ + auto segment_end_point = [&points](size_t idx, bool /* first_point */) -> const Point& { return points[idx]; }; + std::vector> ordered = chain_segments_greedy(segment_end_point, points.size(), start_near); + std::vector out; + out.reserve(ordered.size()); + for (auto &segment_and_reversal : ordered) + out.emplace_back(segment_and_reversal.first); + return out; +} + +#ifndef NDEBUG + // #define DEBUG_SVG_OUTPUT +#endif /* NDEBUG */ + +#ifdef DEBUG_SVG_OUTPUT +void svg_draw_polyline_chain(const char *name, size_t idx, const Polylines &polylines) +{ + BoundingBox bbox = get_extents(polylines); + SVG svg(debug_out_path("%s-%d.svg", name, idx).c_str(), bbox); + svg.draw(polylines); + for (size_t i = 1; i < polylines.size(); ++i) + svg.draw(Line(polylines[i - 1].last_point(), polylines[i].first_point()), "red"); +} +#endif /* DEBUG_SVG_OUTPUT */ + +// Flip the sequences of polylines to lower the total length of connecting lines. +static inline void improve_ordering_by_segment_flipping(Polylines &polylines, bool fixed_start) +{ +#ifndef NDEBUG + auto cost = [&polylines]() { + double sum = 0.; + for (size_t i = 1; i < polylines.size(); ++i) + sum += (polylines[i].first_point() - polylines[i - 1].last_point()).cast().norm(); + return sum; + }; + double cost_initial = cost(); + + static int iRun = 0; + ++ iRun; +#ifdef DEBUG_SVG_OUTPUT + svg_draw_polyline_chain("improve_ordering_by_segment_flipping-initial", iRun, polylines); +#endif /* DEBUG_SVG_OUTPUT */ +#endif /* NDEBUG */ + + struct Connection { + Connection(size_t heap_idx = std::numeric_limits::max(), bool flipped = false) : heap_idx(heap_idx), flipped(flipped) {} + // Position of this object on MutablePriorityHeap. + size_t heap_idx; + // Is segment_idx flipped? + bool flipped; + + double squaredNorm(const Polylines &polylines, const std::vector &connections) const + { return ((this + 1)->start_point(polylines, connections) - this->end_point(polylines, connections)).squaredNorm(); } + double norm(const Polylines &polylines, const std::vector &connections) const + { return sqrt(this->squaredNorm(polylines, connections)); } + double squaredNorm(const Polylines &polylines, const std::vector &connections, bool try_flip1, bool try_flip2) const + { return ((this + 1)->start_point(polylines, connections, try_flip2) - this->end_point(polylines, connections, try_flip1)).squaredNorm(); } + double norm(const Polylines &polylines, const std::vector &connections, bool try_flip1, bool try_flip2) const + { return sqrt(this->squaredNorm(polylines, connections, try_flip1, try_flip2)); } + Vec2d start_point(const Polylines &polylines, const std::vector &connections, bool flip = false) const + { const Polyline &pl = polylines[this - connections.data()]; return ((this->flipped == flip) ? pl.points.front() : pl.points.back()).cast(); } + Vec2d end_point(const Polylines &polylines, const std::vector &connections, bool flip = false) const + { const Polyline &pl = polylines[this - connections.data()]; return ((this->flipped == flip) ? pl.points.back() : pl.points.front()).cast(); } + + bool in_queue() const { return this->heap_idx != std::numeric_limits::max(); } + void flip() { this->flipped = ! this->flipped; } + }; + std::vector connections(polylines.size()); + +#ifndef NDEBUG + auto cost_flipped = [fixed_start, &polylines, &connections]() { + assert(! fixed_start || ! connections.front().flipped); + double sum = 0.; + for (size_t i = 1; i < polylines.size(); ++ i) + sum += connections[i - 1].norm(polylines, connections); + return sum; + }; + double cost_prev = cost_flipped(); + assert(std::abs(cost_initial - cost_prev) < SCALED_EPSILON); + + auto print_statistics = [&polylines, &connections]() { +#if 0 + for (size_t i = 1; i < polylines.size(); ++ i) + printf("Connecting %d with %d: Current length %lf flip(%d, %d), left flipped: %lf, right flipped: %lf, both flipped: %lf, \n", + int(i - 1), int(i), + unscale(connections[i - 1].norm(polylines, connections)), + int(connections[i - 1].flipped), int(connections[i].flipped), + unscale(connections[i - 1].norm(polylines, connections, true, false)), + unscale(connections[i - 1].norm(polylines, connections, false, true)), + unscale(connections[i - 1].norm(polylines, connections, true, true))); +#endif + }; + print_statistics(); +#endif /* NDEBUG */ + + // Initialize a MutablePriorityHeap of connections between polylines. + auto queue = make_mutable_priority_queue( + [](Connection *connection, size_t idx){ connection->heap_idx = idx; }, + // Sort by decreasing connection distance. + [&polylines, &connections](Connection *l, Connection *r){ return l->squaredNorm(polylines, connections) > r->squaredNorm(polylines, connections); }); + queue.reserve(polylines.size() - 1); + for (size_t i = 0; i + 1 < polylines.size(); ++ i) + queue.push(&connections[i]); + + static constexpr size_t itercnt = 100; + size_t iter = 0; + for (; ! queue.empty() && iter < itercnt; ++ iter) { + Connection &connection = *queue.top(); + queue.pop(); + connection.heap_idx = std::numeric_limits::max(); + size_t idx_first = &connection - connections.data(); + // Try to flip segments starting with idx_first + 1 to the end. + // Calculate the last segment to be flipped to improve the total path length. + double length_current = connection.norm(polylines, connections); + double length_flipped = connection.norm(polylines, connections, false, true); + int best_idx_forward = int(idx_first); + double best_improvement_forward = 0.; + for (size_t i = idx_first + 1; i + 1 < connections.size(); ++ i) { + length_current += connections[i].norm(polylines, connections); + double this_improvement = length_current - length_flipped - connections[i].norm(polylines, connections, true, false); + length_flipped += connections[i].norm(polylines, connections, true, true); + if (this_improvement > best_improvement_forward) { + best_improvement_forward = this_improvement; + best_idx_forward = int(i); + } +// if (length_flipped > 1.5 * length_current) +// break; + } + if (length_current - length_flipped > best_improvement_forward) + // Best improvement by flipping up to the end. + best_idx_forward = int(connections.size()) - 1; + // Try to flip segments starting with idx_first - 1 to the start. + // Calculate the last segment to be flipped to improve the total path length. + length_current = connection.norm(polylines, connections); + length_flipped = connection.norm(polylines, connections, true, false); + int best_idx_backwards = int(idx_first); + double best_improvement_backwards = 0.; + for (int i = int(idx_first) - 1; i >= 0; -- i) { + length_current += connections[i].norm(polylines, connections); + double this_improvement = length_current - length_flipped - connections[i].norm(polylines, connections, false, true); + length_flipped += connections[i].norm(polylines, connections, true, true); + if (this_improvement > best_improvement_backwards) { + best_improvement_backwards = this_improvement; + best_idx_backwards = int(i); + } +// if (length_flipped > 1.5 * length_current) +// break; + } + if (! fixed_start && length_current - length_flipped > best_improvement_backwards) + // Best improvement by flipping up to the start including the first polyline. + best_idx_backwards = -1; + int update_begin = int(idx_first); + int update_end = best_idx_forward; + if (best_improvement_backwards > 0. && best_improvement_backwards > best_improvement_forward) { + // Flip the sequence of polylines from idx_first to best_improvement_forward + 1. + update_begin = best_idx_backwards; + update_end = int(idx_first); + } + assert(update_begin <= update_end); + if (update_begin == update_end) + continue; + for (int i = update_begin + 1; i <= update_end; ++ i) + connections[i].flip(); + +#ifndef NDEBUG + double cost = cost_flipped(); + assert(cost < cost_prev); + cost_prev = cost; + print_statistics(); +#endif /* NDEBUG */ + + update_end = std::min(update_end + 1, int(connections.size()) - 1); + for (int i = std::max(0, update_begin); i < update_end; ++ i) { + Connection &c = connections[i]; + if (c.in_queue()) + queue.update(c.heap_idx); + else + queue.push(&c); + } + } + + // Flip the segments based on the flip flag. + for (Polyline &pl : polylines) + if (connections[&pl - polylines.data()].flipped) + pl.reverse(); + +#ifndef NDEBUG + double cost_final = cost(); +#ifdef DEBUG_SVG_OUTPUT + svg_draw_polyline_chain("improve_ordering_by_segment_flipping-final", iRun, polylines); +#endif /* DEBUG_SVG_OUTPUT */ + assert(cost_final <= cost_prev); + assert(cost_final <= cost_initial); +#endif /* NDEBUG */ +} + +struct FlipEdge { + FlipEdge(const Vec2d &p1, const Vec2d &p2, size_t source_index) : p1(p1), p2(p2), source_index(source_index) {} + void flip() { std::swap(this->p1, this->p2); } + Vec2d p1; + Vec2d p2; + size_t source_index; +}; + +struct ConnectionCost { + ConnectionCost(double cost, double cost_flipped) : cost(cost), cost_flipped(cost_flipped) {} + ConnectionCost() : cost(0.), cost_flipped(0.) {} + void flip() { std::swap(this->cost, this->cost_flipped); } + double cost = 0; + double cost_flipped = 0; +}; +static inline ConnectionCost operator-(const ConnectionCost &lhs, const ConnectionCost& rhs) { return ConnectionCost(lhs.cost - rhs.cost, lhs.cost_flipped - rhs.cost_flipped); } + +static inline std::pair minimum_crossover_cost( + const std::vector &edges, + const std::pair &span1, const ConnectionCost &cost1, + const std::pair &span2, const ConnectionCost &cost2, + const std::pair &span3, const ConnectionCost &cost3, + const double cost_current) +{ + auto connection_cost = [&edges]( + const std::pair &span1, const ConnectionCost &cost1, bool reversed1, bool flipped1, + const std::pair &span2, const ConnectionCost &cost2, bool reversed2, bool flipped2, + const std::pair &span3, const ConnectionCost &cost3, bool reversed3, bool flipped3) { + auto first_point = [&edges](const std::pair &span, bool flipped) { return flipped ? edges[span.first].p2 : edges[span.first].p1; }; + auto last_point = [&edges](const std::pair &span, bool flipped) { return flipped ? edges[span.second - 1].p1 : edges[span.second - 1].p2; }; + auto point = [first_point, last_point](const std::pair &span, bool start, bool flipped) { return start ? first_point(span, flipped) : last_point(span, flipped); }; + auto cost = [](const ConnectionCost &acost, bool flipped) { + assert(acost.cost >= 0. && acost.cost_flipped >= 0.); + return flipped ? acost.cost_flipped : acost.cost; + }; + // Ignore reversed single segment spans. + auto simple_span_ignore = [](const std::pair& span, bool reversed) { + return span.first + 1 == span.second && reversed; + }; + assert(span1.first < span1.second); + assert(span2.first < span2.second); + assert(span3.first < span3.second); + return + simple_span_ignore(span1, reversed1) || simple_span_ignore(span2, reversed2) || simple_span_ignore(span3, reversed3) ? + // Don't perform unnecessary calculations simulating reversion of single segment spans. + std::numeric_limits::max() : + // Calculate the cost of reverting chains and / or flipping segment orientations. + cost(cost1, flipped1) + cost(cost2, flipped2) + cost(cost3, flipped3) + + (point(span2, ! reversed2, flipped2) - point(span1, reversed1, flipped1)).norm() + + (point(span3, ! reversed3, flipped3) - point(span2, reversed2, flipped2)).norm(); + }; + +#ifndef NDEBUG + { + double c = connection_cost(span1, cost1, false, false, span2, cost2, false, false, span3, cost3, false, false); + assert(std::abs(c - cost_current) < SCALED_EPSILON); + } +#endif /* NDEBUG */ + + double cost_min = cost_current; + size_t flip_min = 0; // no flip, no improvement + for (size_t i = 0; i < (1 << 6); ++ i) { + // From the three combinations of 1,2,3 ordering, the other three are reversals of the first three. + double c1 = (i == 0) ? cost_current : + connection_cost(span1, cost1, (i & 1) != 0, (i & (1 << 1)) != 0, span2, cost2, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span3, cost3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0); + double c2 = connection_cost(span1, cost1, (i & 1) != 0, (i & (1 << 1)) != 0, span3, cost3, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span2, cost2, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0); + double c3 = connection_cost(span2, cost2, (i & 1) != 0, (i & (1 << 1)) != 0, span1, cost1, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span3, cost3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0); + if (c1 < cost_min) { + cost_min = c1; + flip_min = i; + } + if (c2 < cost_min) { + cost_min = c2; + flip_min = i + (1 << 6); + } + if (c3 < cost_min) { + cost_min = c3; + flip_min = i + (2 << 6); + } + } + return std::make_pair(cost_min, flip_min); +} + +static inline std::pair minimum_crossover_cost( + const std::vector &edges, + const std::pair &span1, const ConnectionCost &cost1, + const std::pair &span2, const ConnectionCost &cost2, + const std::pair &span3, const ConnectionCost &cost3, + const std::pair &span4, const ConnectionCost &cost4, + const double cost_current) +{ + auto connection_cost = [&edges]( + const std::pair &span1, const ConnectionCost &cost1, bool reversed1, bool flipped1, + const std::pair &span2, const ConnectionCost &cost2, bool reversed2, bool flipped2, + const std::pair &span3, const ConnectionCost &cost3, bool reversed3, bool flipped3, + const std::pair &span4, const ConnectionCost &cost4, bool reversed4, bool flipped4) { + auto first_point = [&edges](const std::pair &span, bool flipped) { return flipped ? edges[span.first].p2 : edges[span.first].p1; }; + auto last_point = [&edges](const std::pair &span, bool flipped) { return flipped ? edges[span.second - 1].p1 : edges[span.second - 1].p2; }; + auto point = [first_point, last_point](const std::pair &span, bool start, bool flipped) { return start ? first_point(span, flipped) : last_point(span, flipped); }; + auto cost = [](const ConnectionCost &acost, bool flipped) { + assert(acost.cost >= 0. && acost.cost_flipped >= 0.); + return flipped ? acost.cost_flipped : acost.cost; + }; + // Ignore reversed single segment spans. + auto simple_span_ignore = [](const std::pair& span, bool reversed) { + return span.first + 1 == span.second && reversed; + }; + assert(span1.first < span1.second); + assert(span2.first < span2.second); + assert(span3.first < span3.second); + assert(span4.first < span4.second); + return + simple_span_ignore(span1, reversed1) || simple_span_ignore(span2, reversed2) || simple_span_ignore(span3, reversed3) || simple_span_ignore(span4, reversed4) ? + // Don't perform unnecessary calculations simulating reversion of single segment spans. + std::numeric_limits::max() : + // Calculate the cost of reverting chains and / or flipping segment orientations. + cost(cost1, flipped1) + cost(cost2, flipped2) + cost(cost3, flipped3) + cost(cost4, flipped4) + + (point(span2, ! reversed2, flipped2) - point(span1, reversed1, flipped1)).norm() + + (point(span3, ! reversed3, flipped3) - point(span2, reversed2, flipped2)).norm() + + (point(span4, ! reversed4, flipped4) - point(span3, reversed3, flipped3)).norm(); + }; + +#ifndef NDEBUG + { + double c = connection_cost(span1, cost1, false, false, span2, cost2, false, false, span3, cost3, false, false, span4, cost4, false, false); + assert(std::abs(c - cost_current) < SCALED_EPSILON); + } +#endif /* NDEBUG */ + + double cost_min = cost_current; + size_t flip_min = 0; // no flip, no improvement + for (size_t i = 0; i < (1 << 8); ++ i) { + // From the three combinations of 1,2,3 ordering, the other three are reversals of the first three. + size_t permutation = 0; + for (double c : { + (i == 0) ? cost_current : + connection_cost(span1, cost1, (i & 1) != 0, (i & (1 << 1)) != 0, span2, cost2, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span3, cost3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, cost4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(span1, cost1, (i & 1) != 0, (i & (1 << 1)) != 0, span2, cost2, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span4, cost4, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span3, cost3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(span1, cost1, (i & 1) != 0, (i & (1 << 1)) != 0, span3, cost3, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span2, cost2, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, cost4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(span1, cost1, (i & 1) != 0, (i & (1 << 1)) != 0, span3, cost3, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span4, cost4, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span2, cost2, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(span1, cost1, (i & 1) != 0, (i & (1 << 1)) != 0, span4, cost4, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span2, cost2, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span3, cost3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(span1, cost1, (i & 1) != 0, (i & (1 << 1)) != 0, span4, cost4, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span3, cost3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span2, cost2, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(span2, cost2, (i & 1) != 0, (i & (1 << 1)) != 0, span1, cost1, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span3, cost3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, cost4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(span2, cost2, (i & 1) != 0, (i & (1 << 1)) != 0, span1, cost1, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span4, cost4, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span3, cost3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(span2, cost2, (i & 1) != 0, (i & (1 << 1)) != 0, span3, cost3, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span1, cost1, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, cost4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(span2, cost2, (i & 1) != 0, (i & (1 << 1)) != 0, span4, cost4, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span1, cost1, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span3, cost3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(span3, cost3, (i & 1) != 0, (i & (1 << 1)) != 0, span1, cost1, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span2, cost2, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, cost4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(span3, cost3, (i & 1) != 0, (i & (1 << 1)) != 0, span2, cost2, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span1, cost1, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, cost4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0) + }) { + if (c < cost_min) { + cost_min = c; + flip_min = i + (permutation << 8); + } + ++ permutation; + } + } + return std::make_pair(cost_min, flip_min); +} + +static inline void do_crossover(const std::vector &edges_in, std::vector &edges_out, + const std::pair &span1, const std::pair &span2, const std::pair &span3, + size_t i) +{ + assert(edges_in.size() == edges_out.size()); + auto do_it = [&edges_in, &edges_out]( + const std::pair &span1, bool reversed1, bool flipped1, + const std::pair &span2, bool reversed2, bool flipped2, + const std::pair &span3, bool reversed3, bool flipped3) { + auto it_edges_out = edges_out.begin(); + auto copy_span = [&edges_in, &edges_out, &it_edges_out](std::pair span, bool reversed, bool flipped) { + assert(span.first < span.second); + auto it = it_edges_out; + if (reversed) + std::reverse_copy(edges_in.begin() + span.first, edges_in.begin() + span.second, it_edges_out); + else + std::copy (edges_in.begin() + span.first, edges_in.begin() + span.second, it_edges_out); + it_edges_out += span.second - span.first; + if (reversed != flipped) { + for (; it != it_edges_out; ++ it) + it->flip(); + } + }; + copy_span(span1, reversed1, flipped1); + copy_span(span2, reversed2, flipped2); + copy_span(span3, reversed3, flipped3); + }; + switch (i >> 6) { + case 0: + do_it(span1, (i & 1) != 0, (i & (1 << 1)) != 0, span2, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0); + break; + case 1: + do_it(span1, (i & 1) != 0, (i & (1 << 1)) != 0, span3, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span2, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0); + break; + default: + assert((i >> 6) == 2); + do_it(span2, (i & 1) != 0, (i & (1 << 1)) != 0, span1, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0); + } + assert(edges_in.size() == edges_out.size()); +} + + +static inline void do_crossover(const std::vector &edges_in, std::vector &edges_out, + const std::pair &span1, const std::pair &span2, const std::pair &span3, const std::pair &span4, + size_t i) +{ + assert(edges_in.size() == edges_out.size()); + auto do_it = [&edges_in, &edges_out]( + const std::pair &span1, bool reversed1, bool flipped1, + const std::pair &span2, bool reversed2, bool flipped2, + const std::pair &span3, bool reversed3, bool flipped3, + const std::pair &span4, bool reversed4, bool flipped4) { + auto it_edges_out = edges_out.begin(); + auto copy_span = [&edges_in, &edges_out, &it_edges_out](std::pair span, bool reversed, bool flipped) { + assert(span.first < span.second); + auto it = it_edges_out; + if (reversed) + std::reverse_copy(edges_in.begin() + span.first, edges_in.begin() + span.second, it_edges_out); + else + std::copy (edges_in.begin() + span.first, edges_in.begin() + span.second, it_edges_out); + it_edges_out += span.second - span.first; + if (reversed != flipped) { + for (; it != it_edges_out; ++ it) + it->flip(); + } + }; + copy_span(span1, reversed1, flipped1); + copy_span(span2, reversed2, flipped2); + copy_span(span3, reversed3, flipped3); + copy_span(span4, reversed4, flipped4); + }; + switch (i >> 8) { + case 0: + assert(i != 0); // otherwise it would be a no-op + do_it(span1, (i & 1) != 0, (i & (1 << 1)) != 0, span2, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0); + break; + case 1: + do_it(span1, (i & 1) != 0, (i & (1 << 1)) != 0, span2, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span4, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0); + break; + case 2: + do_it(span1, (i & 1) != 0, (i & (1 << 1)) != 0, span3, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span2, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0); + break; + case 3: + do_it(span1, (i & 1) != 0, (i & (1 << 1)) != 0, span3, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span4, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span2, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0); + break; + case 4: + do_it(span1, (i & 1) != 0, (i & (1 << 1)) != 0, span4, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span2, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0); + break; + case 5: + do_it(span1, (i & 1) != 0, (i & (1 << 1)) != 0, span4, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span2, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0); + break; + case 6: + do_it(span2, (i & 1) != 0, (i & (1 << 1)) != 0, span1, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0); + break; + case 7: + do_it(span2, (i & 1) != 0, (i & (1 << 1)) != 0, span1, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span4, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0); + break; + case 8: + do_it(span2, (i & 1) != 0, (i & (1 << 1)) != 0, span3, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span1, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0); + break; + case 9: + do_it(span2, (i & 1) != 0, (i & (1 << 1)) != 0, span4, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span1, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0); + break; + case 10: + do_it(span3, (i & 1) != 0, (i & (1 << 1)) != 0, span1, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span2, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0); + break; + default: + assert((i >> 8) == 11); + do_it(span3, (i & 1) != 0, (i & (1 << 1)) != 0, span2, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span1, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0); + break; + } + assert(edges_in.size() == edges_out.size()); +} + +static inline void reorder_by_two_exchanges_with_segment_flipping(std::vector &edges) +{ + if (edges.size() < 2) + return; + + std::vector connections(edges.size()); + std::vector edges_tmp(edges); + std::vector> connection_lengths(edges.size() - 1, std::pair(0., 0)); + std::vector connection_tried(edges.size(), false); + for (size_t iter = 0; iter < edges.size(); ++ iter) { + // Initialize connection costs and connection lengths. + for (size_t i = 1; i < edges.size(); ++ i) { + const FlipEdge &e1 = edges[i - 1]; + const FlipEdge &e2 = edges[i]; + ConnectionCost &c = connections[i]; + c = connections[i - 1]; + double l = (e2.p1 - e1.p2).norm(); + c.cost += l; + c.cost_flipped += (e2.p2 - e1.p1).norm(); + connection_lengths[i - 1] = std::make_pair(l, i); + } + std::sort(connection_lengths.begin(), connection_lengths.end(), [](const std::pair &l, const std::pair &r) { return l.first > r.first; }); + std::fill(connection_tried.begin(), connection_tried.end(), false); + size_t crossover1_pos_final = std::numeric_limits::max(); + size_t crossover2_pos_final = std::numeric_limits::max(); + size_t crossover_flip_final = 0; + for (const std::pair &first_crossover_candidate : connection_lengths) { + double longest_connection_length = first_crossover_candidate.first; + size_t longest_connection_idx = first_crossover_candidate.second; + connection_tried[longest_connection_idx] = true; + // Find the second crossover connection with the lowest total chain cost. + size_t crossover_pos_min = std::numeric_limits::max(); + double crossover_cost_min = connections.back().cost; + size_t crossover_flip_min = 0; + for (size_t j = 1; j < connections.size(); ++ j) + if (! connection_tried[j]) { + size_t a = j; + size_t b = longest_connection_idx; + if (a > b) + std::swap(a, b); + std::pair cost_and_flip = minimum_crossover_cost(edges, + std::make_pair(size_t(0), a), connections[a - 1], std::make_pair(a, b), connections[b - 1] - connections[a], std::make_pair(b, edges.size()), connections.back() - connections[b], + connections.back().cost); + if (cost_and_flip.second > 0 && cost_and_flip.first < crossover_cost_min) { + crossover_pos_min = j; + crossover_cost_min = cost_and_flip.first; + crossover_flip_min = cost_and_flip.second; + assert(crossover_cost_min < connections.back().cost + EPSILON); + } + } + if (crossover_cost_min < connections.back().cost) { + // The cost of the chain with the proposed two crossovers has a lower total cost than the current chain. Apply the crossover. + crossover1_pos_final = longest_connection_idx; + crossover2_pos_final = crossover_pos_min; + crossover_flip_final = crossover_flip_min; + break; + } else { + // Continue with another long candidate edge. + } + } + if (crossover_flip_final > 0) { + // Pair of cross over positions and flip / reverse constellation has been found, which improves the total cost of the connection. + // Perform a crossover. + if (crossover1_pos_final > crossover2_pos_final) + std::swap(crossover1_pos_final, crossover2_pos_final); + do_crossover(edges, edges_tmp, std::make_pair(size_t(0), crossover1_pos_final), std::make_pair(crossover1_pos_final, crossover2_pos_final), std::make_pair(crossover2_pos_final, edges.size()), crossover_flip_final); + edges.swap(edges_tmp); + } else { + // No valid pair of cross over positions was found improving the total cost. Giving up. + break; + } + } +} + +static inline void reorder_by_three_exchanges_with_segment_flipping(std::vector &edges) +{ + if (edges.size() < 3) { + reorder_by_two_exchanges_with_segment_flipping(edges); + return; + } + + std::vector connections(edges.size()); + std::vector edges_tmp(edges); + std::vector> connection_lengths(edges.size() - 1, std::pair(0., 0)); + std::vector connection_tried(edges.size(), false); + for (size_t iter = 0; iter < edges.size(); ++ iter) { + // Initialize connection costs and connection lengths. + for (size_t i = 1; i < edges.size(); ++ i) { + const FlipEdge &e1 = edges[i - 1]; + const FlipEdge &e2 = edges[i]; + ConnectionCost &c = connections[i]; + c = connections[i - 1]; + double l = (e2.p1 - e1.p2).norm(); + c.cost += l; + c.cost_flipped += (e2.p2 - e1.p1).norm(); + connection_lengths[i - 1] = std::make_pair(l, i); + } + std::sort(connection_lengths.begin(), connection_lengths.end(), [](const std::pair &l, const std::pair &r) { return l.first > r.first; }); + std::fill(connection_tried.begin(), connection_tried.end(), false); + size_t crossover1_pos_final = std::numeric_limits::max(); + size_t crossover2_pos_final = std::numeric_limits::max(); + size_t crossover3_pos_final = std::numeric_limits::max(); + size_t crossover_flip_final = 0; + for (const std::pair &first_crossover_candidate : connection_lengths) { + double longest_connection_length = first_crossover_candidate.first; + size_t longest_connection_idx = first_crossover_candidate.second; + connection_tried[longest_connection_idx] = true; + // Find the second crossover connection with the lowest total chain cost. + size_t crossover_pos_min = std::numeric_limits::max(); + double crossover_cost_min = connections.back().cost; + for (size_t j = 1; j < connections.size(); ++ j) + if (! connection_tried[j]) { + for (size_t k = j + 1; k < connections.size(); ++ k) + if (! connection_tried[k]) { + size_t a = longest_connection_idx; + size_t b = j; + size_t c = k; + if (a > c) + std::swap(a, c); + if (a > b) + std::swap(a, b); + if (b > c) + std::swap(b, c); + std::pair cost_and_flip = minimum_crossover_cost(edges, + std::make_pair(size_t(0), a), connections[a - 1], std::make_pair(a, b), connections[b - 1] - connections[a], + std::make_pair(b, c), connections[c - 1] - connections[b], std::make_pair(c, edges.size()), connections.back() - connections[c], + connections.back().cost); + if (cost_and_flip.second > 0 && cost_and_flip.first < crossover_cost_min) { + crossover_cost_min = cost_and_flip.first; + crossover1_pos_final = a; + crossover2_pos_final = b; + crossover3_pos_final = c; + crossover_flip_final = cost_and_flip.second; + assert(crossover_cost_min < connections.back().cost + EPSILON); + } + } + } + if (crossover_flip_final > 0) { + // The cost of the chain with the proposed two crossovers has a lower total cost than the current chain. Apply the crossover. + break; + } else { + // Continue with another long candidate edge. + } + } + if (crossover_flip_final > 0) { + // Pair of cross over positions and flip / reverse constellation has been found, which improves the total cost of the connection. + // Perform a crossover. + do_crossover(edges, edges_tmp, std::make_pair(size_t(0), crossover1_pos_final), std::make_pair(crossover1_pos_final, crossover2_pos_final), + std::make_pair(crossover2_pos_final, crossover3_pos_final), std::make_pair(crossover3_pos_final, edges.size()), crossover_flip_final); + edges.swap(edges_tmp); + } else { + // No valid pair of cross over positions was found improving the total cost. Giving up. + break; + } + } +} + +typedef Eigen::Matrix Matrixd; + +class FourOptCosts { +public: + FourOptCosts(const ConnectionCost &c1, const ConnectionCost &c2, const ConnectionCost &c3, const ConnectionCost &c4) : costs { &c1, &c2, &c3, &c4 } {} + + double operator()(size_t piece_idx, bool flipped) const { return flipped ? costs[piece_idx]->cost_flipped : costs[piece_idx]->cost; } + +private: + const ConnectionCost* costs[4]; +}; + +static inline std::pair minimum_crossover_cost( + const FourOptCosts &segment_costs, + const Matrixd &segment_end_point_distance_matrix, + const double cost_current) +{ + // Distance from the end of span1 to the start of span2. + auto end_point_distance = [&segment_end_point_distance_matrix](size_t span1, bool reversed1, bool flipped1, size_t span2, bool reversed2, bool flipped2) { + return segment_end_point_distance_matrix(span1 * 4 + (! reversed1) * 2 + flipped1, span2 * 4 + reversed2 * 2 + flipped2); + }; + auto connection_cost = [&segment_costs, end_point_distance]( + const size_t span1, bool reversed1, bool flipped1, + const size_t span2, bool reversed2, bool flipped2, + const size_t span3, bool reversed3, bool flipped3, + const size_t span4, bool reversed4, bool flipped4) { + // Calculate the cost of reverting chains and / or flipping segment orientations. + return segment_costs(span1, flipped1) + segment_costs(span2, flipped2) + segment_costs(span3, flipped3) + segment_costs(span4, flipped4) + + end_point_distance(span1, reversed1, flipped1, span2, reversed2, flipped2) + + end_point_distance(span2, reversed2, flipped2, span3, reversed3, flipped3) + + end_point_distance(span3, reversed3, flipped3, span4, reversed4, flipped4); + }; + +#ifndef NDEBUG + { + double c = connection_cost(0, false, false, 1, false, false, 2, false, false, 3, false, false); + assert(std::abs(c - cost_current) < SCALED_EPSILON); + } +#endif /* NDEBUG */ + + double cost_min = cost_current; + size_t flip_min = 0; // no flip, no improvement + for (size_t i = 0; i < (1 << 8); ++ i) { + // From the three combinations of 1,2,3 ordering, the other three are reversals of the first three. + size_t permutation = 0; + for (double c : { + (i == 0) ? cost_current : + connection_cost(0, (i & 1) != 0, (i & (1 << 1)) != 0, 1, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, 2, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, 3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(0, (i & 1) != 0, (i & (1 << 1)) != 0, 1, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, 3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, 2, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(0, (i & 1) != 0, (i & (1 << 1)) != 0, 2, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, 1, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, 3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(0, (i & 1) != 0, (i & (1 << 1)) != 0, 2, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, 3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, 1, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(0, (i & 1) != 0, (i & (1 << 1)) != 0, 3, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, 1, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, 2, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(0, (i & 1) != 0, (i & (1 << 1)) != 0, 3, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, 2, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, 1, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(1, (i & 1) != 0, (i & (1 << 1)) != 0, 0, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, 2, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, 3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(1, (i & 1) != 0, (i & (1 << 1)) != 0, 0, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, 3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, 2, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(1, (i & 1) != 0, (i & (1 << 1)) != 0, 2, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, 0, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, 3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(1, (i & 1) != 0, (i & (1 << 1)) != 0, 3, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, 0, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, 2, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(2, (i & 1) != 0, (i & (1 << 1)) != 0, 0, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, 1, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, 3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0), + connection_cost(2, (i & 1) != 0, (i & (1 << 1)) != 0, 1, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, 0, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, 3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0) + }) { + if (c < cost_min) { + cost_min = c; + flip_min = i + (permutation << 8); + } + ++ permutation; + } + } + return std::make_pair(cost_min, flip_min); +} + +static inline void reorder_by_three_exchanges_with_segment_flipping2(std::vector &edges) +{ + if (edges.size() < 3) { + reorder_by_two_exchanges_with_segment_flipping(edges); + return; + } + + std::vector connections(edges.size()); + std::vector edges_tmp(edges); + std::vector> connection_lengths(edges.size() - 1, std::pair(0., 0)); + std::vector connection_tried(edges.size(), false); + for (size_t iter = 0; iter < edges.size(); ++ iter) { + // Initialize connection costs and connection lengths. + for (size_t i = 1; i < edges.size(); ++ i) { + const FlipEdge &e1 = edges[i - 1]; + const FlipEdge &e2 = edges[i]; + ConnectionCost &c = connections[i]; + c = connections[i - 1]; + double l = (e2.p1 - e1.p2).norm(); + c.cost += l; + c.cost_flipped += (e2.p2 - e1.p1).norm(); + connection_lengths[i - 1] = std::make_pair(l, i); + } + std::sort(connection_lengths.begin(), connection_lengths.end(), [](const std::pair &l, const std::pair &r) { return l.first > r.first; }); + std::fill(connection_tried.begin(), connection_tried.end(), false); + size_t crossover1_pos_final = std::numeric_limits::max(); + size_t crossover2_pos_final = std::numeric_limits::max(); + size_t crossover3_pos_final = std::numeric_limits::max(); + size_t crossover_flip_final = 0; + // Distances between the end points of the four pieces of the current segment sequence. +#ifdef NDEBUG + Matrixd segment_end_point_distance_matrix(4 * 4, 4 * 4); +#else /* NDEBUG */ + Matrixd segment_end_point_distance_matrix = Matrixd::Constant(4 * 4, 4 * 4, std::numeric_limits::max()); +#endif /* NDEBUG */ + for (const std::pair &first_crossover_candidate : connection_lengths) { + double longest_connection_length = first_crossover_candidate.first; + size_t longest_connection_idx = first_crossover_candidate.second; + connection_tried[longest_connection_idx] = true; + // Find the second crossover connection with the lowest total chain cost. + size_t crossover_pos_min = std::numeric_limits::max(); + double crossover_cost_min = connections.back().cost; + for (size_t j = 1; j < connections.size(); ++ j) + if (! connection_tried[j]) { + for (size_t k = j + 1; k < connections.size(); ++ k) + if (! connection_tried[k]) { + size_t a = longest_connection_idx; + size_t b = j; + size_t c = k; + if (a > c) + std::swap(a, c); + if (a > b) + std::swap(a, b); + if (b > c) + std::swap(b, c); + const Vec2d* endpts[16] = { + &edges[0].p1, &edges[0].p2, &edges[a - 1].p2, &edges[a - 1].p1, + &edges[a].p1, &edges[a].p2, &edges[b - 1].p2, &edges[b - 1].p1, + &edges[b].p1, &edges[b].p2, &edges[c - 1].p2, &edges[c - 1].p1, + &edges[c].p1, &edges[c].p2, &edges.back().p2, &edges.back().p1 }; + for (size_t v = 0; v < 16; ++ v) { + const Vec2d &p1 = *endpts[v]; + for (size_t u = (v & (~3)) + 4; u < 16; ++ u) + segment_end_point_distance_matrix(u, v) = segment_end_point_distance_matrix(v, u) = (*endpts[u] - p1).norm(); + } + FourOptCosts segment_costs(connections[a - 1], connections[b - 1] - connections[a], connections[c - 1] - connections[b], connections.back() - connections[c]); + std::pair cost_and_flip = minimum_crossover_cost(segment_costs, segment_end_point_distance_matrix, connections.back().cost); + if (cost_and_flip.second > 0 && cost_and_flip.first < crossover_cost_min) { + crossover_cost_min = cost_and_flip.first; + crossover1_pos_final = a; + crossover2_pos_final = b; + crossover3_pos_final = c; + crossover_flip_final = cost_and_flip.second; + assert(crossover_cost_min < connections.back().cost + EPSILON); + } + } + } + if (crossover_flip_final > 0) { + // The cost of the chain with the proposed two crossovers has a lower total cost than the current chain. Apply the crossover. + break; + } else { + // Continue with another long candidate edge. + } + } + if (crossover_flip_final > 0) { + // Pair of cross over positions and flip / reverse constellation has been found, which improves the total cost of the connection. + // Perform a crossover. + do_crossover(edges, edges_tmp, std::make_pair(size_t(0), crossover1_pos_final), std::make_pair(crossover1_pos_final, crossover2_pos_final), + std::make_pair(crossover2_pos_final, crossover3_pos_final), std::make_pair(crossover3_pos_final, edges.size()), crossover_flip_final); + edges.swap(edges_tmp); + } else { + // No valid pair of cross over positions was found improving the total cost. Giving up. + break; + } + } +} + +// Flip the sequences of polylines to lower the total length of connecting lines. +static inline void improve_ordering_by_two_exchanges_with_segment_flipping(Polylines &polylines, bool fixed_start) +{ +#ifndef NDEBUG + auto cost = [&polylines]() { + double sum = 0.; + for (size_t i = 1; i < polylines.size(); ++i) + sum += (polylines[i].first_point() - polylines[i - 1].last_point()).cast().norm(); + return sum; + }; + double cost_initial = cost(); + + static int iRun = 0; + ++ iRun; +#ifdef DEBUG_SVG_OUTPUT + svg_draw_polyline_chain("improve_ordering_by_two_exchanges_with_segment_flipping-initial", iRun, polylines); +#endif /* DEBUG_SVG_OUTPUT */ +#endif /* NDEBUG */ + + std::vector edges; + edges.reserve(polylines.size()); + std::transform(polylines.begin(), polylines.end(), std::back_inserter(edges), + [&polylines](const Polyline &pl){ return FlipEdge(pl.first_point().cast(), pl.last_point().cast(), &pl - polylines.data()); }); +#if 1 + reorder_by_two_exchanges_with_segment_flipping(edges); +#else + // reorder_by_three_exchanges_with_segment_flipping(edges); + reorder_by_three_exchanges_with_segment_flipping2(edges); +#endif + Polylines out; + out.reserve(polylines.size()); + for (const FlipEdge &edge : edges) { + Polyline &pl = polylines[edge.source_index]; + out.emplace_back(std::move(pl)); + if (edge.p2 == pl.first_point().cast()) { + // Polyline is flipped. + out.back().reverse(); + } else { + // Polyline is not flipped. + assert(edge.p1 == pl.first_point().cast()); + } + } + +#ifndef NDEBUG + double cost_final = cost(); +#ifdef DEBUG_SVG_OUTPUT + svg_draw_polyline_chain("improve_ordering_by_two_exchanges_with_segment_flipping-final", iRun, out); +#endif /* DEBUG_SVG_OUTPUT */ + assert(cost_final <= cost_initial); +#endif /* NDEBUG */ +} + +Polylines chain_polylines(Polylines &&polylines, const Point *start_near) +{ +#ifdef DEBUG_SVG_OUTPUT + static int iRun = 0; + ++ iRun; + svg_draw_polyline_chain("chain_polylines-initial", iRun, polylines); +#endif /* DEBUG_SVG_OUTPUT */ + + Polylines out; + if (! polylines.empty()) { + auto segment_end_point = [&polylines](size_t idx, bool first_point) -> const Point& { return first_point ? polylines[idx].first_point() : polylines[idx].last_point(); }; + std::vector> ordered = chain_segments_greedy2(segment_end_point, polylines.size(), start_near); + out.reserve(polylines.size()); + for (auto &segment_and_reversal : ordered) { + out.emplace_back(std::move(polylines[segment_and_reversal.first])); + if (segment_and_reversal.second) + out.back().reverse(); + } + if (out.size() > 1 && start_near == nullptr) { + improve_ordering_by_two_exchanges_with_segment_flipping(out, start_near != nullptr); + //improve_ordering_by_segment_flipping(out, start_near != nullptr); + } + } + +#ifdef DEBUG_SVG_OUTPUT + svg_draw_polyline_chain("chain_polylines-final", iRun, out); +#endif /* DEBUG_SVG_OUTPUT */ + return out; +} + +template static inline T chain_path_items(const Points &points, const T &items) +{ + auto segment_end_point = [&points](size_t idx, bool /* first_point */) -> const Point& { return points[idx]; }; + std::vector> ordered = chain_segments_greedy(segment_end_point, points.size(), nullptr); + T out; + out.reserve(items.size()); + for (auto &segment_and_reversal : ordered) + out.emplace_back(items[segment_and_reversal.first]); + return out; +} + +ClipperLib::PolyNodes chain_clipper_polynodes(const Points &points, const ClipperLib::PolyNodes &items) +{ + return chain_path_items(points, items); +} + +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)); + 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; + out.reserve(instances.size()); + for (auto &segment_and_reversal : ordered) + out.emplace_back(instances[segment_and_reversal.first]); + return out; +} + +} // namespace Slic3r diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp new file mode 100644 index 000000000..cd342015d --- /dev/null +++ b/src/libslic3r/ShortestPath.hpp @@ -0,0 +1,38 @@ +#ifndef slic3r_ShortestPath_hpp_ +#define slic3r_ShortestPath_hpp_ + +#include "libslic3r.h" +#include "ExtrusionEntity.hpp" +#include "Point.hpp" + +#include +#include + +namespace ClipperLib { class PolyNode; } + +namespace Slic3r { + +std::vector chain_points(const Points &points, Point *start_near = nullptr); + +std::vector> chain_extrusion_entities(std::vector &entities, const Point *start_near = nullptr); +void reorder_extrusion_entities(std::vector &entities, const std::vector> &chain); +void chain_and_reorder_extrusion_entities(std::vector &entities, const Point *start_near = nullptr); + +std::vector> chain_extrusion_paths(std::vector &extrusion_paths, const Point *start_near = nullptr); +void reorder_extrusion_paths(std::vector &extrusion_paths, std::vector> &chain); +void chain_and_reorder_extrusion_paths(std::vector &extrusion_paths, const Point *start_near = nullptr); + +Polylines chain_polylines(Polylines &&src, const Point *start_near = nullptr); +inline Polylines chain_polylines(const Polylines& src, const Point* start_near = nullptr) { Polylines tmp(src); return chain_polylines(std::move(tmp), start_near); } + +std::vector chain_clipper_polynodes(const Points &points, const std::vector &items); + +// 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); + + +} // namespace Slic3r + +#endif /* slic3r_ShortestPath_hpp_ */ diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 2c809dadf..6f5975286 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -228,41 +228,59 @@ std::vector layer_height_profile_from_ranges( // Based on the work of @platsch // Fill layer_height_profile by heights ensuring a prescribed maximum cusp height. -// @Deprecated not used anymore (see PrintObject::update_layer_height_profile) +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +std::vector layer_height_profile_adaptive(const SlicingParameters& slicing_params, + const ModelObject& object, float cusp_value) +#else std::vector layer_height_profile_adaptive( const SlicingParameters &slicing_params, const t_layer_config_ranges & /* layer_config_ranges */, const ModelVolumePtrs &volumes) +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE { +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + // 1) Initialize the SlicingAdaptive class with the object meshes. + SlicingAdaptive as; + as.set_slicing_parameters(slicing_params); + as.set_object(object); +#else // 1) Initialize the SlicingAdaptive class with the object meshes. SlicingAdaptive as; as.set_slicing_parameters(slicing_params); for (const ModelVolume *volume : volumes) if (volume->is_model_part()) as.add_mesh(&volume->mesh()); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + as.prepare(); // 2) Generate layers using the algorithm of @platsch // loop until we have at least one layer and the max slice_z reaches the object height - //FIXME make it configurable - // Cusp value: A maximum allowed distance from a corner of a rectangular extrusion to a chrodal line, in mm. - const coordf_t cusp_value = 0.2; // $self->config->get_value('cusp_value'); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + double cusp_value = 0.2; +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - std::vector layer_height_profile; - layer_height_profile.push_back(0.); + std::vector layer_height_profile; + layer_height_profile.push_back(0.0); layer_height_profile.push_back(slicing_params.first_object_layer_height); if (slicing_params.first_object_layer_height_fixed()) { layer_height_profile.push_back(slicing_params.first_object_layer_height); layer_height_profile.push_back(slicing_params.first_object_layer_height); } - coordf_t slice_z = slicing_params.first_object_layer_height; - coordf_t height = slicing_params.first_object_layer_height; + double slice_z = slicing_params.first_object_layer_height; int current_facet = 0; +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + while (slice_z <= slicing_params.object_print_z_height()) { + double height = slicing_params.max_layer_height; +#else + double height = slicing_params.first_object_layer_height; while ((slice_z - height) <= slicing_params.object_print_z_height()) { - height = 999; + height = 999.0; +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // Slic3r::debugf "\n Slice layer: %d\n", $id; // determine next layer height - coordf_t cusp_height = as.cusp_height(slice_z, cusp_value, current_facet); + double cusp_height = as.cusp_height((float)slice_z, cusp_value, current_facet); + // check for horizontal features and object size /* if($self->config->get_value('match_horizontal_surfaces')) { @@ -308,19 +326,115 @@ std::vector layer_height_profile_adaptive( layer_height_profile.push_back(slice_z); layer_height_profile.push_back(height); slice_z += height; +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE layer_height_profile.push_back(slice_z); layer_height_profile.push_back(height); +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE } - coordf_t last = std::max(slicing_params.first_object_layer_height, layer_height_profile[layer_height_profile.size() - 2]); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + double z_gap = slicing_params.object_print_z_height() - layer_height_profile[layer_height_profile.size() - 2]; + if (z_gap > 0.0) + { + layer_height_profile.push_back(slicing_params.object_print_z_height()); + layer_height_profile.push_back(clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, z_gap)); + } +#else + double last = std::max(slicing_params.first_object_layer_height, layer_height_profile[layer_height_profile.size() - 2]); layer_height_profile.push_back(last); layer_height_profile.push_back(slicing_params.first_object_layer_height); layer_height_profile.push_back(slicing_params.object_print_z_height()); layer_height_profile.push_back(slicing_params.first_object_layer_height); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE return layer_height_profile; } +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +std::vector smooth_height_profile(const std::vector& profile, const SlicingParameters& slicing_params, const HeightProfileSmoothingParams& smoothing_params) +{ + auto gauss_blur = [&slicing_params](const std::vector& profile, const HeightProfileSmoothingParams& smoothing_params) -> std::vector { + auto gauss_kernel = [] (unsigned int radius) -> std::vector { + unsigned int size = 2 * radius + 1; + std::vector ret; + ret.reserve(size); + + // Reworked from static inline int getGaussianKernelSize(float sigma) taken from opencv-4.1.2\modules\features2d\src\kaze\AKAZEFeatures.cpp + double sigma = 0.3 * (double)(radius - 1) + 0.8; + double two_sq_sigma = 2.0 * sigma * sigma; + double inv_root_two_pi_sq_sigma = 1.0 / ::sqrt(M_PI * two_sq_sigma); + + for (unsigned int i = 0; i < size; ++i) + { + double x = (double)i - (double)radius; + ret.push_back(inv_root_two_pi_sq_sigma * ::exp(-x * x / two_sq_sigma)); + } + + return ret; + }; + + // skip first layer ? + size_t skip_count = slicing_params.first_object_layer_height_fixed() ? 4 : 0; + + // not enough data to smmoth + if ((int)profile.size() - (int)skip_count < 6) + return profile; + + unsigned int radius = std::max(smoothing_params.radius, (unsigned int)1); + std::vector kernel = gauss_kernel(radius); + int two_radius = 2 * (int)radius; + + std::vector ret; + size_t size = profile.size(); + ret.reserve(size); + + // leave first layer untouched + for (size_t i = 0; i < skip_count; ++i) + { + ret.push_back(profile[i]); + } + + // smooth the rest of the profile by biasing a gaussian blur + // the bias moves the smoothed profile closer to the min_layer_height + double delta_h = slicing_params.max_layer_height - slicing_params.min_layer_height; + double inv_delta_h = (delta_h != 0.0) ? 1.0 / delta_h : 1.0; + + double max_dz_band = (double)radius * slicing_params.layer_height; + for (size_t i = skip_count; i < size; i += 2) + { + double zi = profile[i]; + double hi = profile[i + 1]; + ret.push_back(zi); + ret.push_back(0.0); + double& height = ret.back(); + int begin = std::max((int)i - two_radius, (int)skip_count); + int end = std::min((int)i + two_radius, (int)size - 2); + double weight_total = 0.0; + for (int j = begin; j <= end; j += 2) + { + int kernel_id = radius + (j - (int)i) / 2; + double dz = std::abs(zi - profile[j]); + if (dz * slicing_params.layer_height <= max_dz_band) + { + double dh = std::abs(slicing_params.max_layer_height - profile[j + 1]); + double weight = kernel[kernel_id] * sqrt(dh * inv_delta_h); + height += weight * profile[j + 1]; + weight_total += weight; + } + } + + height = clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, (weight_total != 0.0) ? height /= weight_total : hi); + if (smoothing_params.keep_min) + height = std::min(height, hi); + } + + return ret; + }; + + return gauss_blur(profile, smoothing_params); +} +#endif + void adjust_layer_height_profile( const SlicingParameters &slicing_params, std::vector &layer_height_profile, @@ -641,7 +755,11 @@ int generate_layer_height_texture( const Vec3i32 &color1 = palette_raw[idx1]; const Vec3i32 &color2 = palette_raw[idx2]; coordf_t z = cell_to_z * coordf_t(cell); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + assert((lo - EPSILON <= z) && (z <= hi + EPSILON)); +#else assert(z >= lo && z <= hi); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // Intensity profile to visualize the layers. coordf_t intensity = cos(M_PI * 0.7 * (mid - z) / h); // Color mapping from layer height to RGB. diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index 244d4f06b..d0ac69fb5 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -18,8 +18,12 @@ namespace Slic3r class PrintConfig; class PrintObjectConfig; +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +class ModelObject; +#else class ModelVolume; typedef std::vector ModelVolumePtrs; +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // Parameters to guide object slicing and support generation. // The slicing parameters account for a raft and whether the 1st object layer is printed with a normal or a bridging flow @@ -141,11 +145,29 @@ extern std::vector layer_height_profile_from_ranges( const SlicingParameters &slicing_params, const t_layer_config_ranges &layer_config_ranges); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +extern std::vector layer_height_profile_adaptive( + const SlicingParameters& slicing_params, + const ModelObject& object, float cusp_value); + +struct HeightProfileSmoothingParams +{ + unsigned int radius; + bool keep_min; + + HeightProfileSmoothingParams() : radius(5), keep_min(false) {} + HeightProfileSmoothingParams(unsigned int radius, bool keep_min) : radius(radius), keep_min(keep_min) {} +}; + +extern std::vector smooth_height_profile( + const std::vector& profile, const SlicingParameters& slicing_params, + const HeightProfileSmoothingParams& smoothing_params); +#else extern std::vector layer_height_profile_adaptive( const SlicingParameters &slicing_params, const t_layer_config_ranges &layer_config_ranges, const ModelVolumePtrs &volumes); - +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE enum LayerHeightEditActionType : unsigned int { LAYER_HEIGHT_EDIT_ACTION_INCREASE = 0, diff --git a/src/libslic3r/SlicingAdaptive.cpp b/src/libslic3r/SlicingAdaptive.cpp index ad03b550b..b6ebf1ac0 100644 --- a/src/libslic3r/SlicingAdaptive.cpp +++ b/src/libslic3r/SlicingAdaptive.cpp @@ -1,16 +1,22 @@ #include "libslic3r.h" +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +#include "Model.hpp" +#else #include "TriangleMesh.hpp" +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE #include "SlicingAdaptive.hpp" namespace Slic3r { +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void SlicingAdaptive::clear() { - m_meshes.clear(); + m_meshes.clear(); m_faces.clear(); m_face_normal_z.clear(); } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE std::pair face_z_span(const stl_facet *f) { @@ -21,38 +27,54 @@ std::pair face_z_span(const stl_facet *f) void SlicingAdaptive::prepare() { - // 1) Collect faces of all meshes. - int nfaces_total = 0; - for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + if (m_object == nullptr) + return; + + m_faces.clear(); + m_face_normal_z.clear(); + + m_mesh = m_object->raw_mesh(); + const ModelInstance* first_instance = m_object->instances.front(); + m_mesh.transform(first_instance->get_matrix(), first_instance->is_left_handed()); + + // 1) Collect faces from mesh. + m_faces.reserve(m_mesh.stl.stats.number_of_facets); + for (stl_facet& face : m_mesh.stl.facet_start) + { + face.normal.normalize(); + m_faces.emplace_back(&face); + } +#else + // 1) Collect faces of all meshes. + int nfaces_total = 0; + for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) nfaces_total += (*it_mesh)->stl.stats.number_of_facets; - m_faces.reserve(nfaces_total); - for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) - for (const stl_facet &face : (*it_mesh)->stl.facet_start) - m_faces.emplace_back(&face); + m_faces.reserve(nfaces_total); + for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) + for (const stl_facet& face : (*it_mesh)->stl.facet_start) + m_faces.emplace_back(&face); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // 2) Sort faces lexicographically by their Z span. - std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) { - std::pair span1 = face_z_span(f1); - std::pair span2 = face_z_span(f2); - return span1 < span2; - }); + std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) { return face_z_span(f1) < face_z_span(f2); }); // 3) Generate Z components of the facet normals. - m_face_normal_z.assign(m_faces.size(), 0.f); + m_face_normal_z.assign(m_faces.size(), 0.0f); for (size_t iface = 0; iface < m_faces.size(); ++ iface) m_face_normal_z[iface] = m_faces[iface]->normal(2); } float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet) { - float height = m_slicing_params.max_layer_height; + float height = (float)m_slicing_params.max_layer_height; bool first_hit = false; // find all facets intersecting the slice-layer int ordered_id = current_facet; for (; ordered_id < int(m_faces.size()); ++ ordered_id) { - std::pair zspan = face_z_span(m_faces[ordered_id]); - // facet's minimum is higher than slice_z -> end loop + std::pair zspan = face_z_span(m_faces[ordered_id]); + // facet's minimum is higher than slice_z -> end loop if (zspan.first >= z) break; // facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point @@ -61,14 +83,14 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet if (! first_hit) { first_hit = true; current_facet = ordered_id; - } + } // skip touching facets which could otherwise cause small cusp values if (zspan.second <= z + EPSILON) continue; // compute cusp-height for this facet and store minimum of all heights float normal_z = m_face_normal_z[ordered_id]; - height = std::min(height, (normal_z == 0.f) ? 9999.f : std::abs(cusp_value / normal_z)); - } + height = std::min(height, (normal_z == 0.0f) ? (float)m_slicing_params.max_layer_height : std::abs(cusp_value / normal_z)); + } } // lower height limit due to printer capabilities @@ -77,8 +99,8 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet // check for sloped facets inside the determined layer and correct height if necessary if (height > m_slicing_params.min_layer_height) { for (; ordered_id < int(m_faces.size()); ++ ordered_id) { - std::pair zspan = face_z_span(m_faces[ordered_id]); - // facet's minimum is higher than slice_z + height -> end loop + std::pair zspan = face_z_span(m_faces[ordered_id]); + // facet's minimum is higher than slice_z + height -> end loop if (zspan.first >= z + height) break; @@ -88,13 +110,13 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet // Compute cusp-height for this facet and check against height. float normal_z = m_face_normal_z[ordered_id]; - float cusp = (normal_z == 0) ? 9999 : abs(cusp_value / normal_z); - + float cusp = (normal_z == 0.0f) ? (float)m_slicing_params.max_layer_height : std::abs(cusp_value / normal_z); + float z_diff = zspan.first - z; // handle horizontal facets - if (m_face_normal_z[ordered_id] > 0.999) { - // Slic3r::debugf "cusp computation, height is reduced from %f", $height; + if (normal_z > 0.999f) { + // Slic3r::debugf "cusp computation, height is reduced from %f", $height; height = z_diff; // Slic3r::debugf "to %f due to near horizontal facet\n", $height; } else if (cusp > z_diff) { @@ -112,29 +134,30 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet // lower height limit due to printer capabilities again height = std::max(height, float(m_slicing_params.min_layer_height)); } - + // Slic3r::debugf "cusp computation, layer-bottom at z:%f, cusp_value:%f, resulting layer height:%f\n", unscale $z, $cusp_value, $height; return height; } +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // Returns the distance to the next horizontal facet in Z-dir // to consider horizontal object features in slice thickness float SlicingAdaptive::horizontal_facet_distance(float z) { for (size_t i = 0; i < m_faces.size(); ++ i) { - std::pair zspan = face_z_span(m_faces[i]); - // facet's minimum is higher than max forward distance -> end loop + std::pair zspan = face_z_span(m_faces[i]); + // facet's minimum is higher than max forward distance -> end loop if (zspan.first > z + m_slicing_params.max_layer_height) break; // min_z == max_z -> horizontal facet - if (zspan.first > z && zspan.first == zspan.second) + if ((zspan.first > z) && (zspan.first == zspan.second)) return zspan.first - z; } // objects maximum? - return (z + m_slicing_params.max_layer_height > m_slicing_params.object_print_z_height()) ? - std::max(m_slicing_params.object_print_z_height() - z, 0.f) : - m_slicing_params.max_layer_height; + return (z + (float)m_slicing_params.max_layer_height > (float)m_slicing_params.object_print_z_height()) ? + std::max((float)m_slicing_params.object_print_z_height() - z, 0.f) : (float)m_slicing_params.max_layer_height; } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE }; // namespace Slic3r diff --git a/src/libslic3r/SlicingAdaptive.hpp b/src/libslic3r/SlicingAdaptive.hpp index bfd081d81..1d2996986 100644 --- a/src/libslic3r/SlicingAdaptive.hpp +++ b/src/libslic3r/SlicingAdaptive.hpp @@ -5,29 +5,49 @@ #include "Slicing.hpp" #include "admesh/stl.h" +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +#include "TriangleMesh.hpp" +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE namespace Slic3r { +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +class ModelVolume; +#else class TriangleMesh; +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class SlicingAdaptive { public: - void clear(); - void set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; } - void add_mesh(const TriangleMesh *mesh) { m_meshes.push_back(mesh); } - void prepare(); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void clear(); +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; } +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void set_object(const ModelObject& object) { m_object = &object; } +#else + void add_mesh(const TriangleMesh* mesh) { m_meshes.push_back(mesh); } +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void prepare(); float cusp_height(float z, float cusp_value, int ¤t_facet); - float horizontal_facet_distance(float z); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + float horizontal_facet_distance(float z); +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE protected: SlicingParameters m_slicing_params; - std::vector m_meshes; - // Collected faces of all meshes, sorted by raising Z of the bottom most face. +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + const ModelObject* m_object; + TriangleMesh m_mesh; +#else + std::vector m_meshes; +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + // Collected faces of all meshes, sorted by raising Z of the bottom most face. std::vector m_faces; - // Z component of face normals, normalized. + // Z component of face normals, normalized. std::vector m_face_normal_z; }; diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 60c31d8e9..d7042ffa4 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -446,8 +446,8 @@ Polygons collect_region_slices_by_type(const Layer &layer, SurfaceType surface_t Polygons collect_slices_outer(const Layer &layer) { Polygons out; - out.reserve(out.size() + layer.slices.expolygons.size()); - for (const ExPolygon &expoly : layer.slices.expolygons) + out.reserve(out.size() + layer.slices.size()); + for (const ExPolygon &expoly : layer.slices) out.emplace_back(expoly.contour); return out; } @@ -905,9 +905,13 @@ namespace SupportMaterialInternal { polyline.extend_start(fw); polyline.extend_end(fw); // Is the straight perimeter segment supported at both sides? - if (lower_layer.slices.contains(polyline.first_point()) && lower_layer.slices.contains(polyline.last_point())) + for (size_t i = 0; i < lower_layer.slices.size(); ++ i) + if (lower_layer.slices_bboxes[i].contains(polyline.first_point()) && lower_layer.slices_bboxes[i].contains(polyline.last_point()) && + lower_layer.slices[i].contains(polyline.first_point()) && lower_layer.slices[i].contains(polyline.last_point())) { // Offset a polyline into a thick line. polygons_append(bridges, offset(polyline, 0.5f * w + 10.f)); + break; + } } bridges = union_(bridges); } @@ -921,7 +925,7 @@ namespace SupportMaterialInternal { //FIXME add supports at regular intervals to support long bridges! bridges = diff(bridges, // Offset unsupported edges into polygons. - offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)); + offset(layerm->unsupported_bridge_edges, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)); // Remove bridged areas from the supported areas. contact_polygons = diff(contact_polygons, bridges, true); } @@ -992,7 +996,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ // inflate the polygons over and over. Polygons &covered = buildplate_covered[layer_id]; covered = buildplate_covered[layer_id - 1]; - polygons_append(covered, offset(lower_layer.slices.expolygons, scale_(0.01))); + polygons_append(covered, offset(lower_layer.slices, scale_(0.01))); covered = union_(covered, false); // don't apply the safety offset. } } @@ -1021,7 +1025,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ Polygons contact_polygons; Polygons slices_margin_cached; double slices_margin_cached_offset = -1.; - Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id - 1]->slices.expolygons); + Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id-1]->slices); // Offset of the lower layer, to trim the support polygons with to calculate dense supports. double no_interface_offset = 0.; if (layer_id == 0) { @@ -1160,7 +1164,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ slices_margin_cached_offset = slices_margin_offset; slices_margin_cached = (slices_margin_offset == 0.f) ? lower_layer_polygons : - offset2(to_polygons(lower_layer.slices.expolygons), - no_interface_offset * 0.5, slices_margin_offset + no_interface_offset * 0.5, SUPPORT_SURFACES_OFFSET_PARAMETERS); + offset2(to_polygons(lower_layer.slices), - no_interface_offset * 0.5, slices_margin_offset + no_interface_offset * 0.5, SUPPORT_SURFACES_OFFSET_PARAMETERS); if (! buildplate_covered.empty()) { // Trim the inflated contact surfaces by the top surfaces as well. polygons_append(slices_margin_cached, buildplate_covered[layer_id]); @@ -1469,7 +1473,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta svg.draw(union_ex(top, false), "blue", 0.5f); svg.draw(union_ex(projection_raw, true), "red", 0.5f); svg.draw_outline(union_ex(projection_raw, true), "red", "blue", scale_(0.1f)); - svg.draw(layer.slices.expolygons, "green", 0.5f); + svg.draw(layer.slices, "green", 0.5f); } #endif /* SLIC3R_DEBUG */ @@ -1571,7 +1575,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta task_group.run([this, &projection, &projection_raw, &layer, &layer_support_area, layer_id] { // Remove the areas that touched from the projection that will continue on next, lower, top surfaces. // Polygons trimming = union_(to_polygons(layer.slices.expolygons), touching, true); - Polygons trimming = offset(layer.slices.expolygons, double(SCALED_EPSILON)); + Polygons trimming = offset(layer.slices, double(SCALED_EPSILON)); projection = diff(projection_raw, trimming, false); #ifdef SLIC3R_DEBUG { @@ -2110,7 +2114,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( const Layer &object_layer = *object.layers()[i]; if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON) break; - polygons_append(polygons_trimming, offset(object_layer.slices.expolygons, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS)); + polygons_append(polygons_trimming, offset(object_layer.slices, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS)); } if (!m_slicing_params.soluble_interface) { // Collect all bottom surfaces, which will be extruded with a bridging flow. @@ -2136,7 +2140,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( } // $layer->slices contains the full shape of layer, thus including // perimeter's width. $support contains the full shape of support - // material, thus including the width of its foremost extrusion. + // material, thus including the width of its foremost extrusion. // We leave a gap equal to a full extrusion width. support_layer.polygons = diff(support_layer.polygons, polygons_trimming); } @@ -2225,7 +2229,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf // Expand the bases of the support columns in the 1st layer. columns_base->polygons = diff( offset(columns_base->polygons, inflate_factor_1st_layer), - offset(m_object->layers().front()->slices.expolygons, (float)scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS)); + offset(m_object->layers().front()->slices, (float)scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS)); if (contacts != nullptr) columns_base->polygons = diff(columns_base->polygons, interface_polygons); } @@ -2966,20 +2970,13 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Prepare fillers. SupportMaterialPattern support_pattern = m_object_config->support_material_pattern; bool with_sheath = m_object_config->support_material_with_sheath; - InfillPattern infill_pattern; + InfillPattern infill_pattern = (support_pattern == smpHoneycomb ? ipHoneycomb : ipRectilinear); std::vector angles; angles.push_back(base_angle); - switch (support_pattern) { - case smpRectilinearGrid: + + if (support_pattern == smpRectilinearGrid) angles.push_back(interface_angle); - // fall through - case smpRectilinear: - infill_pattern = ipRectilinear; - break; - case smpHoneycomb: - infill_pattern = ipHoneycomb; - break; - } + InfillPattern interface_pattern = m_object_config->support_material_interface_pattern; BoundingBox bbox_object(Point(-scale_(1.), -scale_(1.0)), Point(scale_(1.), scale_(1.))); @@ -3277,7 +3274,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( filler->link_max_length = coord_t(scale_(spacing * link_max_length_factor / density)); } // use the proper spacing for first layer as we don't need to align - // its pattern to the other layers + // its pattern to the other layers flow = m_first_layer_flow; spacing = flow.spacing(); } else if (with_sheath) { diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 6a4dc652a..c2eb7b166 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -31,6 +31,20 @@ // Use wxDataViewRender instead of wxDataViewCustomRenderer #define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0_ALPHA1) - //#define DEBUG_EXTRUSION_OUTPUT 0 + +//==================== +// 2.2.0.alpha1 techs +//==================== +#define ENABLE_2_2_0_ALPHA1 0 + +// Enable thumbnail generator +// When removing this technology, remove it also from stable branch, +// where it has been partially copied for patch 2.1.1 +#define ENABLE_THUMBNAIL_GENERATOR (1 && ENABLE_2_2_0_ALPHA1) +#define ENABLE_THUMBNAIL_GENERATOR_DEBUG (0 && ENABLE_THUMBNAIL_GENERATOR) + +// Enable adaptive layer height profile +#define ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE (1 && ENABLE_2_2_0_ALPHA1) + #endif // _technologies_h_ diff --git a/src/libslic3r/Tesselate.hpp b/src/libslic3r/Tesselate.hpp index 02e86eb33..2dbe6caa1 100644 --- a/src/libslic3r/Tesselate.hpp +++ b/src/libslic3r/Tesselate.hpp @@ -10,12 +10,15 @@ namespace Slic3r { class ExPolygon; typedef std::vector ExPolygons; -extern std::vector triangulate_expolygon_3d (const ExPolygon &poly, coordf_t z = 0, bool flip = false); -extern std::vector triangulate_expolygons_3d(const ExPolygons &polys, coordf_t z = 0, bool flip = false); -extern std::vector triangulate_expolygon_2d (const ExPolygon &poly, bool flip = false); -extern std::vector triangulate_expolygons_2d(const ExPolygons &polys, bool flip = false); -extern std::vector triangulate_expolygon_2f (const ExPolygon &poly, bool flip = false); -extern std::vector triangulate_expolygons_2f(const ExPolygons &polys, bool flip = false); +const bool constexpr NORMALS_UP = false; +const bool constexpr NORMALS_DOWN = true; + +extern std::vector triangulate_expolygon_3d (const ExPolygon &poly, coordf_t z = 0, bool flip = NORMALS_UP); +extern std::vector triangulate_expolygons_3d(const ExPolygons &polys, coordf_t z = 0, bool flip = NORMALS_UP); +extern std::vector triangulate_expolygon_2d (const ExPolygon &poly, bool flip = NORMALS_UP); +extern std::vector triangulate_expolygons_2d(const ExPolygons &polys, bool flip = NORMALS_UP); +extern std::vector triangulate_expolygon_2f (const ExPolygon &poly, bool flip = NORMALS_UP); +extern std::vector triangulate_expolygons_2f(const ExPolygons &polys, bool flip = NORMALS_UP); } // namespace Slic3r diff --git a/src/libslic3r/Time.cpp b/src/libslic3r/Time.cpp index 1f65189b8..8faa14ade 100644 --- a/src/libslic3r/Time.cpp +++ b/src/libslic3r/Time.cpp @@ -3,116 +3,232 @@ #include #include #include +#include +#include +#include -//#include -//#include +#ifdef _MSC_VER +#include +#endif - -#ifdef WIN32 - #define WIN32_LEAN_AND_MEAN - #include - #undef WIN32_LEAN_AND_MEAN -#endif /* WIN32 */ +// #include "libslic3r/Utils.hpp" namespace Slic3r { namespace Utils { -namespace { +// "YYYY-MM-DD at HH:MM::SS [UTC]" +// If TimeZone::utc is used with the conversion functions, it will append the +// UTC letters to the end. +static const constexpr char *const SLICER_UTC_TIME_FMT = "%Y-%m-%d at %T"; -// FIXME: after we switch to gcc > 4.9 on the build server, please remove me -#if defined(__GNUC__) && __GNUC__ <= 4 -std::string put_time(const std::tm *tm, const char *fmt) +// ISO8601Z representation of time, without time zone info +static const constexpr char *const ISO8601Z_TIME_FMT = "%Y%m%dT%H%M%SZ"; + +static const char * get_fmtstr(TimeFormat fmt) { - static const constexpr int MAX_CHARS = 200; - char out[MAX_CHARS]; - std::strftime(out, MAX_CHARS, fmt, tm); - return out; + switch (fmt) { + case TimeFormat::gcode: return SLICER_UTC_TIME_FMT; + case TimeFormat::iso8601Z: return ISO8601Z_TIME_FMT; + } + + return ""; } -#else -auto put_time(const std::tm *tm, const char *fmt) -> decltype (std::put_time(tm, fmt)) + +namespace __get_put_time_emulation { +// FIXME: Implementations with the cpp11 put_time and get_time either not +// compile or do not pass the tests on the build server. If we switch to newer +// compilers, this namespace can be deleted with all its content. + +#ifdef _MSC_VER +// VS2019 implementation fails with ISO8601Z_TIME_FMT. +// VS2019 does not have std::strptime either. See bug: +// https://developercommunity.visualstudio.com/content/problem/140618/c-stdget-time-not-parsing-correctly.html + +static const std::map sscanf_fmt_map = { + {SLICER_UTC_TIME_FMT, "%04d-%02d-%02d at %02d:%02d:%02d"}, + {std::string(SLICER_UTC_TIME_FMT) + " UTC", "%04d-%02d-%02d at %02d:%02d:%02d UTC"}, + {ISO8601Z_TIME_FMT, "%04d%02d%02dT%02d%02d%02dZ"} +}; + +static const char * strptime(const char *str, const char *const fmt, std::tm *tms) { - return std::put_time(tm, fmt); + auto it = sscanf_fmt_map.find(fmt); + if (it == sscanf_fmt_map.end()) return nullptr; + + int y, M, d, h, m, s; + if (sscanf(str, it->second.c_str(), &y, &M, &d, &h, &m, &s) != 6) + return nullptr; + + tms->tm_year = y - 1900; // Year since 1900 + tms->tm_mon = M - 1; // 0-11 + tms->tm_mday = d; // 1-31 + tms->tm_hour = h; // 0-23 + tms->tm_min = m; // 0-59 + tms->tm_sec = s; // 0-61 (0-60 in C++11) + + return str; // WARN strptime return val should point after the parsed string } #endif +template +struct GetPutTimeReturnT { + Ttm *tms; + const char *fmt; + GetPutTimeReturnT(Ttm *_tms, const char *_fmt): tms(_tms), fmt(_fmt) {} +}; + +using GetTimeReturnT = GetPutTimeReturnT; +using PutTimeReturnT = GetPutTimeReturnT; + +std::ostream &operator<<(std::ostream &stream, PutTimeReturnT &&pt) +{ + static const constexpr int MAX_CHARS = 200; + char _out[MAX_CHARS]; + strftime(_out, MAX_CHARS, pt.fmt, pt.tms); + stream << _out; + return stream; } -time_t parse_time_ISO8601Z(const std::string &sdate) +inline PutTimeReturnT put_time(const std::tm *tms, const char *fmt) { - int y, M, d, h, m, s; - if (sscanf(sdate.c_str(), "%04d%02d%02dT%02d%02d%02dZ", &y, &M, &d, &h, &m, &s) != 6) - return time_t(-1); - struct tm tms; - tms.tm_year = y - 1900; // Year since 1900 - tms.tm_mon = M - 1; // 0-11 - tms.tm_mday = d; // 1-31 - tms.tm_hour = h; // 0-23 - tms.tm_min = m; // 0-59 - tms.tm_sec = s; // 0-61 (0-60 in C++11) + return {tms, fmt}; +} + +std::istream &operator>>(std::istream &stream, GetTimeReturnT &>) +{ + std::string line; + std::getline(stream, line); + + if (strptime(line.c_str(), gt.fmt, gt.tms) == nullptr) + stream.setstate(std::ios::failbit); + + return stream; +} + +inline GetTimeReturnT get_time(std::tm *tms, const char *fmt) +{ + return {tms, fmt}; +} + +} + +namespace { + +// Platform independent versions of gmtime and localtime. Completely thread +// safe only on Linux. MSVC gtime_s and localtime_s sets global errno thus not +// thread safe. +struct std::tm * _gmtime_r(const time_t *timep, struct tm *result) +{ + assert(timep != nullptr && result != nullptr); #ifdef WIN32 - return _mkgmtime(&tms); + time_t t = *timep; + gmtime_s(result, &t); + return result; +#else + return gmtime_r(timep, result); +#endif +} + +struct std::tm * _localtime_r(const time_t *timep, struct tm *result) +{ + assert(timep != nullptr && result != nullptr); +#ifdef WIN32 + // Converts a time_t time value to a tm structure, and corrects for the + // local time zone. + time_t t = *timep; + localtime_s(result, &t); + return result; +#else + return localtime_r(timep, result); +#endif +} + +time_t _mktime(const struct std::tm *tms) +{ + assert(tms != nullptr); + std::tm _tms = *tms; + return mktime(&_tms); +} + +time_t _timegm(const struct std::tm *tms) +{ + std::tm _tms = *tms; +#ifdef WIN32 + return _mkgmtime(&_tms); #else /* WIN32 */ - return timegm(&tms); + return timegm(&_tms); #endif /* WIN32 */ } -std::string format_time_ISO8601Z(time_t time) +std::string process_format(const char *fmt, TimeZone zone) { - struct tm tms; -#ifdef WIN32 - gmtime_s(&tms, &time); -#else - gmtime_r(&time, &tms); -#endif - char buf[128]; - sprintf(buf, "%04d%02d%02dT%02d%02d%02dZ", - tms.tm_year + 1900, - tms.tm_mon + 1, - tms.tm_mday, - tms.tm_hour, - tms.tm_min, - tms.tm_sec); - return buf; + std::string fmtstr(fmt); + + if (fmtstr == SLICER_UTC_TIME_FMT && zone == TimeZone::utc) + fmtstr += " UTC"; + + return fmtstr; } -std::string format_local_date_time(time_t time) -{ - struct tm tms; -#ifdef WIN32 - // Converts a time_t time value to a tm structure, and corrects for the local time zone. - localtime_s(&tms, &time); -#else - localtime_r(&time, &tms); -#endif - char buf[80]; - strftime(buf, 80, "%x %X", &tms); - return buf; -} +} // namespace time_t get_current_time_utc() -{ +{ using clk = std::chrono::system_clock; return clk::to_time_t(clk::now()); } -static std::string tm2str(const std::tm *tm, const char *fmt) +static std::string tm2str(const std::tm *tms, const char *fmt) { std::stringstream ss; - ss << put_time(tm, fmt); + ss.imbue(std::locale("C")); + ss << __get_put_time_emulation::put_time(tms, fmt); return ss.str(); } -std::string time2str(const time_t &t, TimeZone zone, const char *fmt) +std::string time2str(const time_t &t, TimeZone zone, TimeFormat fmt) { std::string ret; - + std::tm tms = {}; + tms.tm_isdst = -1; + std::string fmtstr = process_format(get_fmtstr(fmt), zone); + switch (zone) { - case TimeZone::local: ret = tm2str(std::localtime(&t), fmt); break; - case TimeZone::utc: ret = tm2str(std::gmtime(&t), fmt) + " UTC"; break; + case TimeZone::local: + ret = tm2str(_localtime_r(&t, &tms), fmtstr.c_str()); break; + case TimeZone::utc: + ret = tm2str(_gmtime_r(&t, &tms), fmtstr.c_str()); break; } - + return ret; } +static time_t str2time(std::istream &stream, TimeZone zone, const char *fmt) +{ + std::tm tms = {}; + tms.tm_isdst = -1; + + stream >> __get_put_time_emulation::get_time(&tms, fmt); + time_t ret = time_t(-1); + + switch (zone) { + case TimeZone::local: ret = _mktime(&tms); break; + case TimeZone::utc: ret = _timegm(&tms); break; + } + + if (stream.fail() || ret < time_t(0)) ret = time_t(-1); + + return ret; +} + +time_t str2time(const std::string &str, TimeZone zone, TimeFormat fmt) +{ + std::string fmtstr = process_format(get_fmtstr(fmt), zone).c_str(); + std::stringstream ss(str); + + ss.imbue(std::locale("C")); + return str2time(ss, zone, fmtstr.c_str()); +} + }; // namespace Utils }; // namespace Slic3r diff --git a/src/libslic3r/Time.hpp b/src/libslic3r/Time.hpp index b314e47f7..c03251986 100644 --- a/src/libslic3r/Time.hpp +++ b/src/libslic3r/Time.hpp @@ -7,41 +7,61 @@ namespace Slic3r { namespace Utils { -// Utilities to convert an UTC time_t to/from an ISO8601 time format, -// useful for putting timestamps into file and directory names. -// Returns (time_t)-1 on error. -time_t parse_time_ISO8601Z(const std::string &s); -std::string format_time_ISO8601Z(time_t time); - -// Format the date and time from an UTC time according to the active locales and a local time zone. -// TODO: make sure time2str is a suitable replacement -std::string format_local_date_time(time_t time); - -// There is no gmtime() on windows. +// Should be thread safe. time_t get_current_time_utc(); -const constexpr char *const SLIC3R_TIME_FMT = "%Y-%m-%d at %T"; - enum class TimeZone { local, utc }; +enum class TimeFormat { gcode, iso8601Z }; -std::string time2str(const time_t &t, TimeZone zone, const char *fmt = SLIC3R_TIME_FMT); +// time_t to string functions... -inline std::string current_time2str(TimeZone zone, const char *fmt = SLIC3R_TIME_FMT) +std::string time2str(const time_t &t, TimeZone zone, TimeFormat fmt); + +inline std::string time2str(TimeZone zone, TimeFormat fmt) { return time2str(get_current_time_utc(), zone, fmt); } -inline std::string current_local_time2str(const char * fmt = SLIC3R_TIME_FMT) +inline std::string utc_timestamp(time_t t) { - return current_time2str(TimeZone::local, fmt); + return time2str(t, TimeZone::utc, TimeFormat::gcode); } -inline std::string current_utc_time2str(const char * fmt = SLIC3R_TIME_FMT) +inline std::string utc_timestamp() { - return current_time2str(TimeZone::utc, fmt); + return utc_timestamp(get_current_time_utc()); } -}; // namespace Utils -}; // namespace Slic3r +// String to time_t function. Returns time_t(-1) if fails to parse the input. +time_t str2time(const std::string &str, TimeZone zone, TimeFormat fmt); + + +// ///////////////////////////////////////////////////////////////////////////// +// Utilities to convert an UTC time_t to/from an ISO8601 time format, +// useful for putting timestamps into file and directory names. +// Returns (time_t)-1 on error. + +// Use these functions to convert safely to and from the ISO8601 format on +// all platforms + +inline std::string iso_utc_timestamp(time_t t) +{ + return time2str(t, TimeZone::utc, TimeFormat::iso8601Z); +} + +inline std::string iso_utc_timestamp() +{ + return iso_utc_timestamp(get_current_time_utc()); +} + +inline time_t parse_iso_utc_timestamp(const std::string &str) +{ + return str2time(str, TimeZone::utc, TimeFormat::iso8601Z); +} + +// ///////////////////////////////////////////////////////////////////////////// + +} // namespace Utils +} // namespace Slic3r #endif /* slic3r_Utils_Time_hpp_ */ diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 9d2a14e32..e0f5fb433 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -236,7 +236,7 @@ bool TriangleMesh::needed_repair() const || this->stl.stats.backwards_edges > 0; } -void TriangleMesh::WriteOBJFile(const char* output_file) +void TriangleMesh::WriteOBJFile(const char* output_file) const { its_write_obj(this->its, output_file); } @@ -593,6 +593,17 @@ TriangleMesh TriangleMesh::convex_hull_3d() const return output_mesh; } +std::vector TriangleMesh::slice(const std::vector &z) +{ + // convert doubles to floats + 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, &layers, []() {}); + return layers; +} + void TriangleMesh::require_shared_vertices() { BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - start"; @@ -1885,7 +1896,8 @@ Pointf3s TriangleMesh::vertices() } // Generate the vertex list for a cube solid of arbitrary size in X/Y/Z. -TriangleMesh make_cube(double x, double y, double z) { +TriangleMesh make_cube(double x, double y, double z) +{ Vec3d pv[8] = { Vec3d(x, y, 0), Vec3d(x, 0, 0), Vec3d(0, 0, 0), Vec3d(0, y, 0), Vec3d(x, y, z), Vec3d(0, y, z), @@ -1902,6 +1914,7 @@ TriangleMesh make_cube(double x, double y, double z) { Pointf3s vertices(&pv[0], &pv[0]+8); TriangleMesh mesh(vertices ,facets); + mesh.repair(); return mesh; } @@ -1946,7 +1959,9 @@ TriangleMesh make_cylinder(double r, double h, double fa) facets.emplace_back(Vec3i32(id, 2, 3)); facets.emplace_back(Vec3i32(id, id - 1, 2)); - return TriangleMesh(std::move(vertices), std::move(facets)); + TriangleMesh mesh(std::move(vertices), std::move(facets)); + mesh.repair(); + return mesh; } // Generates mesh for a sphere centered about the origin, using the generated angle @@ -2002,7 +2017,9 @@ TriangleMesh make_sphere(double radius, double fa) k2 = k2_next; } } - return TriangleMesh(std::move(vertices), std::move(facets)); + TriangleMesh mesh(std::move(vertices), std::move(facets)); + mesh.repair(); + return mesh; } } diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 460ece64c..92500eb06 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -31,7 +31,7 @@ public: float volume(); void check_topology(); bool is_manifold() const { return this->stl.stats.connected_facets_3_edge == (int)this->stl.stats.number_of_facets; } - void WriteOBJFile(const char* output_file); + void WriteOBJFile(const char* output_file) const; void scale(float factor); void scale(const Vec3d &versor); void translate(float x, float y, float z); @@ -58,8 +58,14 @@ public: BoundingBoxf3 bounding_box() const; // Returns the bbox of this TriangleMesh transformed by the given transformation BoundingBoxf3 transformed_bounding_box(const Transform3d &trafo) const; + // Return the size of the mesh in coordinates. + Vec3d size() const { return stl.stats.size.cast(); } + /// Return the center of the related bounding box. + Vec3d center() const { return this->bounding_box().center(); } // Returns the convex hull of this TriangleMesh TriangleMesh convex_hull_3d() const; + // Slice this mesh at the provided Z levels and return the vector + std::vector slice(const std::vector& z); void reset_repair_stats(); bool needed_repair() const; void require_shared_vertices(); diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 5d847573d..e5fae485a 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "libslic3r.h" @@ -164,6 +165,65 @@ template size_t next_highest_power_of_2(T v, return next_highest_power_of_2(uint32_t(v)); } +template +inline INDEX_TYPE prev_idx_modulo(INDEX_TYPE idx, const INDEX_TYPE count) +{ + if (idx == 0) + idx = count; + return -- idx; +} + +template +inline INDEX_TYPE next_idx_modulo(INDEX_TYPE idx, const INDEX_TYPE count) +{ + if (++ idx == count) + idx = 0; + return idx; +} + +template +inline typename CONTAINER_TYPE::size_type prev_idx_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container) +{ + return prev_idx_modulo(idx, container.size()); +} + +template +inline typename CONTAINER_TYPE::size_type next_idx_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container) +{ + return next_idx_modulo(idx, container.size()); +} + +template +inline const typename CONTAINER_TYPE::value_type& prev_value_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container) +{ + return container[prev_idx_modulo(idx, container.size())]; +} + +template +inline typename CONTAINER_TYPE::value_type& prev_value_modulo(typename CONTAINER_TYPE::size_type idx, CONTAINER_TYPE &container) +{ + return container[prev_idx_modulo(idx, container.size())]; +} + +template +inline const typename CONTAINER_TYPE::value_type& next_value_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container) +{ + return container[next_idx_modulo(idx, container.size())]; +} + +template +inline typename CONTAINER_TYPE::value_type& next_value_modulo(typename CONTAINER_TYPE::size_type idx, CONTAINER_TYPE &container) +{ + return container[next_idx_modulo(idx, container.size())]; +} + +template +inline T exchange(T& obj, U&& new_value) +{ + T old_value = std::move(obj); + obj = std::forward(new_value); + return old_value; +} extern std::string xml_escape(std::string text); diff --git a/src/libslic3r/Zipper.cpp b/src/libslic3r/Zipper.cpp index 348be49cc..a5b53584d 100644 --- a/src/libslic3r/Zipper.cpp +++ b/src/libslic3r/Zipper.cpp @@ -217,4 +217,9 @@ void Zipper::finalize() m_impl->blow_up(); } +const std::string &Zipper::get_filename() const +{ + return m_impl->m_zipname; +} + } diff --git a/src/libslic3r/Zipper.hpp b/src/libslic3r/Zipper.hpp index a574de959..be1e69b5c 100644 --- a/src/libslic3r/Zipper.hpp +++ b/src/libslic3r/Zipper.hpp @@ -83,6 +83,8 @@ public: void finish_entry(); void finalize(); + + const std::string & get_filename() const; }; diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 895efdb4d..678ad9ed2 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -424,14 +424,19 @@ int copy_file(const std::string &from, const std::string &to) static const auto perms = boost::filesystem::owner_read | boost::filesystem::owner_write | boost::filesystem::group_read | boost::filesystem::others_read; // aka 644 // Make sure the file has correct permission both before and after we copy over it. - try { - if (boost::filesystem::exists(target)) - boost::filesystem::permissions(target, perms); - boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists); - boost::filesystem::permissions(target, perms); - } catch (std::exception & /* ex */) { + // NOTE: error_code variants are used here to supress expception throwing. + // Error code of permission() calls is ignored on purpose - if they fail, + // the copy_file() function will fail appropriately and we don't want the permission() + // calls to cause needless failures on permissionless filesystems (ie. FATs on SD cards etc.) + // or when the target file doesn't exist. + boost::system::error_code ec; + boost::filesystem::permissions(target, perms, ec); + boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists, ec); + if (ec) { return -1; } + boost::filesystem::permissions(target, perms, ec); + return 0; } @@ -543,7 +548,7 @@ std::string string_printf(const char *format, ...) std::string header_slic3r_generated() { - return std::string("generated by " SLIC3R_APP_NAME " " SLIC3R_VERSION " on " ) + Utils::current_utc_time2str(); + return std::string("generated by " SLIC3R_APP_NAME " " SLIC3R_VERSION " on " ) + Utils::utc_timestamp(); } unsigned get_current_pid() diff --git a/src/platform/osx/Info.plist.in b/src/platform/osx/Info.plist.in index f4e298180..d09f015b9 100644 --- a/src/platform/osx/Info.plist.in +++ b/src/platform/osx/Info.plist.in @@ -116,5 +116,7 @@ NSApplication NSHighResolutionCapable + NSRequiresAquaSystemAppearance + diff --git a/src/qhull/CMakeLists.txt b/src/qhull/CMakeLists.txt index 9ca0bdff2..ab9aba9af 100644 --- a/src/qhull/CMakeLists.txt +++ b/src/qhull/CMakeLists.txt @@ -18,11 +18,13 @@ if(Qhull_FOUND) message(STATUS "Using qhull from system.") if(SLIC3R_STATIC) + slic3r_remap_configs("Qhull::qhullcpp;Qhull::qhullstatic_r" RelWithDebInfo Release) target_link_libraries(qhull INTERFACE Qhull::qhullcpp Qhull::qhullstatic_r) else() + slic3r_remap_configs("Qhull::qhullcpp;Qhull::qhull_r" RelWithDebInfo Release) target_link_libraries(qhull INTERFACE Qhull::qhullcpp Qhull::qhull_r) endif() - + else(Qhull_FOUND) project(qhull) diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 17b76e629..7f06cac29 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -105,6 +105,8 @@ set(SLIC3R_GUI_SOURCES GUI/Camera.hpp GUI/wxExtensions.cpp GUI/wxExtensions.hpp + GUI/ExtruderSequenceDialog.cpp + GUI/ExtruderSequenceDialog.hpp GUI/WipeTowerDialog.cpp GUI/WipeTowerDialog.hpp GUI/RammingChart.cpp @@ -136,6 +138,8 @@ set(SLIC3R_GUI_SOURCES GUI/ProgressStatusBar.cpp GUI/PrintHostDialogs.cpp GUI/PrintHostDialogs.hpp + GUI/Mouse3DController.cpp + GUI/Mouse3DController.hpp Utils/Http.cpp Utils/Http.hpp Utils/FixModelByWin10.cpp @@ -144,6 +148,8 @@ set(SLIC3R_GUI_SOURCES Utils/OctoPrint.hpp Utils/Duet.cpp Utils/Duet.hpp + Utils/FlashAir.cpp + Utils/FlashAir.hpp Utils/PrintHost.cpp Utils/PrintHost.hpp Utils/Bonjour.cpp @@ -154,6 +160,7 @@ set(SLIC3R_GUI_SOURCES Utils/UndoRedo.hpp Utils/HexFile.cpp Utils/HexFile.hpp + Utils/Thread.hpp ) if (APPLE) @@ -167,7 +174,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) encoding_check(libslic3r_gui) -target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES}) +target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES} hidapi) if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE) endif () diff --git a/src/slic3r/Config/Snapshot.cpp b/src/slic3r/Config/Snapshot.cpp index 622b31a17..fa756f49d 100644 --- a/src/slic3r/Config/Snapshot.cpp +++ b/src/slic3r/Config/Snapshot.cpp @@ -66,7 +66,7 @@ void Snapshot::load_ini(const std::string &path) if (kvp.first == "id") this->id = kvp.second.data(); else if (kvp.first == "time_captured") { - this->time_captured = Slic3r::Utils::parse_time_ISO8601Z(kvp.second.data()); + this->time_captured = Slic3r::Utils::parse_iso_utc_timestamp(kvp.second.data()); if (this->time_captured == (time_t)-1) throw_on_parse_error("invalid timestamp"); } else if (kvp.first == "slic3r_version_captured") { @@ -165,7 +165,7 @@ void Snapshot::save_ini(const std::string &path) // Export the common "snapshot". c << std::endl << "[snapshot]" << std::endl; c << "id = " << this->id << std::endl; - c << "time_captured = " << Slic3r::Utils::format_time_ISO8601Z(this->time_captured) << std::endl; + c << "time_captured = " << Slic3r::Utils::iso_utc_timestamp(this->time_captured) << std::endl; c << "slic3r_version_captured = " << this->slic3r_version_captured.to_string() << std::endl; c << "comment = " << this->comment << std::endl; c << "reason = " << reason_string(this->reason) << std::endl; @@ -342,7 +342,7 @@ static void copy_config_dir_single_level(const boost::filesystem::path &path_src ! boost::filesystem::create_directory(path_dst)) throw std::runtime_error(std::string("Slic3r was unable to create a directory at ") + path_dst.string()); - for (auto &dir_entry : boost::filesystem::directory_iterator(path_src)) + for (auto &dir_entry : boost::filesystem::directory_iterator(path_src)) if (Slic3r::is_ini_file(dir_entry)) boost::filesystem::copy_file(dir_entry.path(), path_dst / dir_entry.path().filename(), boost::filesystem::copy_option::overwrite_if_exists); } @@ -351,7 +351,7 @@ static void delete_existing_ini_files(const boost::filesystem::path &path) { if (! boost::filesystem::is_directory(path)) return; - for (auto &dir_entry : boost::filesystem::directory_iterator(path)) + for (auto &dir_entry : boost::filesystem::directory_iterator(path)) if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini")) boost::filesystem::remove(dir_entry.path()); } @@ -365,7 +365,7 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot: Snapshot snapshot; // Snapshot header. snapshot.time_captured = Slic3r::Utils::get_current_time_utc(); - snapshot.id = Slic3r::Utils::format_time_ISO8601Z(snapshot.time_captured); + snapshot.id = Slic3r::Utils::iso_utc_timestamp(snapshot.time_captured); snapshot.slic3r_version_captured = Slic3r::SEMVER; snapshot.comment = comment; snapshot.reason = reason; @@ -378,7 +378,7 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot: sprintf(name, "filament_%u", i); if (! app_config.has("presets", name)) break; - snapshot.filaments.emplace_back(app_config.get("presets", name)); + snapshot.filaments.emplace_back(app_config.get("presets", name)); } // Vendor specific config bundles and installed printers. for (const std::pair>> &vendor : app_config.vendors()) { @@ -393,9 +393,9 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot: // Read the active config bundle, parse the config version. PresetBundle bundle; bundle.load_configbundle((data_dir / "vendor" / (cfg.name + ".ini")).string(), PresetBundle::LOAD_CFGBUNDLE_VENDOR_ONLY); - for (const VendorProfile &vp : bundle.vendors) - if (vp.id == cfg.name) - cfg.version.config_version = vp.config_version; + for (const auto &vp : bundle.vendors) + if (vp.second.id == cfg.name) + cfg.version.config_version = vp.second.config_version; // Fill-in the min/max slic3r version from the config index, if possible. try { // Load the config index for the vendor. @@ -417,7 +417,7 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot: // Backup the presets. for (const char *subdir : { "print", "filament", "printer", "vendor" }) copy_config_dir_single_level(data_dir / subdir, snapshot_dir / subdir); - snapshot.save_ini((snapshot_dir / "snapshot.ini").string()); + snapshot.save_ini((snapshot_dir / "snapshot.ini").string()); assert(m_snapshots.empty() || m_snapshots.back().time_captured <= snapshot.time_captured); m_snapshots.emplace_back(std::move(snapshot)); return m_snapshots.back(); diff --git a/src/slic3r/Config/Version.cpp b/src/slic3r/Config/Version.cpp index 175abff69..fcebf88d1 100644 --- a/src/slic3r/Config/Version.cpp +++ b/src/slic3r/Config/Version.cpp @@ -227,17 +227,17 @@ size_t Index::load(const boost::filesystem::path &path) // End of semver or keyword. break; } - if (*key_end != 0 && *key_end != ' ' && *key_end != '\t' && *key_end != '=') + if (*key_end != 0 && *key_end != ' ' && *key_end != '\t' && *key_end != '=') throw file_parser_error("Invalid keyword or semantic version", path, idx_line); - char *value = left_trim(key_end); + char *value = left_trim(key_end); bool key_value_pair = *value == '='; if (key_value_pair) value = left_trim(value + 1); *key_end = 0; boost::optional semver; - if (maybe_semver) + if (maybe_semver) semver = Semver::parse(key); - if (key_value_pair) { + if (key_value_pair) { if (semver) throw file_parser_error("Key cannot be a semantic version", path, idx_line);\ // Verify validity of the key / value pair. @@ -245,11 +245,11 @@ size_t Index::load(const boost::filesystem::path &path) if (strcmp(key, "min_slic3r_version") == 0 || strcmp(key, "max_slic3r_version") == 0) { if (! svalue.empty()) semver = Semver::parse(svalue); - if (! semver) + if (! semver) throw file_parser_error(std::string(key) + " must referece a valid semantic version", path, idx_line); - if (strcmp(key, "min_slic3r_version") == 0) + if (strcmp(key, "min_slic3r_version") == 0) ver.min_slic3r_version = *semver; - else + else ver.max_slic3r_version = *semver; } else { // Ignore unknown keys, as there may come new keys in the future. @@ -286,17 +286,21 @@ Index::const_iterator Index::find(const Semver &ver) const return (it == m_configs.end() || it->config_version == ver) ? it : m_configs.end(); } -Index::const_iterator Index::recommended() const +Index::const_iterator Index::recommended(const Semver &slic3r_version) const { - int idx = -1; const_iterator highest = this->end(); for (const_iterator it = this->begin(); it != this->end(); ++ it) - if (it->is_current_slic3r_supported() && + if (it->is_slic3r_supported(slic3r_version) && (highest == this->end() || highest->config_version < it->config_version)) highest = it; return highest; } +Index::const_iterator Index::recommended() const +{ + return this->recommended(Slic3r::SEMVER); +} + std::vector Index::load_db() { boost::filesystem::path cache_dir = boost::filesystem::path(Slic3r::data_dir()) / "cache"; diff --git a/src/slic3r/Config/Version.hpp b/src/slic3r/Config/Version.hpp index 19c565ffb..e5f1a2e21 100644 --- a/src/slic3r/Config/Version.hpp +++ b/src/slic3r/Config/Version.hpp @@ -71,6 +71,8 @@ public: // Returns configs().end() if such version does not exist in the index. This shall never happen // if the index is valid. const_iterator recommended() const; + // Recommended config for a provided slic3r version. Used when checking for slic3r update (slic3r_version is the old one read out from PrusaSlicer.ini) + const_iterator recommended(const Semver &slic3r_version) const; // Returns the filesystem path from which this index has originally been loaded const boost::filesystem::path& path() const { return m_path; } diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 4185b6664..dce7c4c99 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -171,6 +171,7 @@ void Bed3D::Axes::render() const glsafe(::glPopMatrix()); glsafe(::glDisable(GL_LIGHTING)); + glsafe(::glDisable(GL_DEPTH_TEST)); } void Bed3D::Axes::render_axis(double length) const @@ -204,6 +205,7 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c std::string cst_texture(custom_texture); if (!cst_texture.empty()) { + std::replace(cst_texture.begin(), cst_texture.end(), '\\', '/'); if ((!boost::algorithm::iends_with(custom_texture, ".png") && !boost::algorithm::iends_with(custom_texture, ".svg")) || !boost::filesystem::exists(custom_texture)) cst_texture = ""; } @@ -212,6 +214,7 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c std::string cst_model(custom_model); if (!cst_model.empty()) { + std::replace(cst_model.begin(), cst_model.end(), '\\', '/'); if (!boost::algorithm::iends_with(custom_model, ".stl") || !boost::filesystem::exists(custom_model)) cst_model = ""; } @@ -262,36 +265,27 @@ Point Bed3D::point_projection(const Point& point) const return m_polygon.point_projection(point); } -void Bed3D::render(GLCanvas3D& canvas, float theta, float scale_factor) const +void Bed3D::render(GLCanvas3D& canvas, float theta, float scale_factor, bool show_axes) const { m_scale_factor = scale_factor; - render_axes(); + if (show_axes) + render_axes(); + + glsafe(::glEnable(GL_DEPTH_TEST)); switch (m_type) { - case MK2: - { - render_prusa(canvas, "mk2", theta > 90.0f); - break; - } - case MK3: - { - render_prusa(canvas, "mk3", theta > 90.0f); - break; - } - case SL1: - { - render_prusa(canvas, "sl1", theta > 90.0f); - break; - } + case MK2: { render_prusa(canvas, "mk2", theta > 90.0f); break; } + case MK3: { render_prusa(canvas, "mk3", theta > 90.0f); break; } + case SL1: { render_prusa(canvas, "sl1", theta > 90.0f); break; } + case MINI: { render_prusa(canvas, "mini", theta > 90.0f); break; } + case ENDER3: { render_prusa(canvas, "ender3", theta > 90.0f); break; } default: - case Custom: - { - render_custom(canvas, theta > 90.0f); - break; - } + case Custom: { render_custom(canvas, theta > 90.0f); break; } } + + glsafe(::glDisable(GL_DEPTH_TEST)); } void Bed3D::calc_bounding_boxes() const @@ -362,22 +356,38 @@ Bed3D::EType Bed3D::detect_type(const Pointfs& shape) const { if (curr->config.has("bed_shape")) { - if ((curr->vendor != nullptr) && (curr->vendor->name == "Prusa Research") && (shape == dynamic_cast(curr->config.option("bed_shape"))->values)) + if (curr->vendor != nullptr) { - if (boost::contains(curr->name, "SL1")) + if ((curr->vendor->name == "Prusa Research") && (shape == dynamic_cast(curr->config.option("bed_shape"))->values)) { - type = SL1; - break; + if (boost::contains(curr->name, "SL1")) + { + type = SL1; + break; + } + else if (boost::contains(curr->name, "MK3") || boost::contains(curr->name, "MK2.5")) + { + type = MK3; + break; + } + else if (boost::contains(curr->name, "MK2")) + { + type = MK2; + break; + } + else if (boost::contains(curr->name, "MINI")) + { + type = MINI; + break; + } } - else if (boost::contains(curr->name, "MK3") || boost::contains(curr->name, "MK2.5")) + else if ((curr->vendor->name == "Creality") && (shape == dynamic_cast(curr->config.option("bed_shape"))->values)) { - type = MK3; - break; - } - else if (boost::contains(curr->name, "MK2")) - { - type = MK2; - break; + if (boost::contains(curr->name, "ENDER-3")) + { + type = ENDER3; + break; + } } } } diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index c9a30e6ec..19ecddc0a 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -67,6 +67,8 @@ public: MK2, MK3, SL1, + MINI, + ENDER3, Custom, Num_Types }; @@ -110,7 +112,7 @@ public: bool contains(const Point& point) const; Point point_projection(const Point& point) const; - void render(GLCanvas3D& canvas, float theta, float scale_factor) const; + void render(GLCanvas3D& canvas, float theta, float scale_factor, bool show_axes) const; private: void calc_bounding_boxes() const; diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 5fd85df75..789887564 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -417,7 +417,7 @@ void GLVolume::render(int color_id, int detection_id, int worldmatrix_id) const } bool GLVolume::is_sla_support() const { return this->composite_id.volume_id == -int(slaposSupportTree); } -bool GLVolume::is_sla_pad() const { return this->composite_id.volume_id == -int(slaposBasePool); } +bool GLVolume::is_sla_pad() const { return this->composite_id.volume_id == -int(slaposPad); } std::vector GLVolumeCollection::load_object( const ModelObject *model_object, @@ -501,7 +501,7 @@ void GLVolumeCollection::load_object_auxiliary( TriangleMesh convex_hull = mesh.convex_hull_3d(); for (const std::pair& instance_idx : instances) { const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first]; - this->volumes.emplace_back(new GLVolume((milestone == slaposBasePool) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR)); + this->volumes.emplace_back(new GLVolume((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR)); GLVolume& v = *this->volumes.back(); v.indexed_vertex_array.load_mesh(mesh); v.indexed_vertex_array.finalize_geometry(opengl_initialized); @@ -707,24 +707,24 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M print_volume.min(2) = -1e10; ModelInstance::EPrintVolumeState state = ModelInstance::PVS_Inside; - bool all_contained = true; bool contained_min_one = false; for (GLVolume* volume : this->volumes) { - if ((volume == nullptr) || !volume->printable || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || ((volume->composite_id.volume_id < 0) && !volume->shader_outside_printer_detection_enabled)) + if ((volume == nullptr) || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || ((volume->composite_id.volume_id < 0) && !volume->shader_outside_printer_detection_enabled)) continue; const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box(); bool contained = print_volume.contains(bb); - all_contained &= contained; + + volume->is_outside = !contained; + if (!volume->printable) + continue; if (contained) contained_min_one = true; - volume->is_outside = !contained; - if ((state == ModelInstance::PVS_Inside) && volume->is_outside) state = ModelInstance::PVS_Fully_Outside; @@ -735,7 +735,7 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M if (out_state != nullptr) *out_state = state; - return /*all_contained*/ contained_min_one; // #ys_FIXME_delete_after_testing + return contained_min_one; } void GLVolumeCollection::reset_outside_state() @@ -1717,13 +1717,18 @@ static void thick_point_to_verts(const Vec3crd& point, point_to_indexed_vertex_array(point, width, height, volume.indexed_vertex_array); } +void _3DScene::extrusionentity_to_verts(const Polyline &polyline, float width, float height, float print_z, GLVolume& volume) +{ + if (polyline.size() >= 2) { + size_t num_segments = polyline.size() - 1; + thick_lines_to_verts(polyline.lines(), std::vector(num_segments, width), std::vector(num_segments, height), false, print_z, volume); + } +} + // Fill in the qverts and tverts with quads and triangles for the extrusion_path. void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, GLVolume &volume) { - Lines lines = extrusion_path.polyline.lines(); - std::vector widths(lines.size(), extrusion_path.width); - std::vector heights(lines.size(), extrusion_path.height); - thick_lines_to_verts(lines, widths, heights, false, print_z, volume); + extrusionentity_to_verts(extrusion_path.polyline, extrusion_path.width, extrusion_path.height, print_z, volume); } // Fill in the qverts and tverts with quads and triangles for the extrusion_path. diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 502321163..ed5a6aa83 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -657,6 +657,7 @@ public: static void thick_lines_to_verts(const Lines& lines, const std::vector& widths, const std::vector& heights, bool closed, double top_z, GLVolume& volume); static void thick_lines_to_verts(const Lines3& lines, const std::vector& widths, const std::vector& heights, bool closed, GLVolume& volume); + static void extrusionentity_to_verts(const Polyline &polyline, float width, float height, float print_z, GLVolume& volume); static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, GLVolume& volume); static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GLVolume& volume); static void extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GLVolume& volume); diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp index 5a165e8ae..d33d945ef 100644 --- a/src/slic3r/GUI/AppConfig.cpp +++ b/src/slic3r/GUI/AppConfig.cpp @@ -28,6 +28,9 @@ static const std::string VENDOR_PREFIX = "vendor:"; static const std::string MODEL_PREFIX = "model:"; static const std::string VERSION_CHECK_URL = "https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaSlicer.version"; +const std::string AppConfig::SECTION_FILAMENTS = "filaments"; +const std::string AppConfig::SECTION_MATERIALS = "sla_materials"; + void AppConfig::reset() { m_storage.clear(); @@ -100,7 +103,7 @@ void AppConfig::load() // Error while parsing config file. We'll customize the error message and rethrow to be displayed. throw std::runtime_error( _utf8(L("Error parsing PrusaSlicer config file, it is probably corrupted. " - "Try to manualy delete the file to recover from the error. Your user profiles will not be affected.")) + + "Try to manually delete the file to recover from the error. Your user profiles will not be affected.")) + "\n\n" + AppConfig::config_path() + "\n\n" + ex.what()); } @@ -268,6 +271,80 @@ void AppConfig::set_recent_projects(const std::vector& recent_proje } } +void AppConfig::set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone) +{ + std::string key = std::string("mouse_device:") + name; + auto it = m_storage.find(key); + if (it == m_storage.end()) + it = m_storage.insert(std::map>::value_type(key, std::map())).first; + + it->second.clear(); + it->second["translation_speed"] = std::to_string(translation_speed); + 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); +} + +bool AppConfig::get_mouse_device_translation_speed(const std::string& name, double& speed) +{ + std::string key = std::string("mouse_device:") + name; + auto it = m_storage.find(key); + if (it == m_storage.end()) + return false; + + auto it_val = it->second.find("translation_speed"); + if (it_val == it->second.end()) + return false; + + speed = ::atof(it_val->second.c_str()); + return true; +} + +bool AppConfig::get_mouse_device_translation_deadzone(const std::string& name, double& deadzone) +{ + std::string key = std::string("mouse_device:") + name; + auto it = m_storage.find(key); + if (it == m_storage.end()) + return false; + + auto it_val = it->second.find("translation_deadzone"); + if (it_val == it->second.end()) + return false; + + deadzone = ::atof(it_val->second.c_str()); + return true; +} + +bool AppConfig::get_mouse_device_rotation_speed(const std::string& name, float& speed) +{ + std::string key = std::string("mouse_device:") + name; + auto it = m_storage.find(key); + if (it == m_storage.end()) + return false; + + auto it_val = it->second.find("rotation_speed"); + if (it_val == it->second.end()) + return false; + + speed = (float)::atof(it_val->second.c_str()); + return true; +} + +bool AppConfig::get_mouse_device_rotation_deadzone(const std::string& name, float& deadzone) +{ + std::string key = std::string("mouse_device:") + name; + auto it = m_storage.find(key); + if (it == m_storage.end()) + return false; + + auto it_val = it->second.find("rotation_deadzone"); + if (it_val == it->second.end()) + return false; + + deadzone = (float)::atof(it_val->second.c_str()); + return true; +} + void AppConfig::update_config_dir(const std::string &dir) { this->set("recent", "config_directory", dir); diff --git a/src/slic3r/GUI/AppConfig.hpp b/src/slic3r/GUI/AppConfig.hpp index 8ad17b9db..355370450 100644 --- a/src/slic3r/GUI/AppConfig.hpp +++ b/src/slic3r/GUI/AppConfig.hpp @@ -80,6 +80,12 @@ public: } } + bool has_section(const std::string §ion) const + { return m_storage.find(section) != m_storage.end(); } + const std::map& get_section(const std::string §ion) const + { return m_storage.find(section)->second; } + void set_section(const std::string §ion, const std::map& data) + { m_storage[section] = data; } void clear_section(const std::string §ion) { m_storage[section].clear(); } @@ -125,6 +131,15 @@ public: std::vector get_recent_projects() const; void set_recent_projects(const std::vector& recent_projects); + void set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone); + 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); + + static const std::string SECTION_FILAMENTS; + static const std::string SECTION_MATERIALS; + private: // Map of section, name -> value std::map> m_storage; diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 607fa27bf..158322640 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -10,6 +10,10 @@ #include #include +#if ENABLE_THUMBNAIL_GENERATOR +#include +#endif // ENABLE_THUMBNAIL_GENERATOR + // Print now includes tbb, and tbb includes Windows. This breaks compilation of wxWidgets if included before wx. #include "libslic3r/Print.hpp" #include "libslic3r/SLAPrint.hpp" @@ -29,6 +33,7 @@ #include #include #include "I18N.hpp" +#include "GUI.hpp" namespace Slic3r { @@ -56,6 +61,7 @@ bool BackgroundSlicingProcess::select_technology(PrinterTechnology tech) case ptFFF: m_print = m_fff_print; break; case ptSLA: m_print = m_sla_print; break; case ptSLS: m_print = m_fff_print; break; + default: assert(false); break; } changed = true; } @@ -83,7 +89,19 @@ void BackgroundSlicingProcess::process_fff() assert(m_print == m_fff_print); m_print->process(); wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_slicing_completed_id)); +#if ENABLE_THUMBNAIL_GENERATOR + m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_cb); +#else m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data); +#endif // ENABLE_THUMBNAIL_GENERATOR + + if (m_fff_print->model().custom_gcode_per_height != GUI::wxGetApp().model().custom_gcode_per_height) { + GUI::wxGetApp().model().custom_gcode_per_height = m_fff_print->model().custom_gcode_per_height; + // #ys_FIXME : controll text + GUI::show_info(nullptr, _(L("To except of redundant tool manipulation, \n" + "Color change(s) for unused extruder(s) was(were) deleted")), _(L("Info"))); + } + if (this->set_step_started(bspsGCodeFinalize)) { if (! m_export_path.empty()) { //FIXME localize the messages @@ -103,6 +121,19 @@ void BackgroundSlicingProcess::process_fff() } } +#if ENABLE_THUMBNAIL_GENERATOR +static void write_thumbnail(Zipper& zipper, const ThumbnailData& data) +{ + size_t png_size = 0; + void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)data.pixels.data(), data.width, data.height, 4, &png_size, MZ_DEFAULT_LEVEL, 1); + if (png_data != nullptr) + { + zipper.add_entry("thumbnail/thumbnail" + std::to_string(data.width) + "x" + std::to_string(data.height) + ".png", (const std::uint8_t*)png_data, png_size); + mz_free(png_data); + } +} +#endif // ENABLE_THUMBNAIL_GENERATOR + void BackgroundSlicingProcess::process_sla() { assert(m_print == m_sla_print); @@ -110,7 +141,26 @@ void BackgroundSlicingProcess::process_sla() if (this->set_step_started(bspsGCodeFinalize)) { if (! m_export_path.empty()) { const std::string export_path = m_sla_print->print_statistics().finalize_output_path(m_export_path); - m_sla_print->export_raster(export_path); + + Zipper zipper(export_path); + m_sla_print->export_raster(zipper); + +#if ENABLE_THUMBNAIL_GENERATOR + if (m_thumbnail_cb != nullptr) + { + ThumbnailsList thumbnails; + m_thumbnail_cb(thumbnails, current_print()->full_print_config().option("thumbnails")->values, true, true, true, true); +// m_thumbnail_cb(thumbnails, current_print()->full_print_config().option("thumbnails")->values, true, false, true, true); // renders also supports and pad + for (const ThumbnailData& data : thumbnails) + { + if (data.is_valid()) + write_thumbnail(zipper, data); + } + } +#endif // ENABLE_THUMBNAIL_GENERATOR + + zipper.finalize(); + m_print->set_status(100, (boost::format(_utf8(L("Masked SLA file exported to %1%"))) % export_path).str()); } else if (! m_upload_job.empty()) { prepare_upload(); @@ -222,11 +272,7 @@ bool BackgroundSlicingProcess::start() if (m_state == STATE_INITIAL) { // The worker thread is not running yet. Start it. assert(! m_thread.joinable()); - boost::thread::attributes attrs; - // Duplicating the stack allocation size of Thread Building Block worker threads of the thread pool: - // allocate 4MB on a 64bit system, allocate 2MB on a 32bit system by default. - attrs.set_stack_size((sizeof(void*) == 4) ? (2048 * 1024) : (4096 * 1024)); - m_thread = boost::thread(attrs, [this]{this->thread_proc_safe();}); + m_thread = create_thread([this]{this->thread_proc_safe();}); // Wait until the worker thread is ready to execute the background processing task. m_condition.wait(lck, [this](){ return m_state == STATE_IDLE; }); } @@ -422,8 +468,24 @@ void BackgroundSlicingProcess::prepare_upload() m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string()); } else { m_upload_job.upload_data.upload_path = m_sla_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string()); - m_sla_print->export_raster(source_path.string(), m_upload_job.upload_data.upload_path.string()); + + Zipper zipper{source_path.string()}; + m_sla_print->export_raster(zipper, m_upload_job.upload_data.upload_path.string()); +#if ENABLE_THUMBNAIL_GENERATOR + if (m_thumbnail_cb != nullptr) + { + ThumbnailsList thumbnails; + m_thumbnail_cb(thumbnails, current_print()->full_print_config().option("thumbnails")->values, true, true, true, true); +// m_thumbnail_cb(thumbnails, current_print()->full_print_config().option("thumbnails")->values, true, false, true, true); // renders also supports and pad + for (const ThumbnailData& data : thumbnails) + { + if (data.is_valid()) + write_thumbnail(zipper, data); } + } +#endif // ENABLE_THUMBNAIL_GENERATOR + zipper.finalize(); + } m_print->set_status(100, (boost::format(_utf8(L("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue"))) % m_upload_job.printhost->get_host()).str()); diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index cf5edd55f..a66dcf39c 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -6,12 +6,12 @@ #include #include -#include #include #include "libslic3r/Print.hpp" #include "slic3r/Utils/PrintHost.hpp" +#include "slic3r/Utils/Thread.hpp" namespace Slic3r { @@ -49,6 +49,10 @@ public: void set_fff_print(Print *print) { m_fff_print = print; } void set_sla_print(SLAPrint *print) { m_sla_print = print; } void set_gcode_preview_data(GCodePreviewData *gpd) { m_gcode_preview_data = gpd; } +#if ENABLE_THUMBNAIL_GENERATOR + void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; } +#endif // ENABLE_THUMBNAIL_GENERATOR + // The following wxCommandEvent will be sent to the UI thread / Platter window, when the slicing is finished // and the background processing will transition into G-code export. // The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed. @@ -128,6 +132,11 @@ public: // This "finished" flag does not account for the final export of the output file (.gcode or zipped PNGs), // and it does not account for the OctoPrint scheduling. bool finished() const { return m_print->finished(); } + + void set_force_update_print_regions(bool force_update_print_regions) { + if (m_fff_print) + m_fff_print->set_force_update_print_regions(force_update_print_regions); + } private: void thread_proc(); @@ -151,6 +160,10 @@ private: SLAPrint *m_sla_print = nullptr; // Data structure, to which the G-code export writes its annotations. GCodePreviewData *m_gcode_preview_data = nullptr; +#if ENABLE_THUMBNAIL_GENERATOR + // Callback function, used to write thumbnails into gcode. + ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr; +#endif // ENABLE_THUMBNAIL_GENERATOR // Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID. std::string m_temp_output_path; // Output path provided by the user. The output path may be set even if the slicing is running, diff --git a/src/slic3r/GUI/BedShapeDialog.cpp b/src/slic3r/GUI/BedShapeDialog.cpp index 90fc0ff80..96a0a59cd 100644 --- a/src/slic3r/GUI/BedShapeDialog.cpp +++ b/src/slic3r/GUI/BedShapeDialog.cpp @@ -12,6 +12,7 @@ #include "boost/nowide/iostream.hpp" #include +#include #include @@ -60,7 +61,9 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf { m_shape = default_pt.values; m_custom_texture = custom_texture.value.empty() ? NONE : custom_texture.value; + std::replace(m_custom_texture.begin(), m_custom_texture.end(), '\\', '/'); m_custom_model = custom_model.value.empty() ? NONE : custom_model.value; + std::replace(m_custom_model.begin(), m_custom_model.end(), '\\', '/'); auto sbsizer = new wxStaticBoxSizer(wxVERTICAL, this, _(L("Shape"))); sbsizer->GetStaticBox()->SetFont(wxGetApp().bold_font()); @@ -212,7 +215,18 @@ wxPanel* BedShapePanel::init_texture_panel() wxStaticText* lbl = dynamic_cast(e.GetEventObject()); if (lbl != nullptr) { - wxString tooltip_text = (m_custom_texture == NONE) ? "" : _(m_custom_texture); + bool exists = (m_custom_texture == NONE) || boost::filesystem::exists(m_custom_texture); + lbl->SetForegroundColour(exists ? wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) : wxColor(*wxRED)); + + wxString tooltip_text = ""; + if (m_custom_texture != NONE) + { + if (!exists) + tooltip_text += _(L("Not found: ")); + + tooltip_text += _(m_custom_texture); + } + wxToolTip* tooltip = lbl->GetToolTip(); if ((tooltip == nullptr) || (tooltip->GetTip() != tooltip_text)) lbl->SetToolTip(tooltip_text); @@ -280,7 +294,18 @@ wxPanel* BedShapePanel::init_model_panel() wxStaticText* lbl = dynamic_cast(e.GetEventObject()); if (lbl != nullptr) { - wxString tooltip_text = (m_custom_model == NONE) ? "" : _(m_custom_model); + bool exists = (m_custom_model == NONE) || boost::filesystem::exists(m_custom_model); + lbl->SetForegroundColour(exists ? wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) : wxColor(*wxRED)); + + wxString tooltip_text = ""; + if (m_custom_model != NONE) + { + if (!exists) + tooltip_text += _(L("Not found: ")); + + tooltip_text += _(m_custom_model); + } + wxToolTip* tooltip = lbl->GetToolTip(); if ((tooltip == nullptr) || (tooltip->GetTip() != tooltip_text)) lbl->SetToolTip(tooltip_text); @@ -455,7 +480,7 @@ void BedShapePanel::update_shape() else if (page_idx == SHAPE_CUSTOM) m_shape = m_loaded_shape; - update_preview(); + update_preview(); } // Loads an stl file, projects it to the XY plane and calculates a polygon. @@ -521,6 +546,8 @@ void BedShapePanel::load_texture() return; } + std::replace(file_name.begin(), file_name.end(), '\\', '/'); + wxBusyCursor wait; m_custom_texture = file_name; @@ -544,6 +571,8 @@ void BedShapePanel::load_model() return; } + std::replace(file_name.begin(), file_name.end(), '\\', '/'); + wxBusyCursor wait; m_custom_model = file_name; diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index 8e3a6d1f1..20fda22d3 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -1,7 +1,9 @@ #include "libslic3r/libslic3r.h" #include "Camera.hpp" +#if !ENABLE_THUMBNAIL_GENERATOR #include "3DScene.hpp" +#endif // !ENABLE_THUMBNAIL_GENERATOR #include "GUI_App.hpp" #include "AppConfig.hpp" @@ -22,6 +24,10 @@ namespace Slic3r { namespace GUI { const double Camera::DefaultDistance = 1000.0; +#if ENABLE_THUMBNAIL_GENERATOR +const double Camera::DefaultZoomToBoxMarginFactor = 1.025; +const double Camera::DefaultZoomToVolumesMarginFactor = 1.025; +#endif // ENABLE_THUMBNAIL_GENERATOR double Camera::FrustrumMinZRange = 50.0; double Camera::FrustrumMinNearZ = 100.0; double Camera::FrustrumZMargin = 10.0; @@ -85,10 +91,16 @@ void Camera::select_next_type() void Camera::set_target(const Vec3d& target) { - m_target = target; - m_target(0) = clamp(m_scene_box.min(0), m_scene_box.max(0), m_target(0)); - m_target(1) = clamp(m_scene_box.min(1), m_scene_box.max(1), m_target(1)); - m_target(2) = clamp(m_scene_box.min(2), m_scene_box.max(2), m_target(2)); + 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)); } void Camera::set_theta(float theta, bool apply_limit) @@ -103,20 +115,20 @@ void Camera::set_theta(float theta, bool apply_limit) } } -void Camera::set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h) +void Camera::update_zoom(double delta_zoom) { - zoom = std::max(std::min(zoom, 4.0), -4.0) / 10.0; - zoom = m_zoom / (1.0 - zoom); + set_zoom(m_zoom / (1.0 - std::max(std::min(delta_zoom, 4.0), -4.0) * 0.1)); +} +void Camera::set_zoom(double zoom) +{ // Don't allow to zoom too far outside the scene. - double zoom_min = calc_zoom_to_bounding_box_factor(max_box, canvas_w, canvas_h); + double zoom_min = calc_zoom_to_bounding_box_factor(m_scene_box, (int)m_viewport[2], (int)m_viewport[3]); if (zoom_min > 0.0) zoom = std::max(zoom, zoom_min * 0.7); // Don't allow to zoom too close to the scene. - zoom = std::min(zoom, 100.0); - - m_zoom = zoom; + m_zoom = std::min(zoom, 100.0); } bool Camera::select_view(const std::string& direction) @@ -184,7 +196,7 @@ void Camera::apply_view_matrix() const glsafe(::glGetDoublev(GL_MODELVIEW_MATRIX, m_view_matrix.data())); } -void Camera::apply_projection(const BoundingBoxf3& box) const +void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double far_z) const { set_distance(DefaultDistance); @@ -195,6 +207,12 @@ void Camera::apply_projection(const BoundingBoxf3& box) const { 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]; @@ -266,10 +284,18 @@ void Camera::apply_projection(const BoundingBoxf3& box) const glsafe(::glMatrixMode(GL_MODELVIEW)); } +#if ENABLE_THUMBNAIL_GENERATOR +void Camera::zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h, 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); +#else double zoom = calc_zoom_to_bounding_box_factor(box, canvas_w, canvas_h); +#endif // ENABLE_THUMBNAIL_GENERATOR if (zoom > 0.0) { m_zoom = zoom; @@ -278,6 +304,20 @@ void Camera::zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h) } } +#if ENABLE_THUMBNAIL_GENERATOR +void Camera::zoom_to_volumes(const GLVolumePtrs& volumes, int canvas_w, int canvas_h, double margin_factor) +{ + Vec3d center; + double zoom = calc_zoom_to_volumes_factor(volumes, canvas_w, canvas_h, center, margin_factor); + if (zoom > 0.0) + { + m_zoom = zoom; + // center view around the calculated center + m_target = center; + } +} +#endif // ENABLE_THUMBNAIL_GENERATOR + #if ENABLE_CAMERA_STATISTICS void Camera::debug_render() const { @@ -327,27 +367,10 @@ std::pair Camera::calc_tight_frustrum_zs_around(const BoundingBo while (true) { - ret = std::make_pair(DBL_MAX, -DBL_MAX); - - // box vertices in world space - std::vector vertices; - vertices.reserve(8); - vertices.push_back(box.min); - vertices.emplace_back(box.max(0), box.min(1), box.min(2)); - vertices.emplace_back(box.max(0), box.max(1), box.min(2)); - vertices.emplace_back(box.min(0), box.max(1), box.min(2)); - vertices.emplace_back(box.min(0), box.min(1), box.max(2)); - vertices.emplace_back(box.max(0), box.min(1), box.max(2)); - vertices.push_back(box.max); - vertices.emplace_back(box.min(0), box.max(1), box.max(2)); - - // set the Z range in eye coordinates (negative Zs are in front of the camera) - for (const Vec3d& v : vertices) - { - double z = -(m_view_matrix * v)(2); - ret.first = std::min(ret.first, z); - ret.second = std::max(ret.second, z); - } + // box in eye space + BoundingBoxf3 eye_box = box.transformed(m_view_matrix); + ret.first = -eye_box.max(2); + ret.second = -eye_box.min(2); // apply margin ret.first -= FrustrumZMargin; @@ -372,7 +395,11 @@ std::pair Camera::calc_tight_frustrum_zs_around(const BoundingBo 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 +#else double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int canvas_w, int canvas_h) const +#endif // ENABLE_THUMBNAIL_GENERATOR { double max_bb_size = box.max_size(); if (max_bb_size == 0.0) @@ -402,35 +429,112 @@ double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int ca vertices.push_back(box.max); vertices.emplace_back(box.min(0), box.max(1), box.max(2)); - double max_x = 0.0; - double max_y = 0.0; + double min_x = DBL_MAX; + double min_y = DBL_MAX; + double max_x = -DBL_MAX; + double max_y = -DBL_MAX; +#if !ENABLE_THUMBNAIL_GENERATOR // margin factor to give some empty space around the box double margin_factor = 1.25; +#endif // !ENABLE_THUMBNAIL_GENERATOR for (const Vec3d& v : vertices) { // project vertex on the plane perpendicular to camera forward axis - Vec3d pos(v(0) - bb_center(0), v(1) - bb_center(1), v(2) - bb_center(2)); + Vec3d pos = v - bb_center; Vec3d proj_on_plane = pos - pos.dot(forward) * forward; // calculates vertex coordinate along camera xy axes double x_on_plane = proj_on_plane.dot(right); double y_on_plane = proj_on_plane.dot(up); - max_x = std::max(max_x, std::abs(x_on_plane)); - max_y = std::max(max_y, std::abs(y_on_plane)); + min_x = std::min(min_x, x_on_plane); + min_y = std::min(min_y, y_on_plane); + max_x = std::max(max_x, x_on_plane); + max_y = std::max(max_y, y_on_plane); } - if ((max_x == 0.0) || (max_y == 0.0)) + double dx = max_x - min_x; + double dy = max_y - min_y; + if ((dx <= 0.0) || (dy <= 0.0)) return -1.0f; - max_x *= margin_factor; - max_y *= margin_factor; + double med_x = 0.5 * (max_x + min_x); + double med_y = 0.5 * (max_y + min_y); - return std::min((double)canvas_w / (2.0 * max_x), (double)canvas_h / (2.0 * max_y)); + dx *= margin_factor; + dy *= margin_factor; + + return std::min((double)canvas_w / dx, (double)canvas_h / 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 +{ + if (volumes.empty()) + return -1.0; + + // 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(); + + BoundingBoxf3 box; + for (const GLVolume* volume : volumes) + { + box.merge(volume->transformed_bounding_box()); + } + center = box.center(); + + double min_x = DBL_MAX; + double min_y = DBL_MAX; + double max_x = -DBL_MAX; + double max_y = -DBL_MAX; + + for (const GLVolume* volume : volumes) + { + const Transform3d& transform = volume->world_matrix(); + const TriangleMesh* hull = volume->convex_hull(); + if (hull == nullptr) + continue; + + for (const Vec3f& vertex : hull->its.vertices) + { + Vec3d v = transform * vertex.cast(); + + // project vertex on the plane perpendicular to camera forward axis + Vec3d pos = v - center; + Vec3d proj_on_plane = pos - pos.dot(forward) * forward; + + // calculates vertex coordinate along camera xy axes + double x_on_plane = proj_on_plane.dot(right); + double y_on_plane = proj_on_plane.dot(up); + + min_x = std::min(min_x, x_on_plane); + min_y = std::min(min_y, y_on_plane); + max_x = std::max(max_x, x_on_plane); + max_y = std::max(max_y, y_on_plane); + } + } + + center += 0.5 * (max_x + min_x) * right + 0.5 * (max_y + min_y) * up; + + double dx = margin_factor * (max_x - min_x); + double dy = margin_factor * (max_y - min_y); + + if ((dx <= 0.0) || (dy <= 0.0)) + return -1.0f; + + return std::min((double)canvas_w / dx, (double)canvas_h / dy); +} +#endif // ENABLE_THUMBNAIL_GENERATOR + void Camera::set_distance(double distance) const { m_distance = distance; diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp index 839d0d6cf..6cd1b75a5 100644 --- a/src/slic3r/GUI/Camera.hpp +++ b/src/slic3r/GUI/Camera.hpp @@ -2,6 +2,9 @@ #define slic3r_Camera_hpp_ #include "libslic3r/BoundingBox.hpp" +#if ENABLE_THUMBNAIL_GENERATOR +#include "3DScene.hpp" +#endif // ENABLE_THUMBNAIL_GENERATOR #include namespace Slic3r { @@ -10,6 +13,10 @@ namespace GUI { struct Camera { static const double DefaultDistance; +#if ENABLE_THUMBNAIL_GENERATOR + static const double DefaultZoomToBoxMarginFactor; + static const double DefaultZoomToVolumesMarginFactor; +#endif // ENABLE_THUMBNAIL_GENERATOR static double FrustrumMinZRange; static double FrustrumMinNearZ; static double FrustrumZMargin; @@ -63,8 +70,8 @@ public: void set_theta(float theta, bool apply_limit); double get_zoom() const { return m_zoom; } - void set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h); - void set_zoom(double zoom) { m_zoom = zoom; } + void update_zoom(double delta_zoom); + void set_zoom(double zoom); const BoundingBoxf3& get_scene_box() const { return m_scene_box; } void set_scene_box(const BoundingBoxf3& box) { m_scene_box = box; } @@ -88,9 +95,16 @@ public: void apply_viewport(int x, int y, unsigned int w, unsigned int h) const; void apply_view_matrix() const; - void apply_projection(const BoundingBoxf3& box) const; + // Calculates and applies the projection matrix tighting the frustrum z range around the given box. + // If larger z span is needed, pass the desired values of near and far z (negative values are ignored) + 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); +#else void zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h); +#endif // ENABLE_THUMBNAIL_GENERATOR #if ENABLE_CAMERA_STATISTICS void debug_render() const; @@ -100,7 +114,12 @@ 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; +#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; }; diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index ffabf2283..fa240bad0 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -404,16 +404,18 @@ void ConfigManipulation::toggle_print_sla_options(DynamicPrintConfig* config) toggle_field("pad_wall_thickness", pad_en); toggle_field("pad_wall_height", pad_en); + toggle_field("pad_brim_size", pad_en); toggle_field("pad_max_merge_distance", pad_en); // toggle_field("pad_edge_radius", supports_en); toggle_field("pad_wall_slope", pad_en); toggle_field("pad_around_object", pad_en); + toggle_field("pad_around_object_everywhere", pad_en); - bool has_suppad = pad_en && supports_en; - bool zero_elev = config->opt_bool("pad_around_object") && has_suppad; + bool zero_elev = config->opt_bool("pad_around_object") && pad_en; toggle_field("support_object_elevation", supports_en && !zero_elev); toggle_field("pad_object_gap", zero_elev); + toggle_field("pad_around_object_everywhere", zero_elev); toggle_field("pad_object_connector_stride", zero_elev); toggle_field("pad_object_connector_width", zero_elev); toggle_field("pad_object_connector_penetration", zero_elev); diff --git a/src/slic3r/GUI/ConfigSnapshotDialog.cpp b/src/slic3r/GUI/ConfigSnapshotDialog.cpp index 238fec270..06d64016d 100644 --- a/src/slic3r/GUI/ConfigSnapshotDialog.cpp +++ b/src/slic3r/GUI/ConfigSnapshotDialog.cpp @@ -35,9 +35,14 @@ static wxString generate_html_row(const Config::Snapshot &snapshot, bool row_eve text += snapshot_active ? "#B3FFCB" : (row_even ? "#FFFFFF" : "#D5D5D5"); text += "\">"; text += ""; + + static const constexpr char *LOCALE_TIME_FMT = "%x %X"; + wxString datetime = wxDateTime(snapshot.time_captured).Format(LOCALE_TIME_FMT); + // Format the row header. - text += wxString("") + (snapshot_active ? _(L("Active")) + ": " : "") + - Utils::format_local_date_time(snapshot.time_captured) + ": " + format_reason(snapshot.reason); + text += wxString("") + (snapshot_active ? _(L("Active")) + ": " : "") + + datetime + ": " + format_reason(snapshot.reason); + if (! snapshot.comment.empty()) text += " (" + wxString::FromUTF8(snapshot.comment.data()) + ")"; text += "
"; diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 26b8acfe4..b97070369 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -1,9 +1,12 @@ +// FIXME: extract absolute units -> em + #include "ConfigWizard_private.hpp" #include #include #include #include +#include #include #include #include @@ -19,10 +22,10 @@ #include #include #include +#include #include #include "libslic3r/Utils.hpp" -#include "PresetBundle.hpp" #include "GUI.hpp" #include "GUI_Utils.hpp" #include "slic3r/Config/Snapshot.hpp" @@ -38,6 +41,83 @@ using Config::Snapshot; using Config::SnapshotDB; +// Configuration data structures extensions needed for the wizard + +Bundle::Bundle(fs::path source_path, bool is_in_resources, bool is_prusa_bundle) + : preset_bundle(new PresetBundle) + , vendor_profile(nullptr) + , is_in_resources(is_in_resources) + , is_prusa_bundle(is_prusa_bundle) +{ + preset_bundle->load_configbundle(source_path.string(), PresetBundle::LOAD_CFGBNDLE_SYSTEM); + auto first_vendor = preset_bundle->vendors.begin(); + wxCHECK_RET(first_vendor != preset_bundle->vendors.end(), "Failed to load preset bundle"); + vendor_profile = &first_vendor->second; +} + +Bundle::Bundle(Bundle &&other) + : preset_bundle(std::move(other.preset_bundle)) + , vendor_profile(other.vendor_profile) + , is_in_resources(other.is_in_resources) + , is_prusa_bundle(other.is_prusa_bundle) +{ + other.vendor_profile = nullptr; +} + +BundleMap BundleMap::load() +{ + BundleMap res; + + const auto vendor_dir = (boost::filesystem::path(Slic3r::data_dir()) / "vendor").make_preferred(); + const auto rsrc_vendor_dir = (boost::filesystem::path(resources_dir()) / "profiles").make_preferred(); + + auto prusa_bundle_path = (vendor_dir / PresetBundle::PRUSA_BUNDLE).replace_extension(".ini"); + auto prusa_bundle_rsrc = false; + if (! boost::filesystem::exists(prusa_bundle_path)) { + prusa_bundle_path = (rsrc_vendor_dir / PresetBundle::PRUSA_BUNDLE).replace_extension(".ini"); + prusa_bundle_rsrc = true; + } + Bundle prusa_bundle(std::move(prusa_bundle_path), prusa_bundle_rsrc, true); + res.emplace(PresetBundle::PRUSA_BUNDLE, std::move(prusa_bundle)); + + // Load the other bundles in the datadir/vendor directory + // and then additionally from resources/profiles. + bool is_in_resources = false; + for (auto dir : { &vendor_dir, &rsrc_vendor_dir }) { + for (const auto &dir_entry : boost::filesystem::directory_iterator(*dir)) { + if (Slic3r::is_ini_file(dir_entry)) { + std::string id = dir_entry.path().stem().string(); // stem() = filename() without the trailing ".ini" part + + // Don't load this bundle if we've already loaded it. + if (res.find(id) != res.end()) { continue; } + + Bundle bundle(dir_entry.path(), is_in_resources); + res.emplace(std::move(id), std::move(bundle)); + } + } + + is_in_resources = true; + } + + return res; +} + +Bundle& BundleMap::prusa_bundle() +{ + auto it = find(PresetBundle::PRUSA_BUNDLE); + if (it == end()) { + throw std::runtime_error("ConfigWizard: Internal error in BundleMap: PRUSA_BUNDLE not loaded"); + } + + return it->second; +} + +const Bundle& BundleMap::prusa_bundle() const +{ + return const_cast(this)->prusa_bundle(); +} + + // Printer model picker GUI control struct PrinterPickerEvent : public wxEvent @@ -63,7 +143,9 @@ struct PrinterPickerEvent : public wxEvent wxDEFINE_EVENT(EVT_PRINTER_PICK, PrinterPickerEvent); -PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors, const ModelFilter &filter) +const std::string PrinterPicker::PRINTER_PLACEHOLDER = "printer_placeholder.png"; + +PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig, const ModelFilter &filter) : wxPanel(parent) , vendor_id(vendor.id) , width(0) @@ -85,6 +167,8 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt int max_row_width = 0; int current_row_width = 0; + bool is_variants = false; + for (const auto &model : models) { if (! filter(model)) { continue; } @@ -94,6 +178,17 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt if (wxFileExists(bitmap_file)) { bitmap.LoadFile(bitmap_file, wxBITMAP_TYPE_PNG); bitmap_width = bitmap.GetWidth(); + } else { + BOOST_LOG_TRIVIAL(warning) << boost::format("Can't find bitmap file `%1%` for vendor `%2%`, printer `%3%`, using placeholder icon instead") + % bitmap_file + % vendor.id + % model.id; + + const wxString placeholder_file = GUI::from_u8(Slic3r::var(PRINTER_PLACEHOLDER)); + if (wxFileExists(placeholder_file)) { + bitmap.LoadFile(placeholder_file, wxBITMAP_TYPE_PNG); + bitmap_width = bitmap.GetWidth(); + } } auto *title = new wxStaticText(this, wxID_ANY, model.name, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); @@ -128,12 +223,13 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt auto *alt_label = new wxStaticText(variants_panel, wxID_ANY, _(L("Alternate nozzles:"))); alt_label->SetFont(font_alt_nozzle); variants_sizer->Add(alt_label, 0, wxBOTTOM, 3); + is_variants = true; } auto *cbox = new Checkbox(variants_panel, label, model_id, variant.name); i == 0 ? cboxes.push_back(cbox) : cboxes_alt.push_back(cbox); - bool enabled = appconfig_vendors.get_variant(MAIN_VENDOR, model_id, variant.name); + const bool enabled = appconfig.get_variant(vendor.id, model_id, variant.name); cbox->SetValue(enabled); variants_sizer->Add(cbox, 0, wxBOTTOM, 3); @@ -188,10 +284,10 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt } title_sizer->AddStretchSpacer(); - if (titles.size() > 1) { + if (/*titles.size() > 1*/is_variants) { // It only makes sense to add the All / None buttons if there's multiple printers - auto *sel_all_std = new wxButton(this, wxID_ANY, _(L("All standard"))); + auto *sel_all_std = new wxButton(this, wxID_ANY, titles.size() > 1 ? _(L("All standard")) : _(L("Standard"))); auto *sel_all = new wxButton(this, wxID_ANY, _(L("All"))); auto *sel_none = new wxButton(this, wxID_ANY, _(L("None"))); sel_all_std->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(true, false); }); @@ -211,8 +307,8 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt SetSizer(sizer); } -PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors) - : PrinterPicker(parent, vendor, std::move(title), max_cols, appconfig_vendors, [](const VendorProfile::PrinterModel&) { return true; }) +PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig) + : PrinterPicker(parent, vendor, std::move(title), max_cols, appconfig, [](const VendorProfile::PrinterModel&) { return true; }) {} void PrinterPicker::select_all(bool select, bool alternates) @@ -242,6 +338,19 @@ void PrinterPicker::select_one(size_t i, bool select) } } +bool PrinterPicker::any_selected() const +{ + for (const auto &cb : cboxes) { + if (cb->GetValue()) { return true; } + } + + for (const auto &cb : cboxes_alt) { + if (cb->GetValue()) { return true; } + } + + return false; +} + void PrinterPicker::on_checkbox(const Checkbox *cbox, bool checked) { PrinterPickerEvent evt(EVT_PRINTER_PICK, GetId(), vendor_id, cbox->model, cbox->variant, checked); @@ -266,7 +375,7 @@ ConfigWizardPage::ConfigWizardPage(ConfigWizard *parent, wxString title, wxStrin sizer->AddSpacer(10); content = new wxBoxSizer(wxVERTICAL); - sizer->Add(content, 1); + sizer->Add(content, 1, wxEXPAND); SetSizer(sizer); @@ -280,16 +389,18 @@ ConfigWizardPage::ConfigWizardPage(ConfigWizard *parent, wxString title, wxStrin ConfigWizardPage::~ConfigWizardPage() {} -void ConfigWizardPage::append_text(wxString text) +wxStaticText* ConfigWizardPage::append_text(wxString text) { auto *widget = new wxStaticText(this, wxID_ANY, text, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); widget->Wrap(WRAP_WIDTH); widget->SetMinSize(wxSize(WRAP_WIDTH, -1)); append(widget); + return widget; } void ConfigWizardPage::append_spacer(int space) { + // FIXME: scaling content->AddSpacer(space); } @@ -304,34 +415,42 @@ PageWelcome::PageWelcome(ConfigWizard *parent) _(L("Welcome to the %s Configuration Wizard")) #endif , SLIC3R_APP_NAME), _(L("Welcome"))) - , cbox_reset(nullptr) -{ - if (wizard_p()->run_reason == ConfigWizard::RR_DATA_EMPTY) { - wxString::Format(_(L("Run %s")), ConfigWizard::name()); - append_text(wxString::Format( + , welcome_text(append_text(wxString::Format( _(L("Hello, welcome to %s! This %s helps you with the initial configuration; just a few settings and you will be ready to print.")), SLIC3R_APP_NAME, ConfigWizard::name()) - ); - } else { - cbox_reset = new wxCheckBox(this, wxID_ANY, _(L("Remove user profiles - install from scratch (a snapshot will be taken beforehand)"))); - append(cbox_reset); + )) + , cbox_reset(append( + new wxCheckBox(this, wxID_ANY, _(L("Remove user profiles - install from scratch (a snapshot will be taken beforehand)"))) + )) +{ + welcome_text->Hide(); + cbox_reset->Hide(); } - Show(); +void PageWelcome::set_run_reason(ConfigWizard::RunReason run_reason) +{ + const bool data_empty = run_reason == ConfigWizard::RR_DATA_EMPTY; + welcome_text->Show(data_empty); + cbox_reset->Show(!data_empty); } -PagePrinters::PagePrinters(ConfigWizard *parent, wxString title, wxString shortname, const VendorProfile &vendor, unsigned indent, PrinterTechnology technology) +PagePrinters::PagePrinters(ConfigWizard *parent, + wxString title, + wxString shortname, + const VendorProfile &vendor, + unsigned indent, + Technology technology) : ConfigWizardPage(parent, std::move(title), std::move(shortname), indent) + , technology((Technology)(uint8_t)technology) + , install(false) // only used for 3rd party vendors { enum { COL_SIZE = 200, }; - bool check_first_variant = technology == PrinterTechnology::ptFFF && wizard_p()->check_first_variant(); - - AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors; + AppConfig *appconfig = &this->wizard_p()->appconfig_new; const auto families = vendor.families(); for (const auto &family : families) { @@ -345,16 +464,11 @@ PagePrinters::PagePrinters(ConfigWizard *parent, wxString title, wxString shortn } const auto picker_title = family.empty() ? wxString() : wxString::Format(_(L("%s Family")), family); - auto *picker = new PrinterPicker(this, vendor, picker_title, MAX_COLS, appconfig_vendors, filter); + auto *picker = new PrinterPicker(this, vendor, picker_title, MAX_COLS, *appconfig, filter); - if (check_first_variant) { - // Select the default (first) model/variant on the Prusa vendor - //picker->select_one(0, true); - check_first_variant = false; - } - - picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) { - appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable); + picker->Bind(EVT_PRINTER_PICK, [this, appconfig](const PrinterPickerEvent &evt) { + appconfig->set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable); + wizard_p()->on_printer_pick(this, evt); }); append(new wxStaticLine(this)); @@ -377,6 +491,220 @@ int PagePrinters::get_width() const [](int acc, const PrinterPicker *picker) { return std::max(acc, picker->get_width()); }); } +bool PagePrinters::any_selected() const +{ + for (const auto *picker : printer_pickers) { + if (picker->any_selected()) { return true; } + } + + return false; +} + +void PagePrinters::set_run_reason(ConfigWizard::RunReason run_reason) +{ + if (technology == T_FFF + && (run_reason == ConfigWizard::RR_DATA_EMPTY || run_reason == ConfigWizard::RR_DATA_LEGACY) + && printer_pickers.size() > 0 + && printer_pickers[0]->vendor_id == PresetBundle::PRUSA_BUNDLE) { + printer_pickers[0]->select_one(0, true); + } +} + + +const std::string PageMaterials::EMPTY; + +PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxString title, wxString shortname, wxString list1name) + : ConfigWizardPage(parent, std::move(title), std::move(shortname)) + , materials(materials) + , list_l1(new StringList(this)) + , list_l2(new StringList(this)) + , list_l3(new PresetList(this)) +{ + append_spacer(VERTICAL_SPACING); + + const int em = parent->em_unit(); + const int list_h = 30*em; + + list_l1->SetMinSize(wxSize(8*em, list_h)); + list_l2->SetMinSize(wxSize(13*em, list_h)); + list_l3->SetMinSize(wxSize(25*em, list_h)); + + auto *grid = new wxFlexGridSizer(3, em/2, em); + grid->AddGrowableCol(2, 1); + grid->AddGrowableRow(1, 1); + + grid->Add(new wxStaticText(this, wxID_ANY, list1name)); + grid->Add(new wxStaticText(this, wxID_ANY, _(L("Vendor:")))); + grid->Add(new wxStaticText(this, wxID_ANY, _(L("Profile:")))); + + grid->Add(list_l1, 0, wxEXPAND); + grid->Add(list_l2, 0, wxEXPAND); + grid->Add(list_l3, 1, wxEXPAND); + + auto *btn_sizer = new wxBoxSizer(wxHORIZONTAL); + auto *sel_all = new wxButton(this, wxID_ANY, _(L("All"))); + auto *sel_none = new wxButton(this, wxID_ANY, _(L("None"))); + btn_sizer->Add(sel_all, 0, wxRIGHT, em / 2); + btn_sizer->Add(sel_none); + + grid->Add(new wxBoxSizer(wxHORIZONTAL)); + grid->Add(new wxBoxSizer(wxHORIZONTAL)); + grid->Add(btn_sizer, 0, wxALIGN_RIGHT); + + append(grid, 1, wxEXPAND); + + list_l1->Bind(wxEVT_LISTBOX, [this](wxCommandEvent &) { + update_lists(list_l1->GetSelection(), list_l2->GetSelection()); + }); + list_l2->Bind(wxEVT_LISTBOX, [this](wxCommandEvent &) { + update_lists(list_l1->GetSelection(), list_l2->GetSelection()); + }); + + list_l3->Bind(wxEVT_CHECKLISTBOX, [this](wxCommandEvent &evt) { select_material(evt.GetInt()); }); + + sel_all->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { select_all(true); }); + sel_none->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { select_all(false); }); + + reload_presets(); +} + +void PageMaterials::reload_presets() +{ + clear(); + + list_l1->append(_(L("(All)")), &EMPTY); + + for (const std::string &type : materials->types) { + list_l1->append(type, &type); + } + + if (list_l1->GetCount() > 0) { + list_l1->SetSelection(0); + sel1_prev = wxNOT_FOUND; + sel2_prev = wxNOT_FOUND; + update_lists(0, 0); + } + + presets_loaded = true; +} + +void PageMaterials::update_lists(int sel1, int sel2) +{ + wxWindowUpdateLocker freeze_guard(this); + (void)freeze_guard; + + if (sel1 != sel1_prev) { + // Refresh the second list + + // XXX: The vendor list is created with quadratic complexity here, + // but the number of vendors is going to be very small this shouldn't be a problem. + + list_l2->Clear(); + list_l2->append(_(L("(All)")), &EMPTY); + if (sel1 != wxNOT_FOUND) { + const std::string &type = list_l1->get_data(sel1); + + materials->filter_presets(type, EMPTY, [this](const Preset *p) { + const std::string &vendor = this->materials->get_vendor(p); + + if (list_l2->find(vendor) == wxNOT_FOUND) { + list_l2->append(vendor, &vendor); + } + }); + } + + sel1_prev = sel1; + sel2 = 0; + sel2_prev = wxNOT_FOUND; + list_l2->SetSelection(sel2); + list_l3->Clear(); + } + + if (sel2 != sel2_prev) { + // Refresh the third list + + list_l3->Clear(); + if (sel1 != wxNOT_FOUND && sel2 != wxNOT_FOUND) { + const std::string &type = list_l1->get_data(sel1); + const std::string &vendor = list_l2->get_data(sel2); + + materials->filter_presets(type, vendor, [this](const Preset *p) { + bool was_checked = false; + + int cur_i = list_l3->find(p->alias); + if (cur_i == wxNOT_FOUND) + cur_i = list_l3->append(p->alias, &p->alias); + else + was_checked = list_l3->IsChecked(cur_i); + + const std::string& section = materials->appconfig_section(); + + const bool checked = wizard_p()->appconfig_new.has(section, p->name); + list_l3->Check(cur_i, checked | was_checked); + + /* Update preset selection in config. + * If one preset from aliases bundle is selected, + * than mark all presets with this aliases as selected + * */ + if (checked && !was_checked) + wizard_p()->update_presets_in_config(section, p->alias, true); + else if (!checked && was_checked) + wizard_p()->appconfig_new.set(section, p->name, "1"); + } ); + } + + sel2_prev = sel2; + } + + // for the very begining + if ((wizard_p()->run_reason == ConfigWizard::RR_DATA_EMPTY || wizard_p()->run_reason == ConfigWizard::RR_DATA_LEGACY) + && list_l3->size() > 0 ) + { + list_l3->Check(0, true); + wizard_p()->update_presets_in_config(materials->appconfig_section(), list_l3->get_data(0), true); + } +} + +void PageMaterials::select_material(int i) +{ + const bool checked = list_l3->IsChecked(i); + + const std::string& alias_key = list_l3->get_data(i); + wizard_p()->update_presets_in_config(materials->appconfig_section(), alias_key, checked); +} + +void PageMaterials::select_all(bool select) +{ + wxWindowUpdateLocker freeze_guard(this); + (void)freeze_guard; + + for (unsigned i = 0; i < list_l3->GetCount(); i++) { + const bool current = list_l3->IsChecked(i); + if (current != select) { + list_l3->Check(i, select); + select_material(i); + } + } +} + +void PageMaterials::clear() +{ + list_l1->Clear(); + list_l2->Clear(); + list_l3->Clear(); + sel1_prev = wxNOT_FOUND; + sel2_prev = wxNOT_FOUND; + presets_loaded = false; +} + +void PageMaterials::on_activate() +{ + if (! presets_loaded) { + wizard_p()->update_materials(materials->technology); + reload_presets(); + } +} + const char *PageCustom::default_profile_name = "My Settings"; @@ -400,7 +728,7 @@ PageCustom::PageCustom(ConfigWizard *parent) cb_custom->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { tc_profile_name->Enable(custom_wanted()); - wizard_p()->on_custom_setup(custom_wanted()); + wizard_p()->on_custom_setup(); }); append(cb_custom); @@ -413,7 +741,7 @@ PageUpdate::PageUpdate(ConfigWizard *parent) , version_check(true) , preset_update(true) { - const AppConfig *app_config = GUI::get_app_config(); + const AppConfig *app_config = wxGetApp().app_config; auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); boldfont.SetWeight(wxFONTWEIGHT_BOLD); @@ -445,53 +773,85 @@ PageUpdate::PageUpdate(ConfigWizard *parent) box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); }); } +PageMode::PageMode(ConfigWizard *parent) + : ConfigWizardPage(parent, _(L("View mode")), _(L("View mode"))) +{ + append_text(_(L("PrusaSlicer's user interfaces comes in three variants:\nSimple, 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."))); + + radio_simple = new wxRadioButton(this, wxID_ANY, _(L("Simple mode"))); + radio_advanced = new wxRadioButton(this, wxID_ANY, _(L("Advanced mode"))); + radio_expert = new wxRadioButton(this, wxID_ANY, _(L("Expert mode"))); + + append(radio_simple); + append(radio_advanced); + append(radio_expert); +} + +void PageMode::on_activate() +{ + std::string mode { "simple" }; + wxGetApp().app_config->get("", "view_mode", mode); + + if (mode == "advanced") { radio_advanced->SetValue(true); } + else if (mode == "expert") { radio_expert->SetValue(true); } + else { radio_simple->SetValue(true); } +} + +void PageMode::serialize_mode(AppConfig *app_config) const +{ + std::string mode = ""; + + if (radio_simple->GetValue()) { mode = "simple"; } + if (radio_advanced->GetValue()) { mode = "advanced"; } + if (radio_expert->GetValue()) { mode = "expert"; } + + // If "Mode" page wasn't selected (no one radiobutton is checked), + // we shouldn't to update a view_mode value in app_config + if (mode.empty()) + return; + + app_config->set("view_mode", mode); +} + +#ifdef ALLOW_PRUSA_FIRST PageVendors::PageVendors(ConfigWizard *parent) : ConfigWizardPage(parent, _(L("Other Vendors")), _(L("Other Vendors"))) { - append_text(wxString::Format(_(L("Pick another vendor supported by %s:")), SLIC3R_APP_NAME)); + const AppConfig &appconfig = this->wizard_p()->appconfig_new; + + append_text(wxString::Format(_(L("Pick another vendor supported by %s: (FIXME: this text)")), SLIC3R_APP_NAME)); auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); boldfont.SetWeight(wxFONTWEIGHT_BOLD); - AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors; - wxArrayString choices_vendors; + for (const auto &pair : wizard_p()->bundles) { + const VendorProfile *vendor = pair.second.vendor_profile; + if (vendor->id == PresetBundle::PRUSA_BUNDLE) { continue; } - for (const auto vendor_pair : wizard_p()->vendors) { - const auto &vendor = vendor_pair.second; - if (vendor.id == MAIN_VENDOR) { continue; } - - auto *picker = new PrinterPicker(this, vendor, "", MAX_COLS, appconfig_vendors); - picker->Hide(); - pickers.push_back(picker); - choices_vendors.Add(vendor.name); - - picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) { - appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable); + auto *cbox = new wxCheckBox(this, wxID_ANY, vendor->name); + cbox->Bind(wxEVT_CHECKBOX, [=](wxCommandEvent &event) { + wizard_p()->on_3rdparty_install(vendor, cbox->IsChecked()); }); - } - auto *vendor_picker = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices_vendors); - if (choices_vendors.GetCount() > 0) { - vendor_picker->SetSelection(0); - on_vendor_pick(0); - } + const auto &vendors = appconfig.vendors(); + const bool enabled = vendors.find(pair.first) != vendors.end(); + if (enabled) { + cbox->SetValue(true); - vendor_picker->Bind(wxEVT_CHOICE, [this](wxCommandEvent &evt) { - this->on_vendor_pick(evt.GetInt()); - }); + auto pages = wizard_p()->pages_3rdparty.find(vendor->id); + wxCHECK_RET(pages != wizard_p()->pages_3rdparty.end(), "Internal error: 3rd party vendor printers page not created"); - append(vendor_picker); - for (PrinterPicker *picker : pickers) { this->append(picker); } -} + for (PagePrinters* page : { pages->second.first, pages->second.second }) + if (page) page->install = true; + } -void PageVendors::on_vendor_pick(size_t i) -{ - for (PrinterPicker *picker : pickers) { picker->Hide(); } - if (i < pickers.size()) { - pickers[i]->Show(); - parent->Layout(); + append(cbox); } } +#endif PageFirmware::PageFirmware(ConfigWizard *parent) : ConfigWizardPage(parent, _(L("Firmware Type")), _(L("Firmware")), 1) @@ -684,8 +1044,8 @@ ConfigWizardIndex::ConfigWizardIndex(wxWindow *parent) , bullet_black(ScalableBitmap(parent, "bullet_black.png")) , bullet_blue(ScalableBitmap(parent, "bullet_blue.png")) , bullet_white(ScalableBitmap(parent, "bullet_white.png")) - , item_active(0) - , item_hover(-1) + , item_active(NO_ITEM) + , item_hover(NO_ITEM) , last_page((size_t)-1) { SetMinSize(bg.bmp().GetSize()); @@ -747,6 +1107,8 @@ void ConfigWizardIndex::go_prev() { // Search for a preceiding item that is a page (not a label, ie. page != nullptr) + if (item_active == NO_ITEM) { return; } + for (size_t i = item_active; i > 0; i--) { if (items[i - 1].page != nullptr) { go_to(i - 1); @@ -759,6 +1121,8 @@ void ConfigWizardIndex::go_next() { // Search for a next item that is a page (not a label, ie. page != nullptr) + if (item_active == NO_ITEM) { return; } + for (size_t i = item_active + 1; i < items.size(); i++) { if (items[i].page != nullptr) { go_to(i); @@ -767,30 +1131,41 @@ void ConfigWizardIndex::go_next() } } +// This one actually performs the go-to op void ConfigWizardIndex::go_to(size_t i) { - if (i < items.size() && items[i].page != nullptr) { + if (i != item_active + && i < items.size() + && items[i].page != nullptr) { + auto *new_active = items[i].page; auto *former_active = active_page(); - if (former_active != nullptr) { former_active->Hide(); } + if (former_active != nullptr) { + former_active->Hide(); + } item_active = i; - items[i].page->Show(); + new_active->Show(); wxCommandEvent evt(EVT_INDEX_PAGE, GetId()); AddPendingEvent(evt); Refresh(); + + new_active->on_activate(); } } -void ConfigWizardIndex::go_to(ConfigWizardPage *page) +void ConfigWizardIndex::go_to(const ConfigWizardPage *page) { if (page == nullptr) { return; } for (size_t i = 0; i < items.size(); i++) { - if (items[i].page == page) { go_to(i); } + if (items[i].page == page) { + go_to(i); + return; } } +} void ConfigWizardIndex::clear() { @@ -798,7 +1173,7 @@ void ConfigWizardIndex::clear() if (former_active != nullptr) { former_active->Hide(); } items.clear(); - item_active = 0; + item_active = NO_ITEM; } void ConfigWizardIndex::on_paint(wxPaintEvent & evt) @@ -875,28 +1250,117 @@ void ConfigWizardIndex::msw_rescale() } +// Materials + +const std::string Materials::UNKNOWN = "(Unknown)"; + +void Materials::push(const Preset *preset) +{ + presets.insert(preset); + types.insert(technology & T_FFF + ? Materials::get_filament_type(preset) + : Materials::get_material_type(preset)); +} + +void Materials::clear() +{ + presets.clear(); + types.clear(); +} + +const std::string& Materials::appconfig_section() const +{ + return (technology & T_FFF) ? AppConfig::SECTION_FILAMENTS : AppConfig::SECTION_MATERIALS; +} + +const std::string& Materials::get_type(const Preset *preset) const +{ + return (technology & T_FFF) ? get_filament_type(preset) : get_material_type(preset); +} + +const std::string& Materials::get_vendor(const Preset *preset) const +{ + return (technology & T_FFF) ? get_filament_vendor(preset) : get_material_vendor(preset); +} + +const std::string& Materials::get_filament_type(const Preset *preset) +{ + const auto *opt = preset->config.opt("filament_type"); + if (opt != nullptr && opt->values.size() > 0) { + return opt->values[0]; + } else { + return UNKNOWN; + } +} + +const std::string& Materials::get_filament_vendor(const Preset *preset) +{ + const auto *opt = preset->config.opt("filament_vendor"); + return opt != nullptr ? opt->value : UNKNOWN; +} + +const std::string& Materials::get_material_type(const Preset *preset) +{ + const auto *opt = preset->config.opt("material_type"); + if (opt != nullptr) { + return opt->value; + } else { + return UNKNOWN; + } +} + +const std::string& Materials::get_material_vendor(const Preset *preset) +{ + const auto *opt = preset->config.opt("material_vendor"); + return opt != nullptr ? opt->value : UNKNOWN; +} + + // priv -void ConfigWizard::priv::load_pages(bool custom_setup) +void ConfigWizard::priv::load_pages() { - const auto former_active = index->active_item(); + wxWindowUpdateLocker freeze_guard(q); + (void)freeze_guard; + + const ConfigWizardPage *former_active = index->active_page(); index->clear(); index->add_page(page_welcome); - for (PagePrinters *page : page_vendors) { +#ifdef ALLOW_PRUSA_FIRST + // Printers + index->add_page(page_fff); + index->add_page(page_msla); + index->add_page(page_vendors); + for (const auto &pages : pages_3rdparty) { + for (PagePrinters* page : { pages.second.first, pages.second.second }) + if (page && page->install) + index->add_page(page); + } +#else + for (PagePrinters *page : pages_vendors) { index->add_page(page); } - index->add_page(page_custom); +#endif - if (custom_setup) { + index->add_page(page_custom); + if (page_custom->custom_wanted()) { index->add_page(page_firmware); index->add_page(page_bed); index->add_page(page_diams); index->add_page(page_temps); } + // Filaments & Materials + if (any_fff_selected) { index->add_page(page_filaments); } + if (any_sla_selected) { index->add_page(page_sla_materials); } + + // there should to be selected at least one printer + btn_finish->Enable(any_fff_selected || any_sla_selected); + index->add_page(page_update); + index->add_page(page_mode); index->go_to(former_active); // Will restore the active item/page if possible @@ -926,52 +1390,32 @@ void ConfigWizard::priv::init_dialog_size() q->SetSize(window_rect); } -bool ConfigWizard::priv::check_first_variant() const -{ - return run_reason == RR_DATA_EMPTY || run_reason == RR_DATA_LEGACY; -} - void ConfigWizard::priv::load_vendors() { - const fs::path vendor_dir = fs::path(Slic3r::data_dir()) / "vendor"; - const fs::path rsrc_vendor_dir = fs::path(resources_dir()) / "profiles"; - - // Load vendors from the "vendors" directory in datadir - for (auto &dir_entry : boost::filesystem::directory_iterator(vendor_dir)) - if (Slic3r::is_ini_file(dir_entry)) { - try { - auto vp = VendorProfile::from_ini(dir_entry.path()); - vendors[vp.id] = std::move(vp); - } - catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % dir_entry.path() % e.what(); - } - } - - // Additionally load up vendors from the application resources directory, but only those not seen in the datadir - for (auto &dir_entry : boost::filesystem::directory_iterator(rsrc_vendor_dir)) - if (Slic3r::is_ini_file(dir_entry)) { - const auto id = dir_entry.path().stem().string(); - if (vendors.find(id) == vendors.end()) { - try { - auto vp = VendorProfile::from_ini(dir_entry.path()); - vendors_rsrc[vp.id] = dir_entry.path().filename().string(); - vendors[vp.id] = std::move(vp); - } - catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % dir_entry.path() % e.what(); - } - } - } + bundles = BundleMap::load(); // Load up the set of vendors / models / variants the user has had enabled up till now - const AppConfig *app_config = GUI::get_app_config(); - appconfig_vendors.set_vendors(*app_config); + AppConfig *app_config = wxGetApp().app_config; + appconfig_new.set_vendors(*app_config); + + // Initialize the is_visible flag in printer Presets + for (auto &pair : bundles) { + pair.second.preset_bundle->load_installed_printers(appconfig_new); + } + + if (app_config->has_section(AppConfig::SECTION_FILAMENTS)) { + appconfig_new.set_section(AppConfig::SECTION_FILAMENTS, app_config->get_section(AppConfig::SECTION_FILAMENTS)); + } + if (app_config->has_section(AppConfig::SECTION_MATERIALS)) { + appconfig_new.set_section(AppConfig::SECTION_MATERIALS, app_config->get_section(AppConfig::SECTION_MATERIALS)); + } } void ConfigWizard::priv::add_page(ConfigWizardPage *page) { - hscroll_sizer->Add(page, 0, wxEXPAND); + const int proportion = (page->shortname == _(L("Filaments"))) || (page->shortname == _(L("SLA Materials"))) ? 1 : 0; + hscroll_sizer->Add(page, proportion, wxEXPAND); + all_pages.push_back(page); } void ConfigWizard::priv::enable_next(bool enable) @@ -980,19 +1424,245 @@ void ConfigWizard::priv::enable_next(bool enable) btn_finish->Enable(enable); } -void ConfigWizard::priv::on_custom_setup(bool custom_wanted) +void ConfigWizard::priv::set_start_page(ConfigWizard::StartPage start_page) { - load_pages(custom_wanted); + switch (start_page) { + case ConfigWizard::SP_PRINTERS: +#ifdef ALLOW_PRUSA_FIRST + index->go_to(page_fff); + btn_next->SetFocus(); +#else + if(pages_vendors.empty()) + { + index->go_to(page_welcome); + btn_next->SetFocus(); + } + else + { + index->go_to(pages_vendors.front()); + btn_next->SetFocus(); + } +#endif + break; + case ConfigWizard::SP_FILAMENTS: + index->go_to(page_filaments); + btn_finish->SetFocus(); + break; + case ConfigWizard::SP_MATERIALS: + index->go_to(page_sla_materials); + btn_finish->SetFocus(); + break; + default: + index->go_to(page_welcome); + btn_next->SetFocus(); + break; + } +} + +#ifdef ALLOW_PRUSA_FIRST +void ConfigWizard::priv::create_3rdparty_pages() +{ + for (const auto &pair : bundles) { + const VendorProfile *vendor = pair.second.vendor_profile; + if (vendor->id == PresetBundle::PRUSA_BUNDLE) { continue; } + + bool is_fff_technology = false; + bool is_sla_technology = false; + + for (auto& model: vendor->models) + { + if (!is_fff_technology && model.technology == ptFFF) + is_fff_technology = true; + if (!is_sla_technology && model.technology == ptSLA) + is_sla_technology = true; + } + + PagePrinters* pageFFF = nullptr; + PagePrinters* pageSLA = nullptr; + + if (is_fff_technology) { + pageFFF = new PagePrinters(q, vendor->name + " " +_(L("FFF Technology Printers")), vendor->name+" FFF", *vendor, 1, T_FFF); + add_page(pageFFF); + } + + if (is_sla_technology) { + pageSLA = new PagePrinters(q, vendor->name + " " + _(L("SLA Technology Printers")), vendor->name+" MSLA", *vendor, 1, T_SLA); + add_page(pageSLA); + } + + pages_3rdparty.insert({vendor->id, {pageFFF, pageSLA}}); + } +} +#endif + +void ConfigWizard::priv::set_run_reason(RunReason run_reason) +{ + this->run_reason = run_reason; + for (auto &page : all_pages) { + page->set_run_reason(run_reason); + } +} + +void ConfigWizard::priv::update_materials(Technology technology) +{ + if (any_fff_selected && (technology & T_FFF)) { + filaments.clear(); + aliases_fff.clear(); + + // Iterate filaments in all bundles + for (const auto &pair : bundles) { + for (const auto &filament : pair.second.preset_bundle->filaments) { + // Check if filament is already added + if (filaments.containts(&filament)) { continue; } + + // Iterate printers in all bundles + for (const auto &pair : bundles) { + for (const auto &printer : pair.second.preset_bundle->printers) { + // Filter out inapplicable printers + if (!printer.is_visible || printer.printer_technology() != ptFFF) { + continue; + } + + if (filament.is_compatible_with_printer(printer)) { + filaments.push(&filament); + if (!filament.alias.empty()) + aliases_fff[filament.alias].insert(filament.name); + } + } + } + } + } + } + + if (any_sla_selected && (technology & T_SLA)) { + sla_materials.clear(); + aliases_sla.clear(); + + // Iterate SLA materials in all bundles + for (const auto &pair : bundles) { + for (const auto &material : pair.second.preset_bundle->sla_materials) { + // Check if material is already added + if (sla_materials.containts(&material)) { continue; } + + // Iterate printers in all bundles + for (const auto &pair : bundles) { + for (const auto &printer : pair.second.preset_bundle->printers) { + // Filter out inapplicable printers + if (!printer.is_visible || printer.printer_technology() != ptSLA) { + continue; + } + + if (material.is_compatible_with_printer(printer)) { + sla_materials.push(&material); + if (!material.alias.empty()) + aliases_sla[material.alias].insert(material.name); + } + } + } + } + } + } +} + +void ConfigWizard::priv::on_custom_setup() +{ + load_pages(); +} + +void ConfigWizard::priv::on_printer_pick(PagePrinters *page, const PrinterPickerEvent &evt) +{ + if (check_sla_selected() != any_sla_selected || + check_fff_selected() != any_fff_selected) { + any_fff_selected = check_fff_selected(); + any_sla_selected = check_sla_selected(); + + load_pages(); + } + + // Update the is_visible flag on relevant printer profiles + for (auto &pair : bundles) { + if (pair.first != evt.vendor_id) { continue; } + + for (auto &preset : pair.second.preset_bundle->printers) { + if (preset.config.opt_string("printer_model") == evt.model_id + && preset.config.opt_string("printer_variant") == evt.variant_name) { + preset.is_visible = evt.enable; + } + } + } + + if (page->technology & T_FFF) { + page_filaments->clear(); + } else if (page->technology & T_SLA) { + page_sla_materials->clear(); + } +} + +#ifdef ALLOW_PRUSA_FIRST +void ConfigWizard::priv::on_3rdparty_install(const VendorProfile *vendor, bool install) +{ + auto it = pages_3rdparty.find(vendor->id); + wxCHECK_RET(it != pages_3rdparty.end(), "Internal error: GUI page not found for 3rd party vendor profile"); + + for (PagePrinters* page : { it->second.first, it->second.second }) + if (page) { + if (page->install && !install) + page->select_all(false); + page->install = install; + page->Layout(); + } + + load_pages(); +} +#endif + +bool ConfigWizard::priv::check_material_config() +{ + const auto exist_preset = [this](const std::string& section, const Materials& materials) + { + if (appconfig_new.has_section(section) && + !appconfig_new.get_section(section).empty()) + { + const std::map& appconfig_presets = appconfig_new.get_section(section); + for (const auto& preset : appconfig_presets) + if (materials.exist_preset(preset.first)) + return true; + } + return false; + }; + + if (any_fff_selected && !exist_preset(AppConfig::SECTION_FILAMENTS, filaments)) + { + show_info(q, _(L("You have to select at least one filament for selected printers")), ""); + return false; + } + + if (any_sla_selected && !exist_preset(AppConfig::SECTION_MATERIALS, sla_materials)) + { + show_info(q, _(L("You have to select at least one material for selected printers")), ""); + return false; + } + + return true; } void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater) { - const auto enabled_vendors = appconfig_vendors.vendors(); + const auto enabled_vendors = appconfig_new.vendors(); // Install bundles from resources if needed: std::vector install_bundles; - for (const auto &vendor_rsrc : vendors_rsrc) { - const auto vendor = enabled_vendors.find(vendor_rsrc.first); + for (const auto &pair : bundles) { + if (! pair.second.is_in_resources) { continue; } + + if (pair.second.is_prusa_bundle) { + // Always install Prusa bundle, because it has a lot of filaments/materials + // likely to be referenced by other profiles. + install_bundles.emplace_back(pair.first); + continue; + } + + const auto vendor = enabled_vendors.find(pair.first); if (vendor == enabled_vendors.end()) { continue; } size_t size_sum = 0; @@ -1000,7 +1670,7 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese if (size_sum > 0) { // This vendor needs to be installed - install_bundles.emplace_back(vendor_rsrc.second); + install_bundles.emplace_back(pair.first); } } @@ -1042,20 +1712,27 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese preset_bundle->reset(true); } - app_config->set_vendors(appconfig_vendors); + app_config->set_vendors(appconfig_new); + if (appconfig_new.has_section(AppConfig::SECTION_FILAMENTS)) { + app_config->set_section(AppConfig::SECTION_FILAMENTS, appconfig_new.get_section(AppConfig::SECTION_FILAMENTS)); + } + if (appconfig_new.has_section(AppConfig::SECTION_MATERIALS)) { + app_config->set_section(AppConfig::SECTION_MATERIALS, appconfig_new.get_section(AppConfig::SECTION_MATERIALS)); + } app_config->set("version_check", page_update->version_check ? "1" : "0"); app_config->set("preset_update", page_update->preset_update ? "1" : "0"); + page_mode->serialize_mode(app_config); std::string preferred_model; - // Figure out the default pre-selected printer based on the seletions in the picker. + // Figure out the default pre-selected printer based on the selections in the pickers. // The default is the first selected printer model (one with at least 1 variant selected). // The default is only applied by load_presets() if the user doesn't have a (visible) printer // selected already. - const auto vendor_prusa = vendors.find("PrusaResearch"); + // Prusa printers are considered first, then 3rd party. const auto config_prusa = enabled_vendors.find("PrusaResearch"); - if (vendor_prusa != vendors.end() && config_prusa != enabled_vendors.end()) { - for (const auto &model : vendor_prusa->second.models) { + if (config_prusa != enabled_vendors.end()) { + for (const auto &model : bundles.prusa_bundle().vendor_profile->models) { const auto model_it = config_prusa->second.find(model.id); if (model_it != config_prusa->second.end() && model_it->second.size() > 0) { preferred_model = model.id; @@ -1063,6 +1740,20 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese } } } + if (preferred_model.empty()) { + for (const auto &bundle : bundles) { + if (bundle.second.is_prusa_bundle) { continue; } + + const auto config = enabled_vendors.find(bundle.first); + for (const auto &model : bundle.second.vendor_profile->models) { + const auto model_it = config->second.find(model.id); + if (model_it != config->second.end() && model_it->second.size() > 0) { + preferred_model = model.id; + break; + } + } + } + } preset_bundle->load_presets(*app_config, preferred_model); @@ -1080,20 +1771,71 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese preset_bundle->export_selections(*app_config); } + static const boost::unordered_map tech_to_string{ { { PrinterTechnology::ptFFF, "FFF" }, { PrinterTechnology::ptSLA, "SLA" }, { PrinterTechnology::ptSLS, "SLS" }, } }; +void ConfigWizard::priv::update_presets_in_config(const std::string& section, const std::string& alias_key, bool add) +{ + const PresetAliases& aliases = section == AppConfig::SECTION_FILAMENTS ? aliases_fff : aliases_sla; + + auto update = [this, add](const std::string& s, const std::string& key) { + if (add) + appconfig_new.set(s, key, "1"); + else + appconfig_new.erase(s, key); + }; + + // add or delete presets had a same alias + auto it = aliases.find(alias_key); + if (it != aliases.end()) + for (const std::string& name : it->second) + update(section, name); +} + +bool ConfigWizard::priv::check_fff_selected() +{ +#ifdef ALLOW_PRUSA_FIRST + bool ret = page_fff->any_selected(); + for (const auto& printer: pages_3rdparty) + if (printer.second.first) // FFF page + ret |= printer.second.first->any_selected(); +#else + bool ret = false; + for (const PagePrinters *printer : pages_vendors) + if (printer->technology == T_FFF) + ret |= printer->any_selected(); +#endif + return ret; +} + +bool ConfigWizard::priv::check_sla_selected() +{ +#ifdef ALLOW_PRUSA_FIRST + bool ret = page_msla->any_selected(); + for (const auto& printer: pages_3rdparty) + if (printer.second.second) // SLA page + ret |= printer.second.second->any_selected(); +#else + bool ret = false; + for (const PagePrinters *printer : pages_vendors) + if (printer->technology == T_SLA) + ret |= printer->any_selected(); +#endif + return ret; +} + + // Public -ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) +ConfigWizard::ConfigWizard(wxWindow *parent) : DPIDialog(parent, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + name(), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) , p(new priv(this)) { this->SetFont(wxGetApp().normal_font()); - p->run_reason = reason; p->load_vendors(); p->custom_config.reset(DynamicPrintConfig::new_from_defaults_keys({ @@ -1130,30 +1872,58 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) p->btnsizer->Add(p->btn_finish, 0, wxLEFT, BTN_SPACING); p->btnsizer->Add(p->btn_cancel, 0, wxLEFT, BTN_SPACING); + const auto prusa_it = p->bundles.find("PrusaResearch"); + wxCHECK_RET(prusa_it != p->bundles.cend(), "Vendor PrusaResearch not found"); + const VendorProfile *vendor_prusa = prusa_it->second.vendor_profile; p->add_page(p->page_welcome = new PageWelcome(this)); - for (auto vendor : p->vendors) { +#ifdef ALLOW_PRUSA_FIRST + p->page_fff = new PagePrinters(this, _(L("Prusa FFF Technology Printers")), "Prusa FFF", *vendor_prusa, 0, T_FFF); + p->add_page(p->page_fff); + + p->page_msla = new PagePrinters(this, _(L("Prusa MSLA Technology Printers")), "Prusa MSLA", *vendor_prusa, 0, T_SLA); + p->add_page(p->page_msla); +#else + for (const auto &vendor : p->bundles) { bool first = true; - for (const PrinterTechnology &tech : vendor.second.technologies) { - wxString name = _(L(vendor.second.name)); + for (const PrinterTechnology &tech : vendor.second.vendor_profile->technologies) { + wxString name = _(L(vendor.second.vendor_profile->name)); name.Replace("{technology}", tech_to_string.at(tech)); - wxString description = _(L(vendor.second.full_name)); + wxString description = _(L(vendor.second.vendor_profile->full_name)); description.Replace("{technology}", tech_to_string.at(tech)); - p->page_vendors.push_back(new PagePrinters(this, description, name, vendor.second, (vendor.second.technologies.size()>1 && !first ? 1 : 0), tech)); - p->add_page(p->page_vendors.back()); + p->pages_vendors.push_back(new PagePrinters(this, description, name, *vendor.second.vendor_profile, (uint32_t)(vendor.second.vendor_profile->technologies.size()>1 && !first ? 1 : 0), (Technology)(uint8_t)tech)); + p->add_page(p->pages_vendors.back()); first = false; } } +#endif + p->any_sla_selected = p->check_sla_selected(); + p->any_fff_selected = p->check_fff_selected(); + + p->update_materials(T_ANY); + + p->add_page(p->page_filaments = new PageMaterials(this, &p->filaments, + _(L("Filament Profiles Selection")), _(L("Filaments")), _(L("Type:")) )); + p->add_page(p->page_sla_materials = new PageMaterials(this, &p->sla_materials, + _(L("SLA Material Profiles Selection")), _(L("SLA Materials")), _(L("Layer height:")) )); p->add_page(p->page_custom = new PageCustom(this)); p->add_page(p->page_update = new PageUpdate(this)); + 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)); - p->load_pages(false); +#ifdef ALLOW_PRUSA_FIRST + // Pages for 3rd party vendors + p->create_3rdparty_pages(); // Needs to ne done _before_ creating PageVendors + p->add_page(p->page_vendors = new PageVendors(this)); +#endif + + p->load_pages(); + p->index->go_to(size_t{0}); vsizer->Add(topsizer, 1, wxEXPAND | wxALL, DIALOG_MARGIN); vsizer->Add(hline, 0, wxEXPAND); @@ -1171,21 +1941,36 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) p->btn_prev->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) { this->p->index->go_prev(); }); p->btn_next->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) { this->p->index->go_next(); }); - p->btn_finish->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) { this->EndModal(wxID_OK); }); - p->btn_finish->Hide(); + p->btn_finish->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) + { + if (!p->check_material_config()) + return; + this->EndModal(wxID_OK); + }); +// p->btn_finish->Hide(); p->btn_sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) { +#ifdef ALLOW_PRUSA_FIRST + p->any_sla_selected = true; + p->load_pages(); + p->page_fff->select_all(true, false); + p->page_msla->select_all(true, false); + p->index->go_to(p->page_mode); +#else ConfigWizardPage *page = p->index->active_page(); PagePrinters *page_printers = dynamic_cast(page); if (page_printers) page_printers->select_all(true, false); p->index->go_to(p->page_update); +#endif }); p->index->Bind(EVT_INDEX_PAGE, [this](const wxCommandEvent &) { const bool is_last = p->index->active_is_last(); p->btn_next->Show(! is_last); - p->btn_finish->Show(is_last); +// p->btn_finish->Show(is_last); + if (is_last) + p->btn_finish->SetFocus(); Layout(); }); @@ -1193,13 +1978,19 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) ConfigWizard::~ConfigWizard() {} -bool ConfigWizard::run(PresetBundle *preset_bundle, const PresetUpdater *updater) +bool ConfigWizard::run(RunReason reason, StartPage start_page) { - BOOST_LOG_TRIVIAL(info) << "Running ConfigWizard, reason: " << p->run_reason; + BOOST_LOG_TRIVIAL(info) << boost::format("Running ConfigWizard, reason: %1%, start_page: %2%") % reason % start_page; + + GUI_App &app = wxGetApp(); + + p->set_run_reason(reason); + p->set_start_page(start_page); + if (ShowModal() == wxID_OK) { - auto *app_config = GUI::get_app_config(); - p->apply_config(app_config, preset_bundle, updater); - app_config->set_legacy_datadir(false); + p->apply_config(app.app_config, app.preset_bundle, app.preset_updater); + app.app_config->set_legacy_datadir(false); + app.update_mode(); BOOST_LOG_TRIVIAL(info) << "ConfigWizard applied"; return true; } else { @@ -1208,16 +1999,18 @@ bool ConfigWizard::run(PresetBundle *preset_bundle, const PresetUpdater *updater } } - const wxString& ConfigWizard::name(const bool from_menu/* = false*/) { - // A different naming convention is used for the Wizard on Windows vs. OSX & GTK. + // A different naming convention is used for the Wizard on Windows & GTK vs. OSX. + // Note: Don't call _() macro here. + // This function just return the current name according to the OS. + // Translation is implemented inside GUI_App::add_config_menu() #if __APPLE__ - static const wxString config_wizard_name = _(L("Configuration Assistant")); - static const wxString config_wizard_name_menu = _(L("Configuration &Assistant")); + static const wxString config_wizard_name = L("Configuration Assistant"); + static const wxString config_wizard_name_menu = L("Configuration &Assistant"); #else - static const wxString config_wizard_name = _(L("Configuration Wizard")); - static const wxString config_wizard_name_menu = _(L("Configuration &Wizard")); + static const wxString config_wizard_name = L("Configuration Wizard"); + static const wxString config_wizard_name_menu = L("Configuration &Wizard"); #endif return from_menu ? config_wizard_name_menu : config_wizard_name; } @@ -1226,16 +2019,22 @@ void ConfigWizard::on_dpi_changed(const wxRect &suggested_rect) { p->index->msw_rescale(); - const int& em = em_unit(); + const int em = em_unit(); msw_buttons_rescale(this, em, { wxID_APPLY, wxID_CANCEL, p->btn_sel_all->GetId(), p->btn_next->GetId(), p->btn_prev->GetId() }); - for (PagePrinters* page : p->page_vendors) + +#ifdef ALLOW_PRUSA_FIRST + for (auto printer_picker : p->page_fff->printer_pickers) + msw_buttons_rescale(this, em, printer_picker->get_button_indexes()); +#else + for (PagePrinters* page : p->pages_vendors) for (PrinterPicker* printer_picker : page->printer_pickers) msw_buttons_rescale(this, em, printer_picker->get_button_indexes()); +#endif p->init_dialog_size(); diff --git a/src/slic3r/GUI/ConfigWizard.hpp b/src/slic3r/GUI/ConfigWizard.hpp index b707e525b..c43179a66 100644 --- a/src/slic3r/GUI/ConfigWizard.hpp +++ b/src/slic3r/GUI/ConfigWizard.hpp @@ -14,6 +14,7 @@ class PresetUpdater; namespace GUI { +//#define ALLOW_PRUSA_FIRST 1 class ConfigWizard: public DPIDialog { @@ -26,7 +27,15 @@ public: RR_USER, // User requested the Wizard from the menus }; - ConfigWizard(wxWindow *parent, RunReason run_reason); + // What page should wizard start on + enum StartPage { + SP_WELCOME, + SP_PRINTERS, + SP_FILAMENTS, + SP_MATERIALS, + }; + + ConfigWizard(wxWindow *parent); ConfigWizard(ConfigWizard &&) = delete; ConfigWizard(const ConfigWizard &) = delete; ConfigWizard &operator=(ConfigWizard &&) = delete; @@ -34,7 +43,7 @@ public: ~ConfigWizard(); // Run the Wizard. Return whether it was completed. - bool run(PresetBundle *preset_bundle, const PresetUpdater *updater); + bool run(RunReason reason, StartPage start_page = SP_WELCOME); static const wxString& name(const bool from_menu = false); diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index 4e0f5d8bc..8cf513c62 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -15,11 +15,14 @@ #include #include #include +#include +#include +#include #include "libslic3r/PrintConfig.hpp" #include "slic3r/Utils/PresetUpdater.hpp" #include "AppConfig.hpp" -#include "Preset.hpp" +#include "PresetBundle.hpp" #include "BedShapeDialog.hpp" namespace fs = boost::filesystem; @@ -41,6 +44,84 @@ enum { ROW_SPACING = 75, }; + + +// Configuration data structures extensions needed for the wizard + +enum Technology { + // Bitflag equivalent of PrinterTechnology + T_FFF = 0x1, + T_SLA = 0x2, + T_ANY = ~0, +}; + +struct Materials +{ + Technology technology; + std::set presets; + std::set types; + + Materials(Technology technology) : technology(technology) {} + + void push(const Preset *preset); + void clear(); + bool containts(const Preset *preset) { + return presets.find(preset) != presets.end(); + } + + const std::string& appconfig_section() const; + const std::string& get_type(const Preset *preset) const; + const std::string& get_vendor(const Preset *preset) const; + + template void filter_presets(const std::string &type, const std::string &vendor, F cb) { + for (const Preset *preset : presets) { + if ((type.empty() || get_type(preset) == type) && (vendor.empty() || get_vendor(preset) == vendor)) { + cb(preset); + } + } + } + + bool exist_preset(const std::string& preset_name) const + { + for (const Preset* preset : presets) + if (preset->name == preset_name) + return true; + return false; + } + + static const std::string UNKNOWN; + static const std::string& get_filament_type(const Preset *preset); + static const std::string& get_filament_vendor(const Preset *preset); + static const std::string& get_material_type(const Preset *preset); + static const std::string& get_material_vendor(const Preset *preset); +}; + +struct Bundle +{ + std::unique_ptr preset_bundle; + VendorProfile *vendor_profile; + const bool is_in_resources; + const bool is_prusa_bundle; + + Bundle(fs::path source_path, bool is_in_resources, bool is_prusa_bundle = false); + Bundle(Bundle &&other); + + const std::string& vendor_id() const { return vendor_profile->id; } +}; + +struct BundleMap: std::unordered_map +{ + static BundleMap load(); + + Bundle& prusa_bundle(); + const Bundle& prusa_bundle() const; +}; + +struct PrinterPickerEvent; + + +// GUI elements + typedef std::function ModelFilter; struct PrinterPicker: wxPanel @@ -61,19 +142,22 @@ struct PrinterPicker: wxPanel std::vector cboxes; std::vector cboxes_alt; - PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors, const ModelFilter &filter); - PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors); + PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig, const ModelFilter &filter); + PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig); void select_all(bool select, bool alternates = false); void select_one(size_t i, bool select); - void on_checkbox(const Checkbox *cbox, bool checked); + bool any_selected() const; int get_width() const { return width; } const std::vector& get_button_indexes() { return m_button_indexes; } + + static const std::string PRINTER_PLACEHOLDER; private: int width; - std::vector m_button_indexes; + + void on_checkbox(const Checkbox *cbox, bool checked); }; struct ConfigWizardPage: wxPanel @@ -87,37 +171,110 @@ struct ConfigWizardPage: wxPanel virtual ~ConfigWizardPage(); template - void append(T *thing, int proportion = 0, int flag = wxEXPAND|wxTOP|wxBOTTOM, int border = 10) + T* append(T *thing, int proportion = 0, int flag = wxEXPAND|wxTOP|wxBOTTOM, int border = 10) { content->Add(thing, proportion, flag, border); + return thing; } - void append_text(wxString text); + wxStaticText* append_text(wxString text); void append_spacer(int space); ConfigWizard::priv *wizard_p() const { return parent->p.get(); } virtual void apply_custom_config(DynamicPrintConfig &config) {} + virtual void set_run_reason(ConfigWizard::RunReason run_reason) {} + virtual void on_activate() {} }; struct PageWelcome: ConfigWizardPage { + wxStaticText *welcome_text; wxCheckBox *cbox_reset; PageWelcome(ConfigWizard *parent); bool reset_user_profile() const { return cbox_reset != nullptr ? cbox_reset->GetValue() : false; } + + virtual void set_run_reason(ConfigWizard::RunReason run_reason) override; }; struct PagePrinters: ConfigWizardPage { - std::vector printer_pickers; + Technology technology; + bool install; - PagePrinters(ConfigWizard *parent, wxString title, wxString shortname, const VendorProfile &vendor, unsigned indent, PrinterTechnology technology); + PagePrinters(ConfigWizard *parent, + wxString title, + wxString shortname, + const VendorProfile &vendor, + uint32_t indent, + Technology technology); void select_all(bool select, bool alternates = false); int get_width() const; + bool any_selected() const; + + virtual void set_run_reason(ConfigWizard::RunReason run_reason) override; +}; + +// Here we extend wxListBox and wxCheckListBox +// to make the client data API much easier to use. +template struct DataList : public T +{ + DataList(wxWindow *parent) : T(parent, wxID_ANY) {} + + // Note: We're _not_ using wxLB_SORT here because it doesn't do the right thing, + // eg. "ABS" is sorted before "(All)" + + int append(const std::string &label, const D *data) { + void *ptr = reinterpret_cast(const_cast(data)); + return this->Append(from_u8(label), ptr); + } + + int append(const wxString &label, const D *data) { + void *ptr = reinterpret_cast(const_cast(data)); + return this->Append(label, ptr); + } + + const D& get_data(int n) { + return *reinterpret_cast(this->GetClientData(n)); + } + + int find(const D &data) { + for (unsigned i = 0; i < this->GetCount(); i++) { + if (get_data(i) == data) { return i; } + } + + return wxNOT_FOUND; + } + + int size() { return this->GetCount(); } +}; + +typedef DataList StringList; +typedef DataList PresetList; + +struct PageMaterials: ConfigWizardPage +{ + Materials *materials; + StringList *list_l1, *list_l2; + PresetList *list_l3; + int sel1_prev, sel2_prev; + bool presets_loaded; + + static const std::string EMPTY; + + PageMaterials(ConfigWizard *parent, Materials *materials, wxString title, wxString shortname, wxString list1name); + + void reload_presets(); + void update_lists(int sel1, int sel2); + void select_material(int i); + void select_all(bool select); + void clear(); + + virtual void on_activate() override; }; struct PageCustom: ConfigWizardPage @@ -144,13 +301,22 @@ struct PageUpdate: ConfigWizardPage PageUpdate(ConfigWizard *parent); }; +struct PageMode: ConfigWizardPage +{ + wxRadioButton *radio_simple; + wxRadioButton *radio_advanced; + wxRadioButton *radio_expert; + + PageMode(ConfigWizard *parent); + + void serialize_mode(AppConfig *app_config) const; + + virtual void on_activate(); +}; + struct PageVendors: ConfigWizardPage { - std::vector pickers; - PageVendors(ConfigWizard *parent); - - void on_vendor_pick(size_t i); }; struct PageFirmware: ConfigWizardPage @@ -188,6 +354,11 @@ struct PageTemperatures: ConfigWizardPage virtual void apply_custom_config(DynamicPrintConfig &config); }; +// hypothetically, each vendor can has printers both of technologies (FFF and SLA) +typedef std::map> Pages3rdparty; + class ConfigWizardIndex: public wxPanel { @@ -204,12 +375,14 @@ public: void go_prev(); void go_next(); void go_to(size_t i); - void go_to(ConfigWizardPage *page); + void go_to(const ConfigWizardPage *page); void clear(); void msw_rescale(); int em() const { return em_w; } + + static const size_t NO_ITEM = size_t(-1); private: struct Item { @@ -222,12 +395,6 @@ private: int em_w; int em_h; - /* #ys_FIXME_delete_after_testing by VK - const wxBitmap bg; - const wxBitmap bullet_black; - const wxBitmap bullet_blue; - const wxBitmap bullet_white; - */ ScalableBitmap bg; ScalableBitmap bullet_black; ScalableBitmap bullet_blue; @@ -239,9 +406,6 @@ private: ssize_t item_hover; size_t last_page; - /* #ys_FIXME_delete_after_testing by VK - int item_height() const { return std::max(bullet_black.GetSize().GetHeight(), em_w) + em_w; } - */ int item_height() const { return std::max(bullet_black.bmp().GetSize().GetHeight(), em_w) + em_w; } void on_paint(wxPaintEvent &evt); @@ -250,14 +414,28 @@ private: wxDEFINE_EVENT(EVT_INDEX_PAGE, wxCommandEvent); + + +// ConfigWizard private data + +typedef std::map> PresetAliases; + struct ConfigWizard::priv { ConfigWizard *q; - ConfigWizard::RunReason run_reason; - AppConfig appconfig_vendors; - std::unordered_map vendors; - std::unordered_map vendors_rsrc; - std::unique_ptr custom_config; + ConfigWizard::RunReason run_reason = RR_USER; + AppConfig appconfig_new; // Backing for vendor/model/variant and material selections in the GUI + BundleMap bundles; // Holds all loaded config bundles, the key is the vendor names. + // Materials refers to Presets in those bundles by pointers. + // Also we update the is_visible flag in printer Presets according to the + // PrinterPickers state. + Materials filaments; // Holds available filament presets and their types & vendors + Materials sla_materials; // Ditto for SLA materials + PresetAliases aliases_fff; // Map of aliase to preset names + PresetAliases aliases_sla; // Map of aliase to preset names + std::unique_ptr custom_config; // Backing for custom printer definition + bool any_fff_selected; // Used to decide whether to display Filaments page + bool any_sla_selected; // Used to decide whether to display SLA Materials page wxScrolledWindow *hscroll = nullptr; wxBoxSizer *hscroll_sizer = nullptr; @@ -271,9 +449,19 @@ struct ConfigWizard::priv wxButton *btn_cancel = nullptr; PageWelcome *page_welcome = nullptr; - std::vector page_vendors; + PageMaterials *page_filaments = nullptr; + PageMaterials *page_sla_materials = nullptr; PageCustom *page_custom = nullptr; PageUpdate *page_update = nullptr; + PageMode *page_mode = nullptr; +#ifdef ALLOW_PRUSA_FIRST + PagePrinters *page_fff = nullptr; + PagePrinters *page_msla = nullptr; + PageVendors *page_vendors = nullptr; + Pages3rdparty pages_3rdparty; +#else + std::vector pages_vendors; +#endif // Custom setup pages PageFirmware *page_firmware = nullptr; @@ -281,25 +469,45 @@ struct ConfigWizard::priv PageDiameters *page_diams = nullptr; PageTemperatures *page_temps = nullptr; - priv(ConfigWizard *q) : q(q) {} + // Pointers to all pages (regardless or whether currently part of the ConfigWizardIndex) + std::vector all_pages; - void load_pages(bool custom_setup); + priv(ConfigWizard *q) + : q(q) + , filaments(T_FFF) + , sla_materials(T_SLA) + {} + + void load_pages(); void init_dialog_size(); - bool check_first_variant() const; void load_vendors(); void add_page(ConfigWizardPage *page); void enable_next(bool enable); + void set_start_page(ConfigWizard::StartPage start_page); +#ifdef ALLOW_PRUSA_FIRST + void create_3rdparty_pages(); +#endif + void set_run_reason(RunReason run_reason); + void update_materials(Technology technology); - void on_custom_setup(bool custom_wanted); + void on_custom_setup(); + void on_printer_pick(PagePrinters *page, const PrinterPickerEvent &evt); +#ifdef ALLOW_PRUSA_FIRST + void on_3rdparty_install(const VendorProfile *vendor, bool install); +#endif + bool check_material_config(); void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater); + // #ys_FIXME_alise + void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add); + + bool check_fff_selected(); // Used to decide whether to display Filaments page + bool check_sla_selected(); // Used to decide whether to display SLA Materials page int em() const { return index->em(); } }; - - } } diff --git a/src/slic3r/GUI/ExtruderSequenceDialog.cpp b/src/slic3r/GUI/ExtruderSequenceDialog.cpp new file mode 100644 index 000000000..a850ac192 --- /dev/null +++ b/src/slic3r/GUI/ExtruderSequenceDialog.cpp @@ -0,0 +1,235 @@ +#include "ExtruderSequenceDialog.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "GUI.hpp" +#include "GUI_App.hpp" +#include "I18N.hpp" +#include "OptionsGroup.hpp" + + +namespace Slic3r { +namespace GUI { + +ExtruderSequenceDialog::ExtruderSequenceDialog(const DoubleSlider::ExtrudersSequence& sequence) + : DPIDialog(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _(L("Set extruder sequence")), + wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), + m_sequence(sequence) +{ + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + SetDoubleBuffered(true); + SetFont(wxGetApp().normal_font()); + + auto main_sizer = new wxBoxSizer(wxVERTICAL); + const int em = wxGetApp().em_unit(); + + m_bmp_del = ScalableBitmap(this, "remove_copies"); + m_bmp_add = ScalableBitmap(this, "add_copies"); + + auto option_sizer = new wxBoxSizer(wxVERTICAL); + + auto intervals_box = new wxStaticBox(this, wxID_ANY, _(L("Set extruder change for every"))+ " : "); + auto intervals_box_sizer = new wxStaticBoxSizer(intervals_box, wxVERTICAL); + + m_intervals_grid_sizer = new wxFlexGridSizer(3, 5, em); + + auto editor_sz = wxSize(4*em, wxDefaultCoord); + + auto ID_RADIO_BUTTON = wxWindow::NewControlId(1); + + wxRadioButton* rb_by_layers = new wxRadioButton(this, ID_RADIO_BUTTON, "", wxDefaultPosition, wxDefaultSize, wxRB_GROUP); + rb_by_layers->Bind(wxEVT_RADIOBUTTON, [this](wxCommandEvent& event) { m_sequence.is_mm_intervals = false; }); + rb_by_layers->SetValue(!m_sequence.is_mm_intervals); + + wxStaticText* st_by_layers = new wxStaticText(this, wxID_ANY, _(L("layers"))); + m_interval_by_layers = new wxTextCtrl(this, wxID_ANY, + wxString::Format("%d", m_sequence.interval_by_layers), + wxDefaultPosition, editor_sz); + m_interval_by_layers->Bind(wxEVT_TEXT, [this, rb_by_layers](wxEvent&) + { + wxString str = m_interval_by_layers->GetValue(); + if (str.IsEmpty()) { + m_interval_by_layers->SetValue(wxString::Format("%d", m_sequence.interval_by_layers)); + return; + } + + int val = wxAtoi(str); + if (val < 1) { + m_interval_by_layers->SetValue("1"); + val = 1; + } + + if (m_sequence.interval_by_layers == val) + return; + + m_sequence.interval_by_layers = val; + + m_sequence.is_mm_intervals = false; + rb_by_layers->SetValue(true); + }); + + m_intervals_grid_sizer->Add(rb_by_layers, 0, wxALIGN_CENTER_VERTICAL); + m_intervals_grid_sizer->Add(m_interval_by_layers,0, wxALIGN_CENTER_VERTICAL); + m_intervals_grid_sizer->Add(st_by_layers,0, wxALIGN_CENTER_VERTICAL); + + wxRadioButton* rb_by_mm = new wxRadioButton(this, ID_RADIO_BUTTON, ""); + rb_by_mm->Bind(wxEVT_RADIOBUTTON, [this](wxEvent&) { m_sequence.is_mm_intervals = true; }); + rb_by_mm->SetValue(m_sequence.is_mm_intervals); + + wxStaticText* st_by_mm = new wxStaticText(this, wxID_ANY, _(L("mm"))); + m_interval_by_mm = new wxTextCtrl(this, wxID_ANY, + double_to_string(sequence.interval_by_mm), + wxDefaultPosition, editor_sz, wxTE_PROCESS_ENTER); + + auto change_value = [this]() + { + wxString str = m_interval_by_mm->GetValue(); + if (str.IsEmpty()) { + m_interval_by_mm->SetValue(wxString::Format("%d", m_sequence.interval_by_mm)); + return; + } + + str.Replace(",", ".", false); + double val; + if (str == "." || !str.ToCDouble(&val) || val <= 0.0) + val = 3.0; // default value + + if (fabs(m_sequence.interval_by_layers - val) < 0.001) + return; + + m_sequence.interval_by_mm = val; + }; + + m_interval_by_mm->Bind(wxEVT_TEXT, [this, rb_by_mm](wxEvent&) + { + m_sequence.is_mm_intervals = true; + rb_by_mm->SetValue(true); + }); + + m_interval_by_mm->Bind(wxEVT_KILL_FOCUS, [this, change_value](wxFocusEvent& event) + { + change_value(); + event.Skip(); + }); + + m_interval_by_mm->Bind(wxEVT_TEXT_ENTER, [this, change_value](wxEvent&) + { + change_value(); + }); + + m_intervals_grid_sizer->Add(rb_by_mm, 0, wxALIGN_CENTER_VERTICAL); + m_intervals_grid_sizer->Add(m_interval_by_mm,0, wxALIGN_CENTER_VERTICAL); + m_intervals_grid_sizer->Add(st_by_mm,0, wxALIGN_CENTER_VERTICAL); + + intervals_box_sizer->Add(m_intervals_grid_sizer, 0, wxLEFT, em); + option_sizer->Add(intervals_box_sizer, 0, wxEXPAND); + + + auto extruders_box = new wxStaticBox(this, wxID_ANY, _(L("Set extruder(tool) sequence"))+ " : "); + auto extruders_box_sizer = new wxStaticBoxSizer(extruders_box, wxVERTICAL); + + m_extruders_grid_sizer = new wxFlexGridSizer(3, 5, em); + + apply_extruder_sequence(); + + extruders_box_sizer->Add(m_extruders_grid_sizer, 0, wxALL, em); + option_sizer->Add(extruders_box_sizer, 0, wxEXPAND | wxTOP, em); + + main_sizer->Add(option_sizer, 0, wxEXPAND | wxALL, em); + + wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); + main_sizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, em); + + SetSizer(main_sizer); + main_sizer->SetSizeHints(this); + + /* For this moment min sizes for dialog and its sizer are calculated. + * If we left them, it can cause a problem with layouts during deleting of extruders + */ + if (m_sequence.extruders.size()>1) + { + wxSize sz = wxSize(-1, 10 * em); + SetMinSize(sz); + GetSizer()->SetMinSize(sz); + } +} + +void ExtruderSequenceDialog::apply_extruder_sequence() +{ + m_extruders_grid_sizer->Clear(true); + + for (size_t extruder=0; extruder < m_sequence.extruders.size(); ++extruder) + { + wxBitmapComboBox* extruder_selector = nullptr; + apply_extruder_selector(&extruder_selector, this, "", wxDefaultPosition, wxSize(15*wxGetApp().em_unit(), -1)); + extruder_selector->SetSelection(m_sequence.extruders[extruder]); + + extruder_selector->Bind(wxEVT_COMBOBOX, [this, extruder_selector, extruder](wxCommandEvent& evt) + { + m_sequence.extruders[extruder] = extruder_selector->GetSelection(); + evt.StopPropagation(); + }); + + auto del_btn = new ScalableButton(this, wxID_ANY, m_bmp_del); + del_btn->SetToolTip(_(L("Remove extruder from sequence"))); + if (m_sequence.extruders.size()==1) + del_btn->Disable(); + + del_btn->Bind(wxEVT_BUTTON, [this, extruder](wxEvent&) { + m_sequence.delete_extruder(extruder); + apply_extruder_sequence(); + }); + + auto add_btn = new ScalableButton(this, wxID_ANY, m_bmp_add); + add_btn->SetToolTip(_(L("Add extruder to sequence"))); + + add_btn->Bind(wxEVT_BUTTON, [this, extruder](wxEvent&) { + m_sequence.add_extruder(extruder); + apply_extruder_sequence(); + }); + + m_extruders_grid_sizer->Add(extruder_selector, 0, wxALIGN_CENTER_VERTICAL); + m_extruders_grid_sizer->Add(del_btn, 0, wxALIGN_CENTER_VERTICAL); + m_extruders_grid_sizer->Add(add_btn, 0, wxALIGN_CENTER_VERTICAL); + } + m_extruders_grid_sizer->ShowItems(true); // show items hidden in apply_extruder_selector() + + Fit(); + Refresh(); +} + +void ExtruderSequenceDialog::on_dpi_changed(const wxRect& suggested_rect) +{ + SetFont(wxGetApp().normal_font()); + + m_bmp_add.msw_rescale(); + m_bmp_del.msw_rescale(); + + const int em = em_unit(); + + m_intervals_grid_sizer->SetHGap(em); + m_intervals_grid_sizer->SetVGap(em); + m_extruders_grid_sizer->SetHGap(em); + m_extruders_grid_sizer->SetVGap(em); + + msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); + + // wxSize size = get_size(); + // SetMinSize(size); + + Fit(); + Refresh(); +} + +} +} + + diff --git a/src/slic3r/GUI/ExtruderSequenceDialog.hpp b/src/slic3r/GUI/ExtruderSequenceDialog.hpp new file mode 100644 index 000000000..3efd9e3a2 --- /dev/null +++ b/src/slic3r/GUI/ExtruderSequenceDialog.hpp @@ -0,0 +1,45 @@ +#ifndef slic3r_GUI_ExtruderSequenceDialog_hpp_ +#define slic3r_GUI_ExtruderSequenceDialog_hpp_ + +#include "GUI_Utils.hpp" +#include "wxExtensions.hpp" + +class wxTextCtrl; +class wxFlexGridSizer; + +namespace Slic3r { +namespace GUI { + +// ---------------------------------------------------------------------------- +// ExtruderSequenceDialog: a node inside ObjectDataViewModel +// ---------------------------------------------------------------------------- + +class ExtruderSequenceDialog: public DPIDialog +{ + ScalableBitmap m_bmp_del; + ScalableBitmap m_bmp_add; + DoubleSlider::ExtrudersSequence m_sequence; + + wxTextCtrl* m_interval_by_layers {nullptr}; + wxTextCtrl* m_interval_by_mm {nullptr}; + + wxFlexGridSizer* m_intervals_grid_sizer {nullptr}; + wxFlexGridSizer* m_extruders_grid_sizer {nullptr}; +public: + ExtruderSequenceDialog(const DoubleSlider::ExtrudersSequence& sequence); + + ~ExtruderSequenceDialog() {} + + DoubleSlider::ExtrudersSequence GetValue() { return m_sequence; } + +protected: + void apply_extruder_sequence(); + void on_dpi_changed(const wxRect& suggested_rect) override; + +}; + +} +} + + +#endif // slic3r_GUI_ExtruderSequenceDialog_hpp_ diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 3b815506a..634747589 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -11,6 +11,12 @@ #include #include +#ifdef __WXOSX__ +#define wxOSX true +#else +#define wxOSX false +#endif + namespace Slic3r { namespace GUI { wxString double_to_string(double const value, const int max_precision /*= 4*/) @@ -150,7 +156,13 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true case coFloat:{ if (m_opt.type == coPercent && !str.IsEmpty() && str.Last() == '%') str.RemoveLast(); - else if (check_value && !str.IsEmpty() && str.Last() == '%') { + else if (!str.IsEmpty() && str.Last() == '%') + { + if (!check_value) { + m_value.clear(); + break; + } + wxString label = m_Label->GetLabel(); if (label.Last() == '\n') label.RemoveLast(); while (label.Last() == ' ') label.RemoveLast(); @@ -169,13 +181,21 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true { if (m_opt.nullable && str == na_value()) val = ConfigOptionFloatsNullable::nil_value(); - else if (check_value && !str.ToCDouble(&val)) + else if (!str.ToCDouble(&val)) { + if (!check_value) { + m_value.clear(); + break; + } show_error(m_parent, _(L("Invalid numeric input."))); set_value(double_to_string(val), true); } - if (check_value && (m_opt.min > val || val > m_opt.max)) + if (m_opt.min > val || val > m_opt.max) { + if (!check_value) { + m_value.clear(); + break; + } show_error(m_parent, _(L("Input value is out of range"))); if (m_opt.min > val) val = m_opt.min; if (val > m_opt.max) val = m_opt.max; @@ -192,15 +212,24 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true double val = 0.; // Replace the first occurence of comma in decimal number. str.Replace(",", ".", false); - if (check_value && !str.ToCDouble(&val)) + if (!str.ToCDouble(&val)) { + if (!check_value) { + m_value.clear(); + break; + } show_error(m_parent, _(L("Invalid numeric input."))); set_value(double_to_string(val), true); } - else if (check_value && ((m_opt.sidetext.rfind("mm/s") != std::string::npos && val > m_opt.max) || + else if (((m_opt.sidetext.rfind("mm/s") != std::string::npos && val > m_opt.max) || (m_opt.sidetext.rfind("mm ") != std::string::npos && val > 1)) && (m_value.empty() || std::string(str.ToUTF8().data()) != boost::any_cast(m_value))) { + if (!check_value) { + m_value.clear(); + break; + } + const std::string sidetext = m_opt.sidetext.rfind("mm/s") != std::string::npos ? "mm/s" : "mm"; const wxString stVal = double_to_string(val, 2); const wxString msg_text = wxString::Format(_(L("Do you mean %s%% instead of %s %s?\n" @@ -281,7 +310,7 @@ void TextCtrl::BUILD() { auto temp = new wxTextCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, style); temp->SetFont(Slic3r::GUI::wxGetApp().normal_font()); - if (! m_opt.multiline) + if (! m_opt.multiline && !wxOSX) // Only disable background refresh for single line input fields, as they are completely painted over by the edit control. // This does not apply to the multi-line edit field, where the last line and a narrow frame around the text is not cleared. temp->SetBackgroundStyle(wxBG_STYLE_PAINT); @@ -351,6 +380,7 @@ bool TextCtrl::value_was_changed() boost::any val = m_value; wxString ret_str = static_cast(window)->GetValue(); // update m_value! + // ret_str might be changed inside get_value_by_opt_type get_value_by_opt_type(ret_str); switch (m_opt.type) { @@ -396,8 +426,10 @@ void TextCtrl::set_value(const boost::any& value, bool change_event/* = false*/) if (!change_event) { wxString ret_str = static_cast(window)->GetValue(); - // update m_value to correct work of next value_was_changed(), - // but don't check/change inputed value and don't show a warning message + /* Update m_value to correct work of next value_was_changed(). + * But after checking of entered value, don't fix the "incorrect" value and don't show a warning message, + * just clear m_value in this case. + */ get_value_by_opt_type(ret_str, false); } } @@ -465,7 +497,7 @@ void CheckBox::BUILD() { // Set Label as a string of at least one space simbol to correct system scaling of a CheckBox auto temp = new wxCheckBox(m_parent, wxID_ANY, wxString(" "), wxDefaultPosition, size); temp->SetFont(Slic3r::GUI::wxGetApp().normal_font()); - temp->SetBackgroundStyle(wxBG_STYLE_PAINT); + if (!wxOSX) temp->SetBackgroundStyle(wxBG_STYLE_PAINT); temp->SetValue(check_value); if (m_opt.readonly) temp->Disable(); @@ -575,7 +607,7 @@ void SpinCtrl::BUILD() { auto temp = new wxSpinCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, 0|wxTE_PROCESS_ENTER, min_val, max_val, default_value); temp->SetFont(Slic3r::GUI::wxGetApp().normal_font()); - temp->SetBackgroundStyle(wxBG_STYLE_PAINT); + if (!wxOSX) temp->SetBackgroundStyle(wxBG_STYLE_PAINT); // XXX: On OS X the wxSpinCtrl widget is made up of two subwidgets, unfortunatelly // the kill focus event is not propagated to the encompassing widget, @@ -691,7 +723,7 @@ void Choice::BUILD() { } temp->SetFont(Slic3r::GUI::wxGetApp().normal_font()); - temp->SetBackgroundStyle(wxBG_STYLE_PAINT); + if (!wxOSX) temp->SetBackgroundStyle(wxBG_STYLE_PAINT); // recast as a wxWindow to fit the calling convention window = dynamic_cast(temp); @@ -1086,7 +1118,7 @@ void ColourPicker::BUILD() auto temp = new wxColourPickerCtrl(m_parent, wxID_ANY, clr, wxDefaultPosition, size); temp->SetFont(Slic3r::GUI::wxGetApp().normal_font()); - temp->SetBackgroundStyle(wxBG_STYLE_PAINT); + if (!wxOSX) temp->SetBackgroundStyle(wxBG_STYLE_PAINT); // // recast as a wxWindow to fit the calling convention window = dynamic_cast(temp); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index eb1ddfd8b..1cb3fee3c 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7,6 +7,9 @@ #include "libslic3r/ClipperUtils.hpp" #include "libslic3r/PrintConfig.hpp" #include "libslic3r/GCode/PreviewData.hpp" +#if ENABLE_THUMBNAIL_GENERATOR +#include "libslic3r/GCode/ThumbnailData.hpp" +#endif // ENABLE_THUMBNAIL_GENERATOR #include "libslic3r/Geometry.hpp" #include "libslic3r/ExtrusionEntity.hpp" #include "libslic3r/Utils.hpp" @@ -19,9 +22,11 @@ #include "slic3r/GUI/PresetBundle.hpp" #include "slic3r/GUI/Tab.hpp" #include "slic3r/GUI/GUI_Preview.hpp" + #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" #include "GUI_ObjectManipulation.hpp" +#include "Mouse3DController.hpp" #include "I18N.hpp" #if ENABLE_RETINA_GL @@ -127,6 +132,9 @@ GLCanvas3D::LayersEditing::LayersEditing() , m_object_max_z(0.f) , m_slicing_parameters(nullptr) , m_layer_height_profile_modified(false) +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + , m_adaptive_cusp(0.2f) +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE , state(Unknown) , band_width(2.0f) , strength(0.005f) @@ -147,7 +155,9 @@ GLCanvas3D::LayersEditing::~LayersEditing() } const float GLCanvas3D::LayersEditing::THICKNESS_BAR_WIDTH = 70.0f; +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE const float GLCanvas3D::LayersEditing::THICKNESS_RESET_BUTTON_HEIGHT = 22.0f; +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename) { @@ -214,13 +224,103 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const if (!m_enabled) return; +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + static const ImVec4 orange(0.757f, 0.404f, 0.216f, 1.0f); + + const Size& cnv_size = canvas.get_canvas_size(); + float canvas_w = (float)cnv_size.get_width(); + float canvas_h = (float)cnv_size.get_height(); + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + imgui.set_next_window_pos(canvas_w - imgui.get_style_scaling() * THICKNESS_BAR_WIDTH, canvas_h, ImGuiCond_Always, 1.0f, 1.0f); + imgui.set_next_window_bg_alpha(0.5f); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + + imgui.begin(_(L("Layer height profile")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); + + ImGui::PushStyleColor(ImGuiCol_Text, orange); + imgui.text(_(L("Left mouse button:"))); + ImGui::PopStyleColor(); + ImGui::SameLine(); + imgui.text(_(L("Add detail"))); + + ImGui::PushStyleColor(ImGuiCol_Text, orange); + imgui.text(_(L("Right mouse button:"))); + ImGui::PopStyleColor(); + ImGui::SameLine(); + imgui.text(_(L("Remove detail"))); + + ImGui::PushStyleColor(ImGuiCol_Text, orange); + imgui.text(_(L("Shift + Left mouse button:"))); + ImGui::PopStyleColor(); + ImGui::SameLine(); + imgui.text(_(L("Reset to base"))); + + ImGui::PushStyleColor(ImGuiCol_Text, orange); + imgui.text(_(L("Shift + Right mouse button:"))); + ImGui::PopStyleColor(); + ImGui::SameLine(); + imgui.text(_(L("Smoothing"))); + + ImGui::PushStyleColor(ImGuiCol_Text, orange); + imgui.text(_(L("Mouse wheel:"))); + ImGui::PopStyleColor(); + ImGui::SameLine(); + imgui.text(_(L("Increase/decrease edit area"))); + + ImGui::Separator(); + if (imgui.button(_(L("Adaptive")))) + wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, m_adaptive_cusp)); + + ImGui::SameLine(); + float text_align = ImGui::GetCursorPosX(); + imgui.text(_(L("Cusp (mm)"))); + ImGui::SameLine(); + float widget_align = ImGui::GetCursorPosX(); + ImGui::PushItemWidth(imgui.get_style_scaling() * 120.0f); + m_adaptive_cusp = clamp((float)m_slicing_parameters->min_layer_height, (float)m_slicing_parameters->max_layer_height, m_adaptive_cusp); + ImGui::SliderFloat("", &m_adaptive_cusp, (float)m_slicing_parameters->min_layer_height, (float)m_slicing_parameters->max_layer_height, "%.2f"); + + ImGui::Separator(); + if (imgui.button(_(L("Smooth")))) + wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), HeightProfileSmoothEvent(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, m_smooth_params)); + + ImGui::SameLine(); + ImGui::SetCursorPosX(text_align); + imgui.text(_(L("Radius"))); + ImGui::SameLine(); + ImGui::SetCursorPosX(widget_align); + ImGui::PushItemWidth(imgui.get_style_scaling() * 120.0f); + int radius = (int)m_smooth_params.radius; + if (ImGui::SliderInt("##1", &radius, 1, 10)) + m_smooth_params.radius = (unsigned int)radius; + + ImGui::SetCursorPosX(text_align); + imgui.text(_(L("Keep min"))); + ImGui::SameLine(); + ImGui::SetCursorPosX(widget_align); + ImGui::PushItemWidth(imgui.get_style_scaling() * 120.0f); + imgui.checkbox("##2", m_smooth_params.keep_min); + + ImGui::Separator(); + if (imgui.button(_(L("Reset")))) + wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE)); + + imgui.end(); + + ImGui::PopStyleVar(); + + const Rect& bar_rect = get_bar_rect_viewport(canvas); +#else const Rect& bar_rect = get_bar_rect_viewport(canvas); const Rect& reset_rect = get_reset_rect_viewport(canvas); _render_tooltip_texture(canvas, bar_rect, reset_rect); _render_reset_texture(reset_rect); - _render_active_object_annotations(canvas, bar_rect); - _render_profile(bar_rect); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + render_active_object_annotations(canvas, bar_rect); + render_profile(bar_rect); } float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas) @@ -245,11 +345,13 @@ bool GLCanvas3D::LayersEditing::bar_rect_contains(const GLCanvas3D& canvas, floa return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom()); } +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool GLCanvas3D::LayersEditing::reset_rect_contains(const GLCanvas3D& canvas, float x, float y) { const Rect& rect = get_reset_rect_screen(canvas); return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom()); } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas) { @@ -257,9 +359,14 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas) float w = (float)cnv_size.get_width(); float h = (float)cnv_size.get_height(); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + return Rect(w - thickness_bar_width(canvas), 0.0f, w, h); +#else return Rect(w - thickness_bar_width(canvas), 0.0f, w, h - reset_button_height(canvas)); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE } +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE Rect GLCanvas3D::LayersEditing::get_reset_rect_screen(const GLCanvas3D& canvas) { const Size& cnv_size = canvas.get_canvas_size(); @@ -268,6 +375,7 @@ Rect GLCanvas3D::LayersEditing::get_reset_rect_screen(const GLCanvas3D& canvas) return Rect(w - thickness_bar_width(canvas), h - reset_button_height(canvas), w, h); } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas) { @@ -278,9 +386,14 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas) float zoom = (float)canvas.get_camera().get_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom); +#else return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE } +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas) { const Size& cnv_size = canvas.get_canvas_size(); @@ -292,13 +405,43 @@ Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom); } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - -bool GLCanvas3D::LayersEditing::_is_initialized() const +bool GLCanvas3D::LayersEditing::is_initialized() const { return m_shader.is_initialized(); } +std::string GLCanvas3D::LayersEditing::get_tooltip(const GLCanvas3D& canvas) const +{ + std::string ret; + if (m_enabled && (m_layer_height_profile.size() >= 4)) + { + float z = get_cursor_z_relative(canvas); + if (z != -1000.0f) + { + z *= m_object_max_z; + + float h = 0.0f; + for (size_t i = m_layer_height_profile.size() - 2; i >= 2; i -= 2) + { + float zi = m_layer_height_profile[i]; + float zi_1 = m_layer_height_profile[i - 2]; + if ((zi_1 <= z) && (z <= zi)) + { + float dz = zi - zi_1; + h = (dz != 0.0f) ? lerp(m_layer_height_profile[i - 1], m_layer_height_profile[i + 1], (z - zi_1) / dz) : m_layer_height_profile[i + 1]; + break; + } + } + if (h > 0.0f) + ret = std::to_string(h); + } + } + return ret; +} + +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const { // TODO: do this with ImGui @@ -344,8 +487,9 @@ void GLCanvas3D::LayersEditing::_render_reset_texture(const Rect& reset_rect) co GLTexture::render_texture(m_reset_texture.get_id(), reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top()); } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const +void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const { m_shader.start_using(); @@ -376,7 +520,7 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas m_shader.stop_using(); } -void GLCanvas3D::LayersEditing::_render_profile(const Rect& bar_rect) const +void GLCanvas3D::LayersEditing::render_profile(const Rect& bar_rect) const { //FIXME show some kind of legend. @@ -493,6 +637,27 @@ void GLCanvas3D::LayersEditing::reset_layer_height_profile(GLCanvas3D& canvas) canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp) +{ + this->update_slicing_parameters(); + m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, *m_model_object, cusp); + const_cast(m_model_object)->layer_height_profile = m_layer_height_profile; + m_layers_texture.valid = false; + canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); +} + +void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_params) +{ + this->update_slicing_parameters(); + + m_layer_height_profile = smooth_height_profile(m_layer_height_profile, *m_slicing_parameters, smoothing_params); + const_cast(m_model_object)->layer_height_profile = m_layer_height_profile; + m_layers_texture.valid = false; + canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); +} +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void GLCanvas3D::LayersEditing::generate_layer_height_texture() { this->update_slicing_parameters(); @@ -527,7 +692,7 @@ void GLCanvas3D::LayersEditing::accept_changes(GLCanvas3D& canvas) { if (last_object_id >= 0) { if (m_layer_height_profile_modified) { - wxGetApp().plater()->take_snapshot(_(L("Layers heights"))); + wxGetApp().plater()->take_snapshot(_(L("Layer height profile-Manual edit"))); const_cast(m_model_object)->layer_height_profile = m_layer_height_profile; canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } @@ -554,6 +719,7 @@ float GLCanvas3D::LayersEditing::thickness_bar_width(const GLCanvas3D &canvas) * THICKNESS_BAR_WIDTH; } +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE float GLCanvas3D::LayersEditing::reset_button_height(const GLCanvas3D &canvas) { return @@ -564,6 +730,7 @@ float GLCanvas3D::LayersEditing::reset_button_height(const GLCanvas3D &canvas) #endif * THICKNESS_RESET_BUTTON_HEIGHT; } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE const Point GLCanvas3D::Mouse::Drag::Invalid_2D_Point(INT_MAX, INT_MAX); @@ -833,20 +1000,29 @@ GLCanvas3D::LegendTexture::LegendTexture() { } -void GLCanvas3D::LegendTexture::fill_color_print_legend_values(const GCodePreviewData& preview_data, const GLCanvas3D& canvas, - std::vector>& cp_legend_values) +void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D& canvas, + const std::vector& colors_in, + std::vector& colors, + std::vector& cp_legend_items) { - if (preview_data.extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint && - wxGetApp().extruders_edited_cnt() == 1) // show color change legend only for single-material presets + std::vector custom_gcode_per_height = wxGetApp().plater()->model().custom_gcode_per_height; + + const int extruders_cnt = wxGetApp().extruders_edited_cnt(); + if (extruders_cnt == 1) { - auto& config = wxGetApp().preset_bundle->project_config; - const std::vector& color_print_values = config.option("colorprint_heights")->values; + if (custom_gcode_per_height.empty()) { + cp_legend_items.push_back(I18N::translate_utf8(L("Default print color"))); + colors = colors_in; + return; + } + std::vector> cp_values; - if (!color_print_values.empty()) { std::vector print_zs = canvas.get_current_print_zs(true); - for (auto cp_value : color_print_values) + for (auto custom_code : custom_gcode_per_height) { - auto lower_b = std::lower_bound(print_zs.begin(), print_zs.end(), cp_value - DoubleSlider::epsilon()); + if (custom_code.gcode != ColorChangeCode) + continue; + auto lower_b = std::lower_bound(print_zs.begin(), print_zs.end(), custom_code.height - DoubleSlider::epsilon()); if (lower_b == print_zs.end()) continue; @@ -855,25 +1031,94 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_values(const GCodePrevie double previous_z = lower_b == print_zs.begin() ? 0.0 : *(--lower_b); // to avoid duplicate values, check adding values - if (cp_legend_values.empty() || - !(cp_legend_values.back().first == previous_z && cp_legend_values.back().second == current_z) ) - cp_legend_values.push_back(std::pair(previous_z, current_z)); + if (cp_values.empty() || + !(cp_values.back().first == previous_z && cp_values.back().second == current_z)) + cp_values.push_back(std::pair(previous_z, current_z)); } + + const auto items_cnt = (int)cp_values.size(); + if (items_cnt == 0) // There is no one color change, but there is/are some pause print or custom Gcode + { + cp_legend_items.push_back(I18N::translate_utf8(L("Default print color"))); + cp_legend_items.push_back(I18N::translate_utf8(L("Pause print or custom G-code"))); + colors = colors_in; + return; } + + const int color_cnt = (int)colors_in.size() / 4; + colors.resize(colors_in.size(), 0.0); + + ::memcpy((void*)(colors.data()), (const void*)(colors_in.data() + (color_cnt - 1) * 4), 4 * sizeof(float)); + cp_legend_items.push_back(I18N::translate_utf8(L("Pause print or custom G-code"))); + size_t color_pos = 4; + + for (int i = items_cnt; i >= 0; --i, color_pos+=4) + { + // update colors for color print item + ::memcpy((void*)(colors.data() + color_pos), (const void*)(colors_in.data() + i * 4), 4 * sizeof(float)); + + // create label for color print item + std::string id_str = std::to_string(i + 1) + ": "; + + if (i == 0) { + cp_legend_items.push_back(id_str + (boost::format(I18N::translate_utf8(L("up to %.2f mm"))) % cp_values[0].first).str()); + break; + } + if (i == items_cnt) { + cp_legend_items.push_back(id_str + (boost::format(I18N::translate_utf8(L("above %.2f mm"))) % cp_values[i - 1].second).str()); + continue; +} + + cp_legend_items.push_back(id_str + (boost::format(I18N::translate_utf8(L("%.2f - %.2f mm"))) % cp_values[i - 1].second % cp_values[i].first).str()); + } + } + else +{ + // colors = colors_in; + const int color_cnt = (int)colors_in.size() / 4; + colors.resize(colors_in.size(), 0.0); + + ::memcpy((void*)(colors.data()), (const void*)(colors_in.data()), 4 * extruders_cnt * sizeof(float)); + size_t color_pos = 4 * extruders_cnt; + size_t color_in_pos = 4 * (color_cnt - 1); + + for (unsigned int i = 0; i < (unsigned int)extruders_cnt; ++i) + cp_legend_items.push_back((boost::format(I18N::translate_utf8(L("Extruder %d"))) % (i + 1)).str()); + + ::memcpy((void*)(colors.data() + color_pos), (const void*)(colors_in.data() + color_in_pos), 4 * sizeof(float)); + color_pos += 4; + color_in_pos -= 4; + cp_legend_items.push_back(I18N::translate_utf8(L("Pause print or custom G-code"))); + + int cnt = custom_gcode_per_height.size(); + for (int i = cnt-1; i >= 0; --i) + if (custom_gcode_per_height[i].gcode == ColorChangeCode) { + ::memcpy((void*)(colors.data() + color_pos), (const void*)(colors_in.data() + color_in_pos), 4 * sizeof(float)); + color_pos += 4; + color_in_pos -= 4; + cp_legend_items.push_back((boost::format(I18N::translate_utf8(L("Color change for Extruder %d at %.2f mm"))) % custom_gcode_per_height[i].extruder % custom_gcode_per_height[i].height).str()); + } } } -bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, const std::vector& tool_colors, const GLCanvas3D& canvas, bool compress) +bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, const std::vector& tool_colors_in, const GLCanvas3D& canvas, bool compress) { reset(); // collects items to render auto title = _(preview_data.get_legend_title()); - std::vector> cp_legend_values; - fill_color_print_legend_values(preview_data, canvas, cp_legend_values); + std::vector cp_legend_items; + std::vector cp_colors; - const GCodePreviewData::LegendItemsList& items = preview_data.get_legend_items(tool_colors, cp_legend_values); + if (preview_data.extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint) + { + cp_legend_items.reserve(cp_colors.size()); + fill_color_print_legend_items(canvas, tool_colors_in, cp_colors, cp_legend_items); + } + + const std::vector& tool_colors = preview_data.extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint ? cp_colors : tool_colors_in; + const GCodePreviewData::LegendItemsList& items = preview_data.get_legend_items(tool_colors, cp_legend_items); unsigned int items_count = (unsigned int)items.size(); if (items_count == 0) @@ -1115,6 +1360,15 @@ wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent); wxDEFINE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent); wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +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); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + +#if ENABLE_THUMBNAIL_GENERATOR +const double GLCanvas3D::DefaultCameraZoomToBoxMarginFactor = 1.25; +#endif // ENABLE_THUMBNAIL_GENERATOR GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar) : m_canvas(canvas) @@ -1365,7 +1619,7 @@ void GLCanvas3D::set_model(Model* model) void GLCanvas3D::bed_shape_changed() { - m_camera.set_scene_box(scene_bounding_box()); + refresh_camera_scene_box(); m_camera.requires_zoom_to_bed = true; m_dirty = true; if (m_bed.is_prusa()) @@ -1391,7 +1645,7 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const BoundingBoxf3 GLCanvas3D::scene_bounding_box() const { BoundingBoxf3 bb = volumes_bounding_box(); - bb.merge(m_bed.get_bounding_box(false)); + bb.merge(m_bed.get_bounding_box(true)); if (m_config != nullptr) { @@ -1413,6 +1667,32 @@ bool GLCanvas3D::is_layers_editing_allowed() const return m_layers_editing.is_allowed(); } +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +void GLCanvas3D::reset_layer_height_profile() +{ + wxGetApp().plater()->take_snapshot(_(L("Layer height profile-Reset"))); + m_layers_editing.reset_layer_height_profile(*this); + m_layers_editing.state = LayersEditing::Completed; + m_dirty = true; +} + +void GLCanvas3D::adaptive_layer_height_profile(float cusp) +{ + wxGetApp().plater()->take_snapshot(_(L("Layer height profile-Adaptive"))); + m_layers_editing.adaptive_layer_height_profile(*this, cusp); + m_layers_editing.state = LayersEditing::Completed; + m_dirty = true; +} + +void GLCanvas3D::smooth_layer_height_profile(const HeightProfileSmoothingParams& smoothing_params) +{ + wxGetApp().plater()->take_snapshot(_(L("Layer height profile-Smooth all"))); + m_layers_editing.smooth_layer_height_profile(*this, smoothing_params); + m_layers_editing.state = LayersEditing::Completed; + m_dirty = true; +} +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + bool GLCanvas3D::is_reload_delayed() const { return m_reload_delayed; @@ -1539,10 +1819,11 @@ void GLCanvas3D::render() return; } + const Size& cnv_size = get_canvas_size(); + if (m_camera.requires_zoom_to_bed) { zoom_to_bed(); - const Size& cnv_size = get_canvas_size(); _resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); m_camera.requires_zoom_to_bed = false; } @@ -1583,7 +1864,7 @@ void GLCanvas3D::render() _render_objects(); _render_sla_slices(); _render_selection(); - _render_bed(theta); + _render_bed(theta, true); #if ENABLE_RENDER_SELECTION_CENTER _render_selection_center(); @@ -1633,6 +1914,8 @@ void GLCanvas3D::render() m_camera.debug_render(); #endif // ENABLE_CAMERA_STATISTICS + wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); + wxGetApp().imgui()->render(); m_canvas->SwapBuffers(); @@ -1643,6 +1926,18 @@ void GLCanvas3D::render() #endif // ENABLE_RENDER_STATISTICS } +#if ENABLE_THUMBNAIL_GENERATOR +void GLCanvas3D::render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) +{ + switch (GLCanvas3DManager::get_framebuffers_type()) + { + case GLCanvas3DManager::FB_Arb: { _render_thumbnail_framebuffer(thumbnail_data, w, h, printable_only, parts_only, show_bed, transparent_background); break; } + case GLCanvas3DManager::FB_Ext: { _render_thumbnail_framebuffer_ext(thumbnail_data, w, h, printable_only, parts_only, show_bed, transparent_background); break; } + default: { _render_thumbnail_legacy(thumbnail_data, w, h, printable_only, parts_only, show_bed, transparent_background); break; } + } +} +#endif // ENABLE_THUMBNAIL_GENERATOR + void GLCanvas3D::select_all() { m_selection.add_all(); @@ -1765,9 +2060,20 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re std::vector model_volume_state; std::vector aux_volume_state; + struct GLVolumeState { + GLVolumeState() : + volume_idx(-1) {} + GLVolumeState(const GLVolume* volume, unsigned int volume_idx) : + composite_id(volume->composite_id), volume_idx(volume_idx) {} + + GLVolume::CompositeID composite_id; + // Volume index in the old GLVolume vector. + size_t volume_idx; + }; + // SLA steps to pull the preview meshes for. typedef std::array SLASteps; - SLASteps sla_steps = { slaposSupportTree, slaposBasePool }; + SLASteps sla_steps = { slaposSupportTree, slaposPad }; struct SLASupportState { std::array::value> step; }; @@ -1776,6 +2082,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re std::vector instance_ids_selected; std::vector map_glvolume_old_to_new(m_volumes.volumes.size(), size_t(-1)); + std::vector deleted_volumes; std::vector glvolumes_new; glvolumes_new.reserve(m_volumes.volumes.size()); auto model_volume_state_lower = [](const ModelVolumeState &m1, const ModelVolumeState &m2) { return m1.geometry_id < m2.geometry_id; }; @@ -1838,7 +2145,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // to revert to before it was generated). We only reuse the volume if that's not the case. if (m_model->objects[volume->composite_id.object_id]->sla_points_status != sla::PointsStatus::NoPoints) mvs = &(*it); - } else { + } + else { auto it = std::lower_bound(model_volume_state.begin(), model_volume_state.end(), key, model_volume_state_lower); if (it != model_volume_state.end() && it->geometry_id == key.geometry_id) mvs = &(*it); @@ -1855,8 +2163,12 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re volume_idx_wipe_tower_old = (int)volume_id; } if (! m_reload_delayed) + { + deleted_volumes.emplace_back(volume, volume_id); delete volume; - } else { + } + } + else { // This GLVolume will be reused. volume->set_sla_shift_z(0.0); map_glvolume_old_to_new[volume_id] = glvolumes_new.size(); @@ -1884,6 +2196,16 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re bool update_object_list = false; + auto find_old_volume_id = [&deleted_volumes](const GLVolume::CompositeID& id) -> unsigned int { + for (unsigned int i = 0; i < (unsigned int)deleted_volumes.size(); ++i) + { + const GLVolumeState& v = deleted_volumes[i]; + if (v.composite_id == id) + return v.volume_idx; + } + return (unsigned int)-1; + }; + if (m_volumes.volumes != glvolumes_new) update_object_list = true; m_volumes.volumes = std::move(glvolumes_new); @@ -1898,6 +2220,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id); if (it->new_geometry()) { // New volume. + unsigned int old_id = find_old_volume_id(it->composite_id); + if (old_id != (unsigned int)-1) + map_glvolume_old_to_new[old_id] = 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; @@ -1999,19 +2324,17 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re float a = dynamic_cast(m_config->option("wipe_tower_rotation_angle"))->value; const Print *print = m_process->fff_print(); - float depth = print->get_wipe_tower_depth(); - // Calculate wipe tower brim spacing. const DynamicPrintConfig &print_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; double layer_height = print_config.opt_float("layer_height"); double first_layer_height = print_config.get_abs_value("first_layer_height", layer_height); - float brim_spacing = print->config().nozzle_diameter.values[0] * 1.25f - first_layer_height * (1. - M_PI_4); + double nozzle_diameter = print->config().nozzle_diameter.values[0]; + float depth = print->wipe_tower_data(extruders_count, first_layer_height, nozzle_diameter).depth; + float brim_width = print->wipe_tower_data(extruders_count, first_layer_height, nozzle_diameter).brim_width; - if (!print->is_step_done(psWipeTower)) - depth = (900.f/w) * (float)(extruders_count - 1); int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview( 1000, x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower), - brim_spacing * 4.5f, m_initialized); + brim_width, m_initialized); if (volume_idx_wipe_tower_old != -1) map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new; } @@ -2043,20 +2366,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, contained_min_one && !m_model->objects.empty() && state != ModelInstance::PVS_Partly_Outside)); - -// #ys_FIXME_delete_after_testing -// bool contained = m_volumes.check_outside_state(m_config, &state); -// if (!contained) -// { -// _set_warning_texture(WarningTexture::ObjectOutside, true); -// post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, state == ModelInstance::PVS_Fully_Outside)); -// } -// else -// { -// m_volumes.reset_outside_state(); -// _set_warning_texture(WarningTexture::ObjectOutside, false); -// post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, !m_model->objects.empty())); -// } } else { @@ -2065,7 +2374,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, false)); } - m_camera.set_scene_box(scene_bounding_box()); + refresh_camera_scene_box(); if (m_selection.is_empty()) { @@ -2101,12 +2410,12 @@ static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume& static void load_gcode_retractions(const GCodePreviewData::Retraction& retractions, GLCanvas3D::GCodePreviewVolumeIndex::EType extrusion_type, GLVolumeCollection &volumes, GLCanvas3D::GCodePreviewVolumeIndex &volume_index, bool gl_initialized) { - volume_index.first_volumes.emplace_back(extrusion_type, 0, (unsigned int)volumes.volumes.size()); - // nothing to render, return if (retractions.positions.empty()) return; + volume_index.first_volumes.emplace_back(extrusion_type, 0, (unsigned int)volumes.volumes.size()); + GLVolume *volume = volumes.new_nontoolpath_volume(retractions.color.rgba, VERTEX_BUFFER_RESERVE_SIZE); GCodePreviewData::Retraction::PositionsList copy(retractions.positions); @@ -2172,6 +2481,9 @@ void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const ++ idx_volume_index_src; idx_volume_of_this_type_last = (idx_volume_index_src + 1 == m_gcode_preview_volume_index.first_volumes.size()) ? m_volumes.volumes.size() : m_gcode_preview_volume_index.first_volumes[idx_volume_index_src + 1].id; idx_volume_of_this_type_first_new = idx_volume_dst; + if (idx_volume_src == idx_volume_of_this_type_last) + // Empty sequence of volumes for the current index item. + continue; } if (! m_volumes.volumes[idx_volume_src]->print_zs.empty()) m_volumes.volumes[idx_volume_dst ++] = m_volumes.volumes[idx_volume_src]; @@ -2210,7 +2522,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) @@ -2305,14 +2617,21 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) m_dirty |= m_main_toolbar.update_items_state(); m_dirty |= m_undoredo_toolbar.update_items_state(); m_dirty |= m_view_toolbar.update_items_state(); + bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(m_camera); + m_dirty |= mouse3d_controller_applied; if (!m_dirty) return; _refresh_if_shown_on_screen(); - if (m_keep_dirty) + if (m_keep_dirty || mouse3d_controller_applied) + { m_dirty = true; + evt.RequestMore(); +} + else + m_dirty = false; } void GLCanvas3D::on_char(wxKeyEvent& evt) @@ -2357,6 +2676,20 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) #endif /* __APPLE__ */ post_event(SimpleEvent(EVT_GLTOOLBAR_COPY)); break; + +#ifdef __APPLE__ + case 'm': + case 'M': +#else /* __APPLE__ */ + case WXK_CONTROL_M: +#endif /* __APPLE__ */ + { + Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller(); + controller.show_settings_dialog(!controller.is_settings_dialog_shown()); + m_dirty = true; + break; + } + #ifdef __APPLE__ case 'v': case 'V': @@ -2424,11 +2757,11 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case 'B': case 'b': { zoom_to_bed(); break; } case 'I': - case 'i': { set_camera_zoom(1.0); break; } + case 'i': { _update_camera_zoom(1.0); break; } case 'K': case 'k': { m_camera.select_next_type(); m_dirty = true; break; } case 'O': - case 'o': { set_camera_zoom(-1.0); break; } + case 'o': { _update_camera_zoom(-1.0); break; } #if ENABLE_RENDER_PICKING_PASS case 'T': case 't': { @@ -2531,6 +2864,11 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) { + // try to filter out events coming from mouse 3d + Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller(); + if (controller.process_mouse_wheel()) + return; + if (!m_initialized) return; @@ -2575,7 +2913,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) return; // Calculate the zoom delta and apply it to the current zoom factor - set_camera_zoom((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta()); + _update_camera_zoom((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta()); } void GLCanvas3D::on_timer(wxTimerEvent& evt) @@ -2634,19 +2972,6 @@ std::string format_mouse_event_debug_message(const wxMouseEvent &evt) void GLCanvas3D::on_mouse(wxMouseEvent& evt) { - auto mouse_up_cleanup = [this](){ - m_moving = false; - m_mouse.drag.move_volume_idx = -1; - m_mouse.set_start_position_3D_as_invalid(); - m_mouse.set_start_position_2D_as_invalid(); - m_mouse.dragging = false; - m_mouse.ignore_left_up = false; - m_dirty = true; - - if (m_canvas->HasCapture()) - m_canvas->ReleaseMouse(); - }; - #if ENABLE_RETINA_GL const float scale = m_retina_helper->get_scale_factor(); evt.SetX(evt.GetX() * scale); @@ -2780,6 +3105,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_layers_editing.state = LayersEditing::Editing; _perform_layer_editing_action(&evt); } +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE else if ((layer_editing_object_idx != -1) && m_layers_editing.reset_rect_contains(*this, pos(0), pos(1))) { if (evt.LeftDown()) @@ -2792,6 +3118,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_dirty = true; } } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE else if (evt.LeftDown() && (evt.ShiftDown() || evt.AltDown()) && m_picking_enabled) { if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports) @@ -2853,6 +3180,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) volume_bbox.offset(1.0); if (volume_bbox.contains(m_mouse.scene_position)) { + m_volumes.volumes[volume_idx]->hover = GLVolume::HS_None; // The dragging operation is initiated. m_mouse.drag.move_volume_idx = volume_idx; m_selection.start_dragging(); @@ -2922,6 +3250,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if ((m_layers_editing.state != LayersEditing::Unknown) && (layer_editing_object_idx != -1)) { + set_tooltip(""); if (m_layers_editing.state == LayersEditing::Editing) _perform_layer_editing_action(&evt); } @@ -3031,6 +3360,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_mouse.position = pos.cast(); std::string tooltip = ""; + if (tooltip.empty()) + tooltip = m_layers_editing.get_tooltip(*this); + if (tooltip.empty()) tooltip = m_gizmos.get_tooltip(); @@ -3370,13 +3702,6 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type) m_dirty = true; } -void GLCanvas3D::set_camera_zoom(double zoom) -{ - const Size& cnv_size = get_canvas_size(); - m_camera.set_zoom(zoom, _max_bounding_box(false, true), cnv_size.get_width(), cnv_size.get_height()); - m_dirty = true; -} - void GLCanvas3D::update_gizmos_on_off_state() { set_as_dirty(); @@ -3389,11 +3714,10 @@ void GLCanvas3D::handle_sidebar_focus_event(const std::string& opt_key, bool foc m_sidebar_field = focus_on ? opt_key : ""; if (!m_sidebar_field.empty()) - { m_gizmos.reset_all_states(); + m_dirty = true; } -} void GLCanvas3D::handle_layers_data_focus_event(const t_layer_height_range range, const EditorType type) { @@ -3484,6 +3808,20 @@ void GLCanvas3D::export_toolpaths_to_obj(const char* filename) const m_volumes.export_toolpaths_to_obj(filename); } +void GLCanvas3D::mouse_up_cleanup() +{ + m_moving = false; + m_mouse.drag.move_volume_idx = -1; + m_mouse.set_start_position_3D_as_invalid(); + m_mouse.set_start_position_2D_as_invalid(); + m_mouse.dragging = false; + m_mouse.ignore_left_up = false; + m_dirty = true; + + if (m_canvas->HasCapture()) + m_canvas->ReleaseMouse(); +} + bool GLCanvas3D::_is_shown_on_screen() const { return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; @@ -3525,6 +3863,357 @@ void GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) imgui->end(); } +#if ENABLE_THUMBNAIL_GENERATOR +#define ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT 0 +#if ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT +static void debug_output_thumbnail(const ThumbnailData& thumbnail_data) +{ + // debug export of generated image + wxImage image(thumbnail_data.width, thumbnail_data.height); + image.InitAlpha(); + + for (unsigned int r = 0; r < thumbnail_data.height; ++r) + { + unsigned int rr = (thumbnail_data.height - 1 - r) * thumbnail_data.width; + for (unsigned int c = 0; c < thumbnail_data.width; ++c) + { + unsigned char* px = (unsigned char*)thumbnail_data.pixels.data() + 4 * (rr + c); + image.SetRGB((int)c, (int)r, px[0], px[1], px[2]); + image.SetAlpha((int)c, (int)r, px[3]); + } + } + + image.SaveFile("C:/prusa/test/test.png", wxBITMAP_TYPE_PNG); +} +#endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT + +void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) +{ + auto is_visible = [](const GLVolume& v) -> bool + { + bool ret = v.printable; + ret &= (!v.shader_outside_printer_detection_enabled || !v.is_outside); + return ret; + }; + + static const GLfloat orange[] = { 0.923f, 0.504f, 0.264f, 1.0f }; + static const GLfloat gray[] = { 0.64f, 0.64f, 0.64f, 1.0f }; + + GLVolumePtrs visible_volumes; + + for (GLVolume* vol : m_volumes.volumes) + { + if (!vol->is_modifier && !vol->is_wipe_tower && (!parts_only || (vol->composite_id.volume_id >= 0))) + { + if (!printable_only || is_visible(*vol)) + visible_volumes.push_back(vol); + } + } + + if (visible_volumes.empty()) + return; + + BoundingBoxf3 box; + for (const GLVolume* vol : visible_volumes) + { + box.merge(vol->transformed_bounding_box()); + } + + Camera camera; + camera.set_type(Camera::Ortho); + camera.zoom_to_volumes(visible_volumes, thumbnail_data.width, thumbnail_data.height); + camera.apply_viewport(0, 0, thumbnail_data.width, thumbnail_data.height); + camera.apply_view_matrix(); + + double near_z = -1.0; + double far_z = -1.0; + + if (show_bed) + { + // extends the near and far z of the frustrum to avoid the bed being clipped + + // box in eye space + BoundingBoxf3 t_bed_box = m_bed.get_bounding_box(true).transformed(camera.get_view_matrix()); + near_z = -t_bed_box.max(2); + far_z = -t_bed_box.min(2); + } + + camera.apply_projection(box, near_z, far_z); + + if (transparent_background) + glsafe(::glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); + + glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); + glsafe(::glEnable(GL_DEPTH_TEST)); + + m_shader.start_using(); + + GLint shader_id = m_shader.get_shader_program_id(); + GLint color_id = ::glGetUniformLocation(shader_id, "uniform_color"); + GLint print_box_detection_id = ::glGetUniformLocation(shader_id, "print_box.volume_detection"); + glcheck(); + + if (print_box_detection_id != -1) + glsafe(::glUniform1i(print_box_detection_id, 0)); + + for (const GLVolume* vol : visible_volumes) + { + if (color_id >= 0) + glsafe(::glUniform4fv(color_id, 1, (vol->printable && !vol->is_outside) ? orange : gray)); + else + glsafe(::glColor4fv((vol->printable && !vol->is_outside) ? orange : gray)); + + vol->render(); + } + + m_shader.stop_using(); + + glsafe(::glDisable(GL_DEPTH_TEST)); + + if (show_bed) + _render_bed(camera.get_theta(), false); + + if (transparent_background) + glsafe(::glClearColor(1.0f, 1.0f, 1.0f, 1.0f)); +} + +void GLCanvas3D::_render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) +{ + thumbnail_data.set(w, h); + if (!thumbnail_data.is_valid()) + return; + + bool multisample = m_multisample_allowed; + if (multisample) + glsafe(::glEnable(GL_MULTISAMPLE)); + + GLint max_samples; + glsafe(::glGetIntegerv(GL_MAX_SAMPLES, &max_samples)); + GLsizei num_samples = max_samples / 2; + + GLuint render_fbo; + glsafe(::glGenFramebuffers(1, &render_fbo)); + glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, render_fbo)); + + GLuint render_tex = 0; + GLuint render_tex_buffer = 0; + if (multisample) + { + // use renderbuffer instead of texture to avoid the need to use glTexImage2DMultisample which is available only since OpenGL 3.2 + glsafe(::glGenRenderbuffers(1, &render_tex_buffer)); + glsafe(::glBindRenderbuffer(GL_RENDERBUFFER, render_tex_buffer)); + glsafe(::glRenderbufferStorageMultisample(GL_RENDERBUFFER, num_samples, GL_RGBA8, w, h)); + glsafe(::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, render_tex_buffer)); + } + else + { + glsafe(::glGenTextures(1, &render_tex)); + glsafe(::glBindTexture(GL_TEXTURE_2D, render_tex)); + glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, render_tex, 0)); + } + + GLuint render_depth; + glsafe(::glGenRenderbuffers(1, &render_depth)); + glsafe(::glBindRenderbuffer(GL_RENDERBUFFER, render_depth)); + if (multisample) + glsafe(::glRenderbufferStorageMultisample(GL_RENDERBUFFER, num_samples, GL_DEPTH_COMPONENT24, w, h)); + else + glsafe(::glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, w, h)); + + glsafe(::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, render_depth)); + + GLenum drawBufs[] = { GL_COLOR_ATTACHMENT0 }; + glsafe(::glDrawBuffers(1, drawBufs)); + + if (::glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) + { + _render_thumbnail_internal(thumbnail_data, printable_only, parts_only, show_bed, transparent_background); + + if (multisample) + { + GLuint resolve_fbo; + glsafe(::glGenFramebuffers(1, &resolve_fbo)); + glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, resolve_fbo)); + + GLuint resolve_tex; + glsafe(::glGenTextures(1, &resolve_tex)); + glsafe(::glBindTexture(GL_TEXTURE_2D, resolve_tex)); + glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolve_tex, 0)); + + glsafe(::glDrawBuffers(1, drawBufs)); + + if (::glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) + { + glsafe(::glBindFramebuffer(GL_READ_FRAMEBUFFER, render_fbo)); + glsafe(::glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo)); + glsafe(::glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_LINEAR)); + + glsafe(::glBindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo)); + glsafe(::glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (void*)thumbnail_data.pixels.data())); + } + + glsafe(::glDeleteTextures(1, &resolve_tex)); + glsafe(::glDeleteFramebuffers(1, &resolve_fbo)); + } + else + glsafe(::glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (void*)thumbnail_data.pixels.data())); + +#if ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT + debug_output_thumbnail(thumbnail_data); +#endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT + } + + glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0)); + glsafe(::glDeleteRenderbuffers(1, &render_depth)); + if (render_tex_buffer != 0) + glsafe(::glDeleteRenderbuffers(1, &render_tex_buffer)); + if (render_tex != 0) + glsafe(::glDeleteTextures(1, &render_tex)); + glsafe(::glDeleteFramebuffers(1, &render_fbo)); + + if (multisample) + glsafe(::glDisable(GL_MULTISAMPLE)); +} + +void GLCanvas3D::_render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) +{ + thumbnail_data.set(w, h); + if (!thumbnail_data.is_valid()) + return; + + bool multisample = m_multisample_allowed; + if (multisample) + glsafe(::glEnable(GL_MULTISAMPLE)); + + GLint max_samples; + glsafe(::glGetIntegerv(GL_MAX_SAMPLES_EXT, &max_samples)); + GLsizei num_samples = max_samples / 2; + + GLuint render_fbo; + glsafe(::glGenFramebuffersEXT(1, &render_fbo)); + glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, render_fbo)); + + GLuint render_tex = 0; + GLuint render_tex_buffer = 0; + if (multisample) + { + // use renderbuffer instead of texture to avoid the need to use glTexImage2DMultisample which is available only since OpenGL 3.2 + glsafe(::glGenRenderbuffersEXT(1, &render_tex_buffer)); + glsafe(::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render_tex_buffer)); + glsafe(::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, num_samples, GL_RGBA8, w, h)); + glsafe(::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, render_tex_buffer)); + } + else + { + glsafe(::glGenTextures(1, &render_tex)); + glsafe(::glBindTexture(GL_TEXTURE_2D, render_tex)); + glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, render_tex, 0)); + } + + GLuint render_depth; + glsafe(::glGenRenderbuffersEXT(1, &render_depth)); + glsafe(::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render_depth)); + if (multisample) + glsafe(::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, num_samples, GL_DEPTH_COMPONENT24, w, h)); + else + glsafe(::glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, w, h)); + + glsafe(::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, render_depth)); + + GLenum drawBufs[] = { GL_COLOR_ATTACHMENT0 }; + glsafe(::glDrawBuffers(1, drawBufs)); + + if (::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT) + { + _render_thumbnail_internal(thumbnail_data, printable_only, parts_only, show_bed, transparent_background); + + if (multisample) + { + GLuint resolve_fbo; + glsafe(::glGenFramebuffersEXT(1, &resolve_fbo)); + glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, resolve_fbo)); + + GLuint resolve_tex; + glsafe(::glGenTextures(1, &resolve_tex)); + glsafe(::glBindTexture(GL_TEXTURE_2D, resolve_tex)); + glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + glsafe(::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, resolve_tex, 0)); + + glsafe(::glDrawBuffers(1, drawBufs)); + + if (::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT) + { + glsafe(::glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, render_fbo)); + glsafe(::glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, resolve_fbo)); + glsafe(::glBlitFramebufferEXT(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_LINEAR)); + + glsafe(::glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, resolve_fbo)); + glsafe(::glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (void*)thumbnail_data.pixels.data())); + } + + glsafe(::glDeleteTextures(1, &resolve_tex)); + glsafe(::glDeleteFramebuffersEXT(1, &resolve_fbo)); + } + else + glsafe(::glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (void*)thumbnail_data.pixels.data())); + +#if ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT + debug_output_thumbnail(thumbnail_data); +#endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT + } + + glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)); + glsafe(::glDeleteRenderbuffersEXT(1, &render_depth)); + if (render_tex_buffer != 0) + glsafe(::glDeleteRenderbuffersEXT(1, &render_tex_buffer)); + if (render_tex != 0) + glsafe(::glDeleteTextures(1, &render_tex)); + glsafe(::glDeleteFramebuffersEXT(1, &render_fbo)); + + if (multisample) + glsafe(::glDisable(GL_MULTISAMPLE)); +} + +void GLCanvas3D::_render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) +{ + // check that thumbnail size does not exceed the default framebuffer size + const Size& cnv_size = get_canvas_size(); + unsigned int cnv_w = (unsigned int)cnv_size.get_width(); + unsigned int cnv_h = (unsigned int)cnv_size.get_height(); + if ((w > cnv_w) || (h > cnv_h)) + { + float ratio = std::min((float)cnv_w / (float)w, (float)cnv_h / (float)h); + w = (unsigned int)(ratio * (float)w); + h = (unsigned int)(ratio * (float)h); + } + + thumbnail_data.set(w, h); + if (!thumbnail_data.is_valid()) + return; + + _render_thumbnail_internal(thumbnail_data, printable_only, parts_only, show_bed, transparent_background); + + glsafe(::glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (void*)thumbnail_data.pixels.data())); +#if ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT + debug_output_thumbnail(thumbnail_data); +#endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT + + // restore the default framebuffer size to avoid flickering on the 3D scene + m_camera.apply_viewport(0, 0, cnv_size.get_width(), cnv_size.get_height()); +} +#endif // ENABLE_THUMBNAIL_GENERATOR + bool GLCanvas3D::_init_toolbars() { if (!_init_main_toolbar()) @@ -3814,8 +4503,6 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) // updates camera m_camera.apply_viewport(0, 0, w, h); - - m_dirty = false; } BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_bed_model) const @@ -3836,12 +4523,27 @@ BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_be return bb; } +#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_dirty = true; +} +#else void GLCanvas3D::_zoom_to_box(const BoundingBoxf3& box) { const Size& cnv_size = get_canvas_size(); m_camera.zoom_to_box(box, cnv_size.get_width(), cnv_size.get_height()); m_dirty = true; } +#endif // ENABLE_THUMBNAIL_GENERATOR + +void GLCanvas3D::_update_camera_zoom(double zoom) +{ + m_camera.update_zoom(zoom); + m_dirty = true; +} void GLCanvas3D::_refresh_if_shown_on_screen() { @@ -4024,13 +4726,13 @@ void GLCanvas3D::_render_background() const glsafe(::glPopMatrix()); } -void GLCanvas3D::_render_bed(float theta) const +void GLCanvas3D::_render_bed(float theta, bool show_axes) const { float scale_factor = 1.0; #if ENABLE_RETINA_GL scale_factor = m_retina_helper->get_scale_factor(); #endif // ENABLE_RETINA_GL - m_bed.render(const_cast(*this), theta, scale_factor); + m_bed.render(const_cast(*this), theta, scale_factor, show_axes); } void GLCanvas3D::_render_objects() const @@ -4038,7 +4740,9 @@ void GLCanvas3D::_render_objects() const if (m_volumes.empty()) return; +#if !ENABLE_THUMBNAIL_GENERATOR glsafe(::glEnable(GL_LIGHTING)); +#endif // !ENABLE_THUMBNAIL_GENERATOR glsafe(::glEnable(GL_DEPTH_TEST)); m_camera_clipping_plane = m_gizmos.get_sla_clipping_plane(); @@ -4082,7 +4786,9 @@ void GLCanvas3D::_render_objects() const m_shader.stop_using(); m_camera_clipping_plane = ClippingPlane::ClipsNothing(); +#if !ENABLE_THUMBNAIL_GENERATOR glsafe(::glDisable(GL_LIGHTING)); +#endif // !ENABLE_THUMBNAIL_GENERATOR } void GLCanvas3D::_render_selection() const @@ -4638,7 +5344,7 @@ 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); @@ -4650,11 +5356,14 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c bool has_infill; bool has_support; const std::vector* tool_colors; - const std::vector* color_print_values; + bool is_single_material_print; + int extruders_cnt; + 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 static const float* color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish + static const float* color_pause_or_custom_code() { static float color[4] = { 0.5f, 0.5f, 0.5f, 1.f }; return color; } // gray // For cloring by a tool, return a parsed color. bool color_by_tool() const { return tool_colors != nullptr; } @@ -4664,9 +5373,106 @@ 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 { - auto it = std::lower_bound(color_print_values->begin(), color_print_values->end(), layers[layer_idx]->print_z + EPSILON); + const Model::CustomGCode 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(); } + + const size_t color_print_color_idx_by_layer_idx_and_extruder(const size_t layer_idx, const int extruder) const + { + 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) + { return fabs(code.height - print_z) < EPSILON; }); + if (it != color_print_values->end()) + { + const std::string& code = it->gcode; + // pause print or custom Gcode + if (code == PausePrintCode || + (code != ColorChangeCode && code != ExtruderChangeCode)) + return number_tools()-1; // last color item is a gray color for pause print or custom G-code + + // change tool (extruder) + if (code == ExtruderChangeCode) + return get_color_idx_for_tool_change(it, extruder); + // change color for current extruder + if (code == ColorChangeCode) { + int color_idx = get_color_idx_for_color_change(it, extruder); + if (color_idx >= 0) + return color_idx; + } + } + + const Model::CustomGCode value(print_z + EPSILON, "", 0, ""); + it = std::lower_bound(color_print_values->begin(), color_print_values->end(), value); + while (it != color_print_values->begin()) + { + --it; + // change color for current extruder + if (it->gcode == ColorChangeCode) { + int color_idx = get_color_idx_for_color_change(it, extruder); + if (color_idx >= 0) + return color_idx; + } + // change tool (extruder) + if (it->gcode == ExtruderChangeCode) + return get_color_idx_for_tool_change(it, extruder); + } + + return std::min(extruders_cnt - 1, std::max(extruder - 1, 0));; + } + + private: + int get_m600_color_idx(std::vector::const_iterator it) const + { + int shift = 0; + while (it != color_print_values->begin()) { + --it; + if (it->gcode == ColorChangeCode) + shift++; + } + return extruders_cnt + shift; + } + + 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" + return std::min(extruders_cnt - 1, std::max(current_extruder - 1, 0)); + + auto it_n = it; + while (it_n != color_print_values->begin()) { + --it_n; + if (it_n->gcode == ColorChangeCode && it_n->extruder == current_extruder) + return get_m600_color_idx(it_n); + } + + 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 + { + if (extruders_cnt == 1) + return get_m600_color_idx(it); + + auto it_n = it; + bool is_tool_change = false; + while (it_n != color_print_values->begin()) { + --it_n; + if (it_n->gcode == ExtruderChangeCode) { + is_tool_change = true; + if (it_n->extruder == it->extruder || (it_n->extruder == 0 && it->extruder == extruder)) + return get_m600_color_idx(it); + break; + } + } + if (!is_tool_change && it->extruder == extruder) + return get_m600_color_idx(it); + + return -1; + } + } ctxt; ctxt.has_perimeters = print_object.is_step_done(posPerimeters); @@ -4674,6 +5480,8 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c ctxt.has_support = print_object.is_step_done(posSupportMaterial); ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; ctxt.color_print_values = color_print_values.empty() ? nullptr : &color_print_values; + ctxt.is_single_material_print = this->fff_print()->extruders().size()==1; + ctxt.extruders_cnt = wxGetApp().extruders_edited_cnt(); ctxt.shifted_copies = &print_object.copies(); @@ -4697,6 +5505,8 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c // Maximum size of an allocation block: 32MB / sizeof(float) BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start" << m_volumes.log_memory_info() << log_memory_info(); + const bool is_selected_separate_extruder = m_selected_extruder > 0 && ctxt.color_by_color_print(); + //FIXME Improve the heuristics for a grain size. size_t grain_size = std::max(ctxt.layers.size() / 16, size_t(1)); tbb::spin_mutex new_volume_mutex; @@ -4714,32 +5524,18 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c const size_t volumes_cnt_initial = m_volumes.volumes.size(); tbb::parallel_for( tbb::blocked_range(0, ctxt.layers.size(), grain_size), - [&ctxt, &new_volume](const tbb::blocked_range& range) { + [&ctxt, &new_volume, is_selected_separate_extruder, this](const tbb::blocked_range& range) { GLVolumePtrs vols; std::vector color_print_layer_to_glvolume; auto volume = [&ctxt, &vols, &color_print_layer_to_glvolume, &range](size_t layer_idx, int extruder, int feature) -> GLVolume& { return *vols[ctxt.color_by_color_print() ? - color_print_layer_to_glvolume[layer_idx - range.begin()] : + ctxt.color_print_color_idx_by_layer_idx_and_extruder(layer_idx, extruder) : ctxt.color_by_tool() ? std::min(ctxt.number_tools() - 1, std::max(extruder - 1, 0)) : feature ]; }; - if (ctxt.color_by_color_print()) { - // Create a map from the layer index to a GLVolume, which is initialized with the correct layer span color. - std::vector color_print_tool_to_glvolume(ctxt.number_tools(), -1); - color_print_layer_to_glvolume.reserve(range.end() - range.begin()); - vols.reserve(ctxt.number_tools()); - for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - int idx_tool = (int)ctxt.color_print_color_idx_by_layer_idx(idx_layer); - if (color_print_tool_to_glvolume[idx_tool] == -1) { - color_print_tool_to_glvolume[idx_tool] = (int)vols.size(); - vols.emplace_back(new_volume(ctxt.color_tool(idx_tool))); - } - color_print_layer_to_glvolume.emplace_back(color_print_tool_to_glvolume[idx_tool]); - } - } - else if (ctxt.color_by_tool()) { + if (ctxt.color_by_color_print() || ctxt.color_by_tool()) { for (size_t i = 0; i < ctxt.number_tools(); ++i) vols.emplace_back(new_volume(ctxt.color_tool(i))); } @@ -4750,6 +5546,26 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c vol->indexed_vertex_array.reserve(VERTEX_BUFFER_RESERVE_SIZE / 6); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) { const Layer *layer = ctxt.layers[idx_layer]; + + if (is_selected_separate_extruder) + { + bool at_least_one_has_correct_extruder = false; + for (const LayerRegion* layerm : layer->regions()) + { + if (layerm->slices.surfaces.empty()) + continue; + const PrintRegionConfig& cfg = layerm->region()->config(); + if (cfg.perimeter_extruder.value == m_selected_extruder || + cfg.infill_extruder.value == m_selected_extruder || + cfg.solid_infill_extruder.value == m_selected_extruder ) { + at_least_one_has_correct_extruder = true; + break; + } + } + if (!at_least_one_has_correct_extruder) + continue; + } + for (GLVolume *vol : vols) if (vol->print_zs.empty() || vol->print_zs.back() != layer->print_z) { vol->print_zs.push_back(layer->print_z); @@ -4758,6 +5574,14 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c } for (const Point © : *ctxt.shifted_copies) { for (const LayerRegion *layerm : layer->regions()) { + if (is_selected_separate_extruder) + { + const PrintRegionConfig& cfg = layerm->region()->config(); + if (cfg.perimeter_extruder.value != m_selected_extruder || + cfg.infill_extruder.value != m_selected_extruder || + cfg.solid_infill_extruder.value != m_selected_extruder) + continue; + } if (ctxt.has_perimeters) _3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy, volume(idx_layer, layerm->region()->config().perimeter_extruder.value, 0)); @@ -4991,19 +5815,21 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat // helper functions to select data in dependence of the extrusion view type struct Helper { - static float path_filter(GCodePreviewData::Extrusion::EViewType type, const ExtrusionPath& path) + static float path_filter(GCodePreviewData::Extrusion::EViewType type, const GCodePreviewData::Extrusion::Path& path) { switch (type) { case GCodePreviewData::Extrusion::FeatureType: // The role here is used for coloring. - return (float)path.role(); + return (float)path.extrusion_role; case GCodePreviewData::Extrusion::Height: return path.height; case GCodePreviewData::Extrusion::Width: return path.width; case GCodePreviewData::Extrusion::Feedrate: return path.feedrate; + case GCodePreviewData::Extrusion::FanSpeed: + return path.fan_speed; case GCodePreviewData::Extrusion::VolumetricRate: return path.feedrate * (float)path.mm3_per_mm; case GCodePreviewData::Extrusion::Tool: @@ -5030,6 +5856,8 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat return data.get_width_color(value); case GCodePreviewData::Extrusion::Feedrate: return data.get_feedrate_color(value); + case GCodePreviewData::Extrusion::FanSpeed: + return data.get_fan_speed_color(value); case GCodePreviewData::Extrusion::VolumetricRate: return data.get_volumetric_rate_color(value); case GCodePreviewData::Extrusion::Tool: @@ -5042,11 +5870,8 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat case GCodePreviewData::Extrusion::ColorPrint: { int color_cnt = (int)tool_colors.size() / 4; + int val = value > color_cnt ? color_cnt - 1 : value; - int val = int(value); - while (val >= color_cnt) - val -= color_cnt; - GCodePreviewData::Color color; ::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + val * 4), 4 * sizeof(float)); @@ -5073,15 +5898,15 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat { std::vector num_paths_per_role(size_t(erCount), 0); for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) - for (const ExtrusionPath& path : layer.paths) - ++ num_paths_per_role[size_t(path.role())]; + for (const GCodePreviewData::Extrusion::Path &path : layer.paths) + ++ num_paths_per_role[size_t(path.extrusion_role)]; std::vector> roles_values; roles_values.assign(size_t(erCount), std::vector()); for (size_t i = 0; i < roles_values.size(); ++ i) roles_values[i].reserve(num_paths_per_role[i]); for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) - for (const ExtrusionPath& path : layer.paths) - roles_values[size_t(path.role())].emplace_back(Helper::path_filter(preview_data.extrusion.view_type, path)); + for (const GCodePreviewData::Extrusion::Path &path : layer.paths) + roles_values[size_t(path.extrusion_role)].emplace_back(Helper::path_filter(preview_data.extrusion.view_type, path)); roles_filters.reserve(size_t(erCount)); size_t num_buffers = 0; for (std::vector &values : roles_values) { @@ -5107,11 +5932,14 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - populate volumes" << m_volumes.log_memory_info() << log_memory_info(); // populates volumes + const bool is_selected_separate_extruder = m_selected_extruder > 0 && preview_data.extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint; for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) { - for (const ExtrusionPath& path : layer.paths) + for (const GCodePreviewData::Extrusion::Path& path : layer.paths) { - std::vector> &filters = roles_filters[size_t(path.role())]; + if (is_selected_separate_extruder && path.extruder_id != m_selected_extruder - 1) + continue; + std::vector> &filters = roles_filters[size_t(path.extrusion_role)]; auto key = std::make_pair(Helper::path_filter(preview_data.extrusion.view_type, path), nullptr); auto it_filter = std::lower_bound(filters.begin(), filters.end(), key); assert(it_filter != filters.end() && key.first == it_filter->first); @@ -5121,7 +5949,7 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size()); vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size()); - _3DScene::extrusionentity_to_verts(path, layer.z, vol); + _3DScene::extrusionentity_to_verts(path.polyline, path.width, path.height, layer.z, vol); } // Ensure that no volume grows over the limits. If the volume is too large, allocate a new one. for (std::vector> &filters : roles_filters) { @@ -5286,20 +6114,18 @@ void GLCanvas3D::_load_fff_shells() // adds wipe tower's volume double max_z = print->objects()[0]->model_object()->get_model()->bounding_box().max(2); const PrintConfig& config = print->config(); - unsigned int extruders_count = config.nozzle_diameter.size(); + size_t extruders_count = config.nozzle_diameter.size(); if ((extruders_count > 1) && config.wipe_tower && !config.complete_objects) { - float depth = print->get_wipe_tower_depth(); - // Calculate wipe tower brim spacing. const DynamicPrintConfig &print_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; double layer_height = print_config.opt_float("layer_height"); double first_layer_height = print_config.get_abs_value("first_layer_height", layer_height); - float brim_spacing = print->config().nozzle_diameter.values[0] * 1.25f - first_layer_height * (1. - M_PI_4); + double nozzle_diameter = print->config().nozzle_diameter.values[0]; + float depth = print->wipe_tower_data(extruders_count, first_layer_height, nozzle_diameter).depth; + float brim_width = print->wipe_tower_data(extruders_count, first_layer_height, nozzle_diameter).brim_width; - if (!print->is_step_done(psWipeTower)) - depth = (900.f/config.wipe_tower_width) * (float)(extruders_count - 1); m_volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle, - !print->is_step_done(psWipeTower), brim_spacing * 4.5f, m_initialized); + !print->is_step_done(psWipeTower), brim_width, m_initialized); } } } @@ -5342,8 +6168,8 @@ void GLCanvas3D::_load_sla_shells() m_volumes.volumes.back()->extruder_id = obj->model_object()->volumes.front()->extruder_id(); if (obj->is_step_done(slaposSupportTree) && obj->has_mesh(slaposSupportTree)) add_volume(*obj, -int(slaposSupportTree), instance, obj->support_mesh(), GLVolume::SLA_SUPPORT_COLOR, true); - if (obj->is_step_done(slaposBasePool) && obj->has_mesh(slaposBasePool)) - add_volume(*obj, -int(slaposBasePool), instance, obj->pad_mesh(), GLVolume::SLA_PAD_COLOR, false); + if (obj->is_step_done(slaposPad) && obj->has_mesh(slaposPad)) + add_volume(*obj, -int(slaposPad), instance, obj->pad_mesh(), GLVolume::SLA_PAD_COLOR, false); } double shift_z = obj->get_current_elevation(); for (unsigned int i = initial_volumes_count; i < m_volumes.volumes.size(); ++ i) { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index fb767360c..3b23a74a5 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -36,6 +36,9 @@ class GLShader; class ExPolygon; class BackgroundSlicingProcess; class GCodePreviewData; +#if ENABLE_THUMBNAIL_GENERATOR +struct ThumbnailData; +#endif // ENABLE_THUMBNAIL_GENERATOR struct SlicingParameters; enum LayerHeightEditActionType : unsigned int; @@ -78,6 +81,8 @@ template using Vec2dsEvent = ArrayEvent; using Vec3dEvent = Event; template using Vec3dsEvent = ArrayEvent; +using HeightProfileSmoothEvent = Event; + wxDECLARE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent); @@ -101,9 +106,18 @@ wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent); wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent); wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +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); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class GLCanvas3D { +#if ENABLE_THUMBNAIL_GENERATOR + static const double DefaultCameraZoomToBoxMarginFactor; +#endif // ENABLE_THUMBNAIL_GENERATOR + public: struct GCodePreviewVolumeIndex { @@ -146,13 +160,17 @@ private: private: static const float THICKNESS_BAR_WIDTH; +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static const float THICKNESS_RESET_BUTTON_HEIGHT; +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool m_enabled; Shader m_shader; unsigned int m_z_texture_id; +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE mutable GLTexture m_tooltip_texture; mutable GLTexture m_reset_texture; +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // Not owned by LayersEditing. const DynamicPrintConfig *m_config; // ModelObject for the currently selected object (Model::objects[last_object_id]). @@ -161,9 +179,14 @@ private: float m_object_max_z; // Owned by LayersEditing. SlicingParameters *m_slicing_parameters; - std::vector m_layer_height_profile; + std::vector m_layer_height_profile; bool m_layer_height_profile_modified; +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + mutable float m_adaptive_cusp; + mutable HeightProfileSmoothingParams m_smooth_params; +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + class LayersTexture { public: @@ -210,28 +233,44 @@ private: void adjust_layer_height_profile(); void accept_changes(GLCanvas3D& canvas); void reset_layer_height_profile(GLCanvas3D& canvas); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp); + void smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_paramsn); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static float get_cursor_z_relative(const GLCanvas3D& canvas); static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static bool reset_rect_contains(const GLCanvas3D& canvas, float x, float y); +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static Rect get_bar_rect_screen(const GLCanvas3D& canvas); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static Rect get_reset_rect_screen(const GLCanvas3D& canvas); +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static Rect get_bar_rect_viewport(const GLCanvas3D& canvas); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static Rect get_reset_rect_viewport(const GLCanvas3D& canvas); +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE float object_max_z() const { return m_object_max_z; } + std::string get_tooltip(const GLCanvas3D& canvas) const; + private: - bool _is_initialized() const; + bool is_initialized() const; void generate_layer_height_texture(); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void _render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const; void _render_reset_texture(const Rect& reset_rect) const; - void _render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const; - void _render_profile(const Rect& bar_rect) const; +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const; + void render_profile(const Rect& bar_rect) const; void update_slicing_parameters(); static float thickness_bar_width(const GLCanvas3D &canvas); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static float reset_button_height(const GLCanvas3D &canvas); +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE }; struct Mouse @@ -345,8 +384,10 @@ private: public: LegendTexture(); - void fill_color_print_legend_values(const GCodePreviewData& preview_data, const GLCanvas3D& canvas, - std::vector>& cp_legend_values); + void fill_color_print_legend_items(const GLCanvas3D& canvas, + const std::vector& colors_in, + std::vector& colors, + std::vector& cp_legend_items); bool generate(const GCodePreviewData& preview_data, const std::vector& tool_colors, const GLCanvas3D& canvas, bool compress); @@ -436,6 +477,7 @@ private: #endif // ENABLE_RENDER_STATISTICS int m_imgui_undo_redo_hovered_pos{ -1 }; + int m_selected_extruder; public: GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar); @@ -486,6 +528,7 @@ public: void set_color_by(const std::string& value); const Camera& get_camera() const { return m_camera; } + Camera& get_camera() { return m_camera; } BoundingBoxf3 volumes_bounding_box() const; BoundingBoxf3 scene_bounding_box() const; @@ -493,6 +536,12 @@ public: bool is_layers_editing_enabled() const; bool is_layers_editing_allowed() const; +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void reset_layer_height_profile(); + void adaptive_layer_height_profile(float cusp); + void smooth_layer_height_profile(const HeightProfileSmoothingParams& smoothing_params); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + bool is_reload_delayed() const; void enable_layers_editing(bool enable); @@ -519,6 +568,11 @@ public: bool is_dragging() const { return m_gizmos.is_dragging() || m_moving; } void render(); +#if ENABLE_THUMBNAIL_GENERATOR + // printable_only == false -> render also non printable volumes as grayed + // parts_only == false -> render also sla support and pad + void render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background); +#endif // ENABLE_THUMBNAIL_GENERATOR void select_all(); void deselect_all(); @@ -537,7 +591,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(); @@ -564,8 +618,6 @@ public: void do_flatten(const Vec3d& normal, const std::string& snapshot_type); void do_mirror(const std::string& snapshot_type); - void set_camera_zoom(double zoom); - void update_gizmos_on_off_state(); void reset_all_gizmos() { m_gizmos.reset_all_states(); } @@ -578,6 +630,7 @@ public: int get_move_volume_id() const { return m_mouse.drag.move_volume_idx; } int get_first_hover_volume_idx() const { return m_hover_volume_idxs.empty() ? -1 : m_hover_volume_idxs.front(); } + void set_selected_extruder(int extruder) { m_selected_extruder = extruder;} class WipeTowerInfo { protected: @@ -624,6 +677,8 @@ public: bool has_toolpaths_to_export() const; void export_toolpaths_to_obj(const char* filename) const; + void mouse_up_cleanup(); + private: bool _is_shown_on_screen() const; @@ -636,14 +691,19 @@ private: BoundingBoxf3 _max_bounding_box(bool include_gizmos, bool include_bed_model) const; +#if ENABLE_THUMBNAIL_GENERATOR + void _zoom_to_box(const BoundingBoxf3& box, double margin_factor = DefaultCameraZoomToBoxMarginFactor); +#else void _zoom_to_box(const BoundingBoxf3& box); +#endif // ENABLE_THUMBNAIL_GENERATOR + void _update_camera_zoom(double zoom); void _refresh_if_shown_on_screen(); void _picking_pass() const; void _rectangular_selection_picking_pass() const; void _render_background() const; - void _render_bed(float theta) const; + void _render_bed(float theta, bool show_axes) const; void _render_objects() const; void _render_selection() const; #if ENABLE_RENDER_SELECTION_CENTER @@ -664,6 +724,15 @@ private: void _render_sla_slices() const; void _render_selection_sidebar_hints() const; void _render_undo_redo_stack(const bool is_undo, float pos_x); +#if ENABLE_THUMBNAIL_GENERATOR + void _render_thumbnail_internal(ThumbnailData& thumbnail_data, bool printable_only, bool parts_only, bool show_bed, bool transparent_background); + // render thumbnail using an off-screen framebuffer + void _render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background); + // render thumbnail using an off-screen framebuffer when GLEW_EXT_framebuffer_object is supported + void _render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background); + // render thumbnail using the default framebuffer + void _render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background); +#endif // ENABLE_THUMBNAIL_GENERATOR void _update_volumes_hover_state() const; @@ -686,7 +755,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 42ad41a10..6e71a49a6 100644 --- a/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -107,7 +107,9 @@ void GLCanvas3DManager::GLInfo::detect() const m_renderer = data; glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_max_tex_size)); - + + m_max_tex_size /= 2; + if (GLEW_EXT_texture_filter_anisotropic) glsafe(::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &m_max_anisotropy)); @@ -187,6 +189,7 @@ std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool exten GLCanvas3DManager::EMultisampleState GLCanvas3DManager::s_multisample = GLCanvas3DManager::MS_Unknown; bool GLCanvas3DManager::s_compressed_textures_supported = false; +GLCanvas3DManager::EFramebufferType GLCanvas3DManager::s_framebuffers_type = GLCanvas3DManager::FB_None; GLCanvas3DManager::GLInfo GLCanvas3DManager::s_gl_info; GLCanvas3DManager::GLCanvas3DManager() @@ -267,6 +270,13 @@ void GLCanvas3DManager::init_gl() else s_compressed_textures_supported = false; + if (GLEW_ARB_framebuffer_object) + s_framebuffers_type = FB_Arb; + else if (GLEW_EXT_framebuffer_object) + s_framebuffers_type = FB_Ext; + else + s_framebuffers_type = FB_None; + if (! s_gl_info.is_version_greater_or_equal_to(2, 0)) { // Complain about the OpenGL version. wxString message = wxString::Format( diff --git a/src/slic3r/GUI/GLCanvas3DManager.hpp b/src/slic3r/GUI/GLCanvas3DManager.hpp index 760266a27..940e0230a 100644 --- a/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -30,6 +30,13 @@ struct Camera; class GLCanvas3DManager { public: + enum EFramebufferType : unsigned char + { + FB_None, + FB_Arb, + FB_Ext + }; + class GLInfo { mutable bool m_detected; @@ -77,6 +84,7 @@ private: bool m_gl_initialized; static EMultisampleState s_multisample; static bool s_compressed_textures_supported; + static EFramebufferType s_framebuffers_type; public: GLCanvas3DManager(); @@ -97,6 +105,8 @@ public: static bool can_multisample() { return s_multisample == MS_Enabled; } static bool are_compressed_textures_supported() { return s_compressed_textures_supported; } + static bool are_framebuffers_supported() { return (s_framebuffers_type != FB_None); } + static EFramebufferType get_framebuffers_type() { return s_framebuffers_type; } static wxGLCanvas* create_wxglcanvas(wxWindow *parent); diff --git a/src/slic3r/GUI/GLShader.cpp b/src/slic3r/GUI/GLShader.cpp index 577f6e1b5..11f109fd5 100644 --- a/src/slic3r/GUI/GLShader.cpp +++ b/src/slic3r/GUI/GLShader.cpp @@ -118,9 +118,9 @@ bool GLShader::load_from_text(const char *fragment_shader, const char *vertex_sh glsafe(::glGetProgramiv(this->shader_program_id, GL_LINK_STATUS, ¶ms)); if (params == GL_FALSE) { // Linking failed. Get the log. - glsafe(::glGetProgramiv(this->vertex_program_id, GL_INFO_LOG_LENGTH, ¶ms)); + glsafe(::glGetProgramiv(this->shader_program_id, GL_INFO_LOG_LENGTH, ¶ms)); std::vector msg(params); - glsafe(::glGetProgramInfoLog(this->vertex_program_id, params, ¶ms, msg.data())); + glsafe(::glGetProgramInfoLog(this->shader_program_id, params, ¶ms, msg.data())); this->last_error = std::string("Shader linking failed:\n") + msg.data(); this->release(); return false; diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp index 55ca5f723..62129cfc0 100644 --- a/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -107,8 +107,8 @@ void GLTexture::Compressor::compress() break; // stb_dxt library, despite claiming that the needed size of the destination buffer is equal to (source buffer size)/4, - // crashes if doing so, so we start with twice the required size - level.compressed_data = std::vector(level.w * level.h * 2, 0); + // crashes if doing so, requiring a minimum of 16 bytes and up to a third of the source buffer size, so we set the destination buffer initial size to be half the source buffer size + level.compressed_data = std::vector(std::max((unsigned int)16, level.w * level.h * 2), 0); int compressed_size = 0; rygCompress(level.compressed_data.data(), level.src_data.data(), level.w, level.h, 1, compressed_size); level.compressed_data.resize(compressed_size); @@ -455,8 +455,7 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo int lod_w = m_width; int lod_h = m_height; GLint level = 0; - // we do not need to generate all levels down to 1x1 - while ((lod_w > 16) || (lod_h > 16)) + while ((lod_w > 1) || (lod_h > 1)) { ++level; @@ -600,8 +599,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo int lod_w = m_width; int lod_h = m_height; GLint level = 0; - // we do not need to generate all levels down to 1x1 - while ((lod_w > 16) || (lod_h > 16)) + while ((lod_w > 1) || (lod_h > 1)) { ++level; diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 51d787d9d..b955f39b4 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -368,7 +368,7 @@ void GLToolbar::get_additional_tooltip(int item_id, std::string& text) } } - text = L(""); + text.clear(); } void GLToolbar::set_additional_tooltip(int item_id, const std::string& text) @@ -443,7 +443,7 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) if (item_id == -1) { // mouse is outside the toolbar - m_tooltip = L(""); + m_tooltip.clear(); } else { @@ -610,7 +610,7 @@ void GLToolbar::do_action(GLToolbarItem::EActionType type, int item_id, GLCanvas std::string GLToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent) { if (!m_enabled) - return L(""); + return ""; switch (m_layout.type) { @@ -665,7 +665,7 @@ std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLC { const std::string& additional_tooltip = item->get_additional_tooltip(); if (!additional_tooltip.empty()) - tooltip += L("\n") + additional_tooltip; + tooltip += "\n" + additional_tooltip; } } @@ -769,7 +769,7 @@ std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCan { const std::string& additional_tooltip = item->get_additional_tooltip(); if (!additional_tooltip.empty()) - tooltip += L("\n") + additional_tooltip; + tooltip += "\n" + additional_tooltip; } } @@ -1194,7 +1194,12 @@ bool GLToolbar::generate_icons_texture() const states.push_back(std::make_pair(1, true)); } - bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, (unsigned int)(m_layout.icons_size * m_layout.scale), true); + unsigned int sprite_size_px = (unsigned int)(m_layout.icons_size * m_layout.scale); + // force even size + if (sprite_size_px % 2 != 0) + sprite_size_px += 1; + + bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, sprite_size_px, false); if (res) m_icons_texture_dirty = false; diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index cddd943fe..4e85967a5 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -101,49 +101,6 @@ const std::string& shortkey_alt_prefix() return str; } -bool config_wizard_startup(bool app_config_exists) -{ - if (!app_config_exists || wxGetApp().preset_bundle->printers.size() <= 1) { - config_wizard(ConfigWizard::RR_DATA_EMPTY); - return true; - } else if (get_app_config()->legacy_datadir()) { - // Looks like user has legacy pre-vendorbundle data directory, - // explain what this is and run the wizard - - MsgDataLegacy dlg; - dlg.ShowModal(); - - config_wizard(ConfigWizard::RR_DATA_LEGACY); - return true; - } - return false; -} - -void config_wizard(int reason) -{ - // Exit wizard if there are unsaved changes and the user cancels the action. - if (! wxGetApp().check_unsaved_changes()) - return; - - try { - ConfigWizard wizard(nullptr, static_cast(reason)); - wizard.run(wxGetApp().preset_bundle, wxGetApp().preset_updater); - } - catch (const std::exception &e) { - show_error(nullptr, e.what()); - } - - wxGetApp().load_current_presets(); - - if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA && model_has_multi_part_objects(wxGetApp().model())) - { - show_info(nullptr, - _(L("It's impossible to print multi-part object(s) with SLA technology.")) + "\n\n" + - _(L("Please check and fix your object list.")), - _(L("Attention!"))); - } -} - // opt_index = 0, by the reason of zero-index in ConfigOptionVector by default (in case only one element) void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/) { diff --git a/src/slic3r/GUI/GUI.hpp b/src/slic3r/GUI/GUI.hpp index 4074c2afc..0b904bad8 100644 --- a/src/slic3r/GUI/GUI.hpp +++ b/src/slic3r/GUI/GUI.hpp @@ -35,14 +35,6 @@ extern AppConfig* get_app_config(); extern void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change); -// Checks if configuration wizard needs to run, calls config_wizard if so. -// Returns whether the Wizard ran. -extern bool config_wizard_startup(bool app_config_exists); - -// Opens the configuration wizard, returns true if wizard is finished & accepted. -// The run_reason argument is actually ConfigWizard::RunReason, but int is used here because of Perl. -extern void config_wizard(int run_reason); - // Change option value in config void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index c04ed2371..8c3e0d4ff 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -38,7 +38,6 @@ #include "../Utils/PresetUpdater.hpp" #include "../Utils/PrintHost.hpp" #include "../Utils/MacDarkMode.hpp" -#include "ConfigWizard.hpp" #include "slic3r/Config/Snapshot.hpp" #include "ConfigSnapshotDialog.hpp" #include "FirmwareDialog.hpp" @@ -46,11 +45,17 @@ #include "Tab.hpp" #include "SysInfoDialog.hpp" #include "KBShortcutsDialog.hpp" +#include "UpdateDialogs.hpp" #ifdef __WXMSW__ #include #endif // __WXMSW__ +#if ENABLE_THUMBNAIL_GENERATOR_DEBUG +#include +#include +#endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG + namespace Slic3r { namespace GUI { @@ -100,7 +105,7 @@ static void register_dpi_event() const auto rect = reinterpret_cast(lParam); const wxRect wxrect(wxPoint(rect->top, rect->left), wxPoint(rect->bottom, rect->right)); - DpiChangedEvent evt(EVT_DPI_CHANGED, dpi, wxrect); + DpiChangedEvent evt(EVT_DPI_CHANGED_SLICER, dpi, wxrect); win->GetEventHandler()->AddPendingEvent(evt); return true; @@ -148,6 +153,7 @@ GUI_App::GUI_App() : wxApp() , m_em_unit(10) , m_imgui(new ImGuiWrapper()) + , m_wizard(nullptr) {} GUI_App::~GUI_App() @@ -204,7 +210,6 @@ bool GUI_App::on_init_inner() // supplied as argument to --datadir; in that case we should still run the wizard preset_bundle->setup_directories(); - app_conf_exists = app_config->exists(); // load settings app_conf_exists = app_config->exists(); if (app_conf_exists) { @@ -276,7 +281,7 @@ bool GUI_App::on_init_inner() PresetUpdater::UpdateResult updater_result; try { - updater_result = preset_updater->config_update(); + 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) { @@ -287,7 +292,7 @@ bool GUI_App::on_init_inner() } CallAfter([this] { - config_wizard_startup(app_conf_exists); + config_wizard_startup(); preset_updater->slic3r_update_notify(); preset_updater->sync(preset_bundle); }); @@ -826,7 +831,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu) local_menu->Bind(wxEVT_MENU, [this, config_id_base](wxEvent &event) { switch (event.GetId() - config_id_base) { case ConfigMenuWizard: - config_wizard(ConfigWizard::RR_USER); + run_wizard(ConfigWizard::RR_USER); break; case ConfigMenuTakeSnapshot: // Take a configuration snapshot. @@ -1057,6 +1062,97 @@ void GUI_App::open_web_page_localized(const std::string &http_address) wxLaunchDefaultBrowser(http_address + "&lng=" + this->current_language_code_safe()); } +bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage start_page) +{ + wxCHECK_MSG(mainframe != nullptr, false, "Internal error: Main frame not created / null"); + + if (! m_wizard) { + m_wizard = new ConfigWizard(mainframe); + } + + const bool res = m_wizard->run(reason, start_page); + + if (res) { + load_current_presets(); + + if (preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA + && Slic3r::model_has_multi_part_objects(wxGetApp().model())) { + GUI::show_info(nullptr, + _(L("It's impossible to print multi-part object(s) with SLA technology.")) + "\n\n" + + _(L("Please check and fix your object list.")), + _(L("Attention!"))); + } + } + + return res; +} + +#if ENABLE_THUMBNAIL_GENERATOR_DEBUG +void GUI_App::gcode_thumbnails_debug() +{ + const std::string BEGIN_MASK = "; thumbnail begin"; + const std::string END_MASK = "; thumbnail end"; + std::string gcode_line; + bool reading_image = false; + unsigned int width = 0; + unsigned int height = 0; + + wxFileDialog dialog(GetTopWindow(), _(L("Select a gcode file:")), "", "", "G-code files (*.gcode)|*.gcode;*.GCODE;", wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (dialog.ShowModal() != wxID_OK) + return; + + std::string in_filename = into_u8(dialog.GetPath()); + std::string out_path = boost::filesystem::path(in_filename).remove_filename().append(L"thumbnail").string(); + + boost::nowide::ifstream in_file(in_filename.c_str()); + std::vector rows; + std::string row; + if (in_file.good()) + { + while (std::getline(in_file, gcode_line)) + { + if (in_file.good()) + { + if (boost::starts_with(gcode_line, BEGIN_MASK)) + { + reading_image = true; + gcode_line = gcode_line.substr(BEGIN_MASK.length() + 1); + std::string::size_type x_pos = gcode_line.find('x'); + std::string width_str = gcode_line.substr(0, x_pos); + width = (unsigned int)::atoi(width_str.c_str()); + std::string height_str = gcode_line.substr(x_pos + 1); + height = (unsigned int)::atoi(height_str.c_str()); + row.clear(); + } + else if (reading_image && boost::starts_with(gcode_line, END_MASK)) + { + std::string out_filename = out_path + std::to_string(width) + "x" + std::to_string(height) + ".png"; + boost::nowide::ofstream out_file(out_filename.c_str(), std::ios::binary); + if (out_file.good()) + { + std::string decoded; + decoded.resize(boost::beast::detail::base64::decoded_size(row.size())); + decoded.resize(boost::beast::detail::base64::decode((void*)&decoded[0], row.data(), row.size()).first); + + out_file.write(decoded.c_str(), decoded.size()); + out_file.close(); + } + + reading_image = false; + width = 0; + height = 0; + rows.clear(); + } + else if (reading_image) + row += gcode_line.substr(2); + } + } + + in_file.close(); + } +} +#endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG + void GUI_App::window_pos_save(wxTopLevelWindow* window, const std::string &name) { if (name.empty()) { return; } @@ -1105,6 +1201,24 @@ void GUI_App::window_pos_sanitize(wxTopLevelWindow* window) } } +bool GUI_App::config_wizard_startup() +{ + if (!app_conf_exists || preset_bundle->printers.size() <= 1) { + run_wizard(ConfigWizard::RR_DATA_EMPTY); + return true; + } else if (get_app_config()->legacy_datadir()) { + // Looks like user has legacy pre-vendorbundle data directory, + // explain what this is and run the wizard + + MsgDataLegacy dlg; + dlg.ShowModal(); + + run_wizard(ConfigWizard::RR_DATA_LEGACY); + return true; + } + return false; +} + // 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 d35e6391a..a730ff95f 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -6,6 +6,7 @@ #include "libslic3r/PrintConfig.hpp" #include "MainFrame.hpp" #include "ImGuiWrapper.hpp" +#include "ConfigWizard.hpp" #include #include @@ -69,6 +70,7 @@ enum ConfigMenuIDs { }; class Tab; +class ConfigWizard; static wxString dots("…", wxConvUTF8); @@ -85,7 +87,7 @@ class GUI_App : public wxApp wxFont m_bold_font; wxFont m_normal_font; - size_t m_em_unit; // width of a "m"-symbol in pixels for current system font + int m_em_unit; // width of a "m"-symbol in pixels for current system font // Note: for 100% Scale m_em_unit = 10 -> it's a good enough coefficient for a size setting of controls std::unique_ptr m_wxLocale; @@ -96,13 +98,14 @@ class GUI_App : public wxApp std::unique_ptr m_imgui; std::unique_ptr m_printhost_job_queue; + ConfigWizard* m_wizard; // Managed by wxWindow tree public: bool OnInit() override; bool initialized() const { return m_initialized; } GUI_App(); - ~GUI_App(); + ~GUI_App() override; static unsigned get_colour_approx_luma(const wxColour &colour); static bool dark_mode(); @@ -121,8 +124,7 @@ public: const wxFont& small_font() { return m_small_font; } const wxFont& bold_font() { return m_bold_font; } const wxFont& normal_font() { return m_normal_font; } - size_t em_unit() const { return m_em_unit; } - void set_em_unit(const size_t em_unit) { m_em_unit = em_unit; } + int em_unit() const { return m_em_unit; } float toolbar_icon_scale(const bool is_limited = false) const; void recreate_GUI(); @@ -184,6 +186,12 @@ public: PrintHostJobQueue& printhost_job_queue() { return *m_printhost_job_queue.get(); } void open_web_page_localized(const std::string &http_address); + bool run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage start_page = ConfigWizard::SP_WELCOME); + +#if ENABLE_THUMBNAIL_GENERATOR_DEBUG + // temporary and debug only -> extract thumbnails from selected gcode and save them as png files + void gcode_thumbnails_debug(); +#endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG private: bool on_init_inner(); @@ -191,6 +199,9 @@ private: void window_pos_restore(wxTopLevelWindow* window, const std::string &name, bool default_maximized = false); void window_pos_sanitize(wxTopLevelWindow* window); bool select_language(); + + bool config_wizard_startup(); + #ifdef __WXMSW__ void associate_3mf_files(); #endif // __WXMSW__ diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 80ca63fff..ff3947949 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -137,7 +137,7 @@ ObjectList::ObjectList(wxWindow* parent) : { wxDataViewItemArray sels; GetSelections(sels); - if (sels.front() == m_last_selected_item) + if (! sels.empty() && sels.front() == m_last_selected_item) m_last_selected_item = sels.back(); else m_last_selected_item = event.GetItem(); @@ -273,12 +273,15 @@ void ObjectList::create_objects_ctrl() wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE); // column Extruder of the view control: - AppendColumn(create_objects_list_extruder_column(4)); + AppendColumn(new wxDataViewColumn(_(L("Extruder")), new BitmapChoiceRenderer(), + colExtruder, 8*em, wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE)); // column ItemEditing of the view control: AppendBitmapColumn(_(L("Editing")), colEditing, wxDATAVIEW_CELL_INERT, 3*em, wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE); + // For some reason under OSX on 4K(5K) monitors in wxDataViewColumn constructor doesn't set width of column. + // Therefore, force set column width. if (wxOSX) { GetColumn(colName)->SetWidth(20*em); @@ -331,7 +334,7 @@ wxString ObjectList::get_mesh_errors_list(const int obj_idx, const int vol_idx / return ""; // hide tooltip // Create tooltip string, if there are errors - wxString tooltip = wxString::Format(_(L("Auto-repaired (%d errors):\n")), errors); + wxString tooltip = wxString::Format(_(L("Auto-repaired (%d errors):")), errors) + "\n"; const stl_stats& stats = vol_idx == -1 ? (*m_objects)[obj_idx]->get_object_stl_stats() : @@ -438,19 +441,6 @@ DynamicPrintConfig& ObjectList::get_item_config(const wxDataViewItem& item) cons (*m_objects)[obj_idx]->config; } -wxDataViewColumn* ObjectList::create_objects_list_extruder_column(size_t extruders_count) -{ - wxArrayString choices; - choices.Add(_(L("default"))); - for (int i = 1; i <= extruders_count; ++i) - choices.Add(wxString::Format("%d", i)); - wxDataViewChoiceRenderer *c = - new wxDataViewChoiceRenderer(choices, wxDATAVIEW_CELL_EDITABLE, wxALIGN_CENTER_HORIZONTAL); - wxDataViewColumn* column = new wxDataViewColumn(_(L("Extruder")), c, colExtruder, - 8*wxGetApp().em_unit()/*80*/, wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE); - return column; -} - void ObjectList::update_extruder_values_for_items(const size_t max_extruder) { for (size_t i = 0; i < m_objects->size(); ++i) @@ -461,24 +451,24 @@ void ObjectList::update_extruder_values_for_items(const size_t max_extruder) auto object = (*m_objects)[i]; wxString extruder; if (!object->config.has("extruder") || - object->config.option("extruder")->value > max_extruder) + size_t(object->config.option("extruder")->value) > max_extruder) extruder = _(L("default")); else extruder = wxString::Format("%d", object->config.option("extruder")->value); - m_objects_model->SetValue(extruder, item, colExtruder); + m_objects_model->SetExtruder(extruder, item); if (object->volumes.size() > 1) { for (size_t id = 0; id < object->volumes.size(); id++) { item = m_objects_model->GetItemByVolumeId(i, id); if (!item) continue; if (!object->volumes[id]->config.has("extruder") || - object->volumes[id]->config.option("extruder")->value > max_extruder) + size_t(object->volumes[id]->config.option("extruder")->value) > max_extruder) extruder = _(L("default")); else extruder = wxString::Format("%d", object->volumes[id]->config.option("extruder")->value); - m_objects_model->SetValue(extruder, item, colExtruder); + m_objects_model->SetExtruder(extruder, item); } } } @@ -490,19 +480,13 @@ void ObjectList::update_objects_list_extruder_column(size_t extruders_count) if (printer_technology() == ptSLA) extruders_count = 1; - wxDataViewChoiceRenderer* ch_render = dynamic_cast(GetColumn(colExtruder)->GetRenderer()); - if (ch_render->GetChoices().GetCount() - 1 == extruders_count) - return; - m_prevent_update_extruder_in_config = true; if (m_objects && extruders_count > 1) update_extruder_values_for_items(extruders_count); - // delete old extruder column - DeleteColumn(GetColumn(colExtruder)); - // insert new created extruder column - InsertColumn(colExtruder, create_objects_list_extruder_column(extruders_count)); + update_extruder_colors(); + // set show/hide for this column set_extruder_column_hidden(extruders_count <= 1); //a workaround for a wrong last column width updating under OSX @@ -511,6 +495,11 @@ void ObjectList::update_objects_list_extruder_column(size_t extruders_count) m_prevent_update_extruder_in_config = false; } +void ObjectList::update_extruder_colors() +{ + m_objects_model->UpdateColumValues(colExtruder); +} + void ObjectList::set_extruder_column_hidden(const bool hide) const { GetColumn(colExtruder)->SetHidden(hide); @@ -539,14 +528,12 @@ void ObjectList::update_extruder_in_config(const wxDataViewItem& item) m_config = &get_item_config(item); } - wxVariant variant; - m_objects_model->GetValue(variant, item, colExtruder); - const wxString selection = variant.GetString(); - - if (!m_config || selection.empty()) + if (!m_config) return; - const int extruder = /*selection.size() > 1 ? 0 : */atoi(selection.c_str()); + take_snapshot(_(L("Change Extruder"))); + + const int extruder = m_objects_model->GetExtruderNumber(item); m_config->set_key_value("extruder", new ConfigOptionInt(extruder)); // update scene @@ -739,9 +726,9 @@ void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& vol } select_items(items); -#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME +//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME selection_changed(); -#endif //no __WXOSX__ //__WXMSW__ +//#endif //no __WXOSX__ //__WXMSW__ } void ObjectList::paste_objects_into_list(const std::vector& object_idxs) @@ -759,9 +746,9 @@ void ObjectList::paste_objects_into_list(const std::vector& object_idxs) wxGetApp().plater()->changed_objects(object_idxs); select_items(items); -#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME +//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME selection_changed(); -#endif //no __WXOSX__ //__WXMSW__ +//#endif //no __WXOSX__ //__WXMSW__ } #ifdef __WXOSX__ @@ -781,7 +768,13 @@ void ObjectList::OnChar(wxKeyEvent& event) void ObjectList::OnContextMenu(wxDataViewEvent&) { - list_manipulation(true); + // Do not show the context menu if the user pressed the right mouse button on the 3D scene and released it on the objects list + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + bool evt_context_menu = (canvas != nullptr) ? !canvas->is_mouse_dragging() : true; + if (!evt_context_menu) + canvas->mouse_up_cleanup(); + + list_manipulation(evt_context_menu); } void ObjectList::list_manipulation(bool evt_context_menu/* = false*/) @@ -791,6 +784,9 @@ void ObjectList::list_manipulation(bool evt_context_menu/* = false*/) const wxPoint pt = get_mouse_position_in_control(); HitTest(pt, item, col); + if (m_extruder_editor) + m_extruder_editor->Hide(); + /* Note: Under OSX right click doesn't send "selection changed" event. * It means that Selection() will be return still previously selected item. * Thus under OSX we should force UnselectAll(), when item and col are nullptr, @@ -801,7 +797,9 @@ void ObjectList::list_manipulation(bool evt_context_menu/* = false*/) if (col == nullptr) { if (wxOSX) UnselectAll(); - else + else if (!evt_context_menu) + // Case, when last item was deleted and under GTK was called wxEVT_DATAVIEW_SELECTION_CHANGED, + // which invoked next list_manipulation(false) return; } @@ -837,6 +835,9 @@ void ObjectList::list_manipulation(bool evt_context_menu/* = false*/) fix_through_netfabb(); } } + // workaround for extruder editing under OSX + else if (wxOSX && evt_context_menu && title == _("Extruder")) + extruder_editing(); #ifndef __WXMSW__ GetMainWindow()->SetToolTip(""); // hide tooltip @@ -878,6 +879,45 @@ void ObjectList::show_context_menu(const bool evt_context_menu) wxGetApp().plater()->PopupMenu(menu); } +void ObjectList::extruder_editing() +{ + wxDataViewItem item = GetSelection(); + if (!item || !(m_objects_model->GetItemType(item) & (itVolume | itObject))) + return; + + const int column_width = GetColumn(colExtruder)->GetWidth() + wxSystemSettings::GetMetric(wxSYS_VSCROLL_X) + 5; + + wxPoint pos = get_mouse_position_in_control(); + wxSize size = wxSize(column_width, -1); + pos.x = GetColumn(colName)->GetWidth() + GetColumn(colPrint)->GetWidth() + 5; + pos.y -= GetTextExtent("m").y; + + apply_extruder_selector(&m_extruder_editor, this, L("default"), pos, size); + + m_extruder_editor->SetSelection(m_objects_model->GetExtruderNumber(item)); + m_extruder_editor->Show(); + + auto set_extruder = [this]() + { + wxDataViewItem item = GetSelection(); + if (!item) return; + + const int selection = m_extruder_editor->GetSelection(); + if (selection >= 0) + m_objects_model->SetExtruder(m_extruder_editor->GetString(selection), item); + + m_extruder_editor->Hide(); + update_extruder_in_config(item); + }; + + // to avoid event propagation to other sidebar items + m_extruder_editor->Bind(wxEVT_COMBOBOX, [set_extruder](wxCommandEvent& evt) + { + set_extruder(); + evt.StopPropagation(); + }); +} + void ObjectList::copy() { // if (m_selection_mode & smLayer) @@ -1503,6 +1543,12 @@ void ObjectList::append_menu_item_export_stl(wxMenu* menu) const menu->AppendSeparator(); } +void ObjectList::append_menu_item_reload_from_disk(wxMenu* menu) const +{ + 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()); +} + void ObjectList::append_menu_item_change_extruder(wxMenu* menu) const { const wxString name = _(L("Change extruder")); @@ -1552,6 +1598,7 @@ void ObjectList::create_object_popupmenu(wxMenu *menu) append_menu_items_osx(menu); #endif // __WXOSX__ + append_menu_item_reload_from_disk(menu); append_menu_item_export_stl(menu); append_menu_item_fix_through_netfabb(menu); append_menu_item_scale_selection_to_fit_print_volume(menu); @@ -1575,6 +1622,7 @@ void ObjectList::create_sla_object_popupmenu(wxMenu *menu) append_menu_items_osx(menu); #endif // __WXOSX__ + append_menu_item_reload_from_disk(menu); append_menu_item_export_stl(menu); append_menu_item_fix_through_netfabb(menu); // rest of a object_sla_menu will be added later in: @@ -1587,8 +1635,9 @@ void ObjectList::create_part_popupmenu(wxMenu *menu) append_menu_items_osx(menu); #endif // __WXOSX__ - append_menu_item_fix_through_netfabb(menu); + append_menu_item_reload_from_disk(menu); append_menu_item_export_stl(menu); + append_menu_item_fix_through_netfabb(menu); append_menu_item_split(menu); @@ -1712,9 +1761,9 @@ void ObjectList::load_subobject(ModelVolumeType type) if (sel_item) select_item(sel_item); -#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME +//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME selection_changed(); -#endif //no __WXOSX__ //__WXMSW__ +//#endif //no __WXOSX__ //__WXMSW__ } void ObjectList::load_part( ModelObject* model_object, @@ -1850,9 +1899,9 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode const auto object_item = m_objects_model->GetTopParent(GetSelection()); select_item(m_objects_model->AddVolumeChild(object_item, name, type, new_volume->get_mesh_errors_count()>0)); -#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME +//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME selection_changed(); -#endif //no __WXOSX__ //__WXMSW__ +//#endif //no __WXOSX__ //__WXMSW__ } void ObjectList::load_shape_object(const std::string& type_name) @@ -1891,6 +1940,9 @@ void ObjectList::load_shape_object(const std::string& type_name) new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); new_object->invalidate_bounding_box(); + new_object->center_around_origin(); + new_object->ensure_on_bed(); + const BoundingBoxf bed_shape = wxGetApp().plater()->bed_shape_bb(); new_object->instances[0]->set_offset(Slic3r::to_3d(bed_shape.center().cast(), -new_object->origin_translation(2))); @@ -2246,6 +2298,7 @@ void ObjectList::changed_object(const int obj_idx/* = -1*/) const void ObjectList::part_selection_changed() { + if (m_extruder_editor) m_extruder_editor->Hide(); int obj_idx = -1; int volume_id = -1; m_config = nullptr; @@ -2328,7 +2381,8 @@ void ObjectList::part_selection_changed() wxGetApp().obj_manipul()->get_og()->set_name(" " + og_name + " "); if (item) { - wxGetApp().obj_manipul()->get_og()->set_value("object_name", m_objects_model->GetName(item)); + // wxGetApp().obj_manipul()->get_og()->set_value("object_name", m_objects_model->GetName(item)); + wxGetApp().obj_manipul()->update_item_name(m_objects_model->GetName(item)); wxGetApp().obj_manipul()->update_warning_icon_state(get_mesh_errors_list(obj_idx, volume_id)); } } @@ -2534,7 +2588,7 @@ void ObjectList::delete_from_model_and_list(const std::vector& it (*m_objects)[item->obj_idx]->config.has("extruder")) { const wxString extruder = wxString::Format("%d", (*m_objects)[item->obj_idx]->config.option("extruder")->value); - m_objects_model->SetValue(extruder, m_objects_model->GetItemById(item->obj_idx), colExtruder); + m_objects_model->SetExtruder(extruder, m_objects_model->GetItemById(item->obj_idx)); } wxGetApp().plater()->canvas3D()->ensure_on_bed(item->obj_idx); } @@ -3676,10 +3730,10 @@ void ObjectList::msw_rescale() // update min size !!! A width of control shouldn't be a wxDefaultCoord SetMinSize(wxSize(1, 15 * em)); - GetColumn(colName)->SetWidth(19 * em); - GetColumn(colPrint)->SetWidth( 2 * em); + GetColumn(colName )->SetWidth(20 * em); + GetColumn(colPrint )->SetWidth( 3 * em); GetColumn(colExtruder)->SetWidth( 8 * em); - GetColumn(colEditing)->SetWidth( 2 * em); + GetColumn(colEditing )->SetWidth( 3 * em); // rescale all icons, used by ObjectList msw_rescale_icons(); @@ -3788,6 +3842,9 @@ void ObjectList::set_extruder_for_selected_items(const int extruder) const wxDataViewItemArray sels; GetSelections(sels); + if (!sels.empty()) + take_snapshot(_(L("Change Extruders"))); + for (const wxDataViewItem& item : sels) { DynamicPrintConfig& config = get_item_config(item); @@ -3809,7 +3866,7 @@ void ObjectList::set_extruder_for_selected_items(const int extruder) const /* We can change extruder for Object/Volume only. * So, if Instance is selected, get its Object item and change it */ - m_objects_model->SetValue(extruder_str, type & itInstance ? m_objects_model->GetTopParent(item) : item, colExtruder); + m_objects_model->SetExtruder(extruder_str, type & itInstance ? m_objects_model->GetTopParent(item) : item); const int obj_idx = type & itObject ? m_objects_model->GetIdByItem(item) : m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)); diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index a894d32e5..e41d48c8e 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -12,6 +12,7 @@ #include "wxExtensions.hpp" class wxBoxSizer; +class wxBitmapComboBox; class wxMenuItem; class ObjectDataViewModel; class MenuWithSeparators; @@ -140,6 +141,8 @@ private: DynamicPrintConfig *m_config {nullptr}; std::vector *m_objects{ nullptr }; + wxBitmapComboBox *m_extruder_editor { nullptr }; + std::vector m_bmp_vector; t_layer_config_ranges m_layer_config_ranges_cache; @@ -183,8 +186,8 @@ public: void create_objects_ctrl(); void create_popup_menus(); - wxDataViewColumn* create_objects_list_extruder_column(size_t extruders_count); void update_objects_list_extruder_column(size_t extruders_count); + void update_extruder_colors(); // show/hide "Extruder" column for Objects List void set_extruder_column_hidden(const bool hide) const; // update extruder in current config @@ -210,6 +213,7 @@ public: void selection_changed(); void show_context_menu(const bool evt_context_menu); + void extruder_editing(); #ifndef __WXOSX__ void key_event(wxKeyEvent& event); #endif /* __WXOSX__ */ @@ -233,7 +237,8 @@ public: wxMenuItem* append_menu_item_printable(wxMenu* menu, wxWindow* parent); void append_menu_items_osx(wxMenu* menu); wxMenuItem* append_menu_item_fix_through_netfabb(wxMenu* menu); - void append_menu_item_export_stl(wxMenu* menu) const ; + void append_menu_item_export_stl(wxMenu* menu) const; + void append_menu_item_reload_from_disk(wxMenu* menu) const; void append_menu_item_change_extruder(wxMenu* menu) const; void append_menu_item_delete(wxMenu* menu); void append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 2295ac0b6..36293525a 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -57,7 +57,7 @@ static wxBitmapComboBox* create_word_local_combo(wxWindow *parent) #endif //__WXOSX__ temp->SetFont(Slic3r::GUI::wxGetApp().normal_font()); - temp->SetBackgroundStyle(wxBG_STYLE_PAINT); + if (!wxOSX) temp->SetBackgroundStyle(wxBG_STYLE_PAINT); temp->Append(_(L("World coordinates"))); temp->Append(_(L("Local coordinates"))); @@ -112,40 +112,33 @@ void msw_rescale_word_local_combo(wxBitmapComboBox* combo) combo->SetValue(selection); } +static void set_font_and_background_style(wxWindow* win, const wxFont& font) +{ + win->SetFont(font); + win->SetBackgroundStyle(wxBG_STYLE_PAINT); +} ObjectManipulation::ObjectManipulation(wxWindow* parent) : OG_Settings(parent, true) -#ifndef __APPLE__ - , m_focused_option("") -#endif // __APPLE__ { m_manifold_warning_bmp = ScalableBitmap(parent, "exclamation"); - m_og->set_name(_(L("Object Manipulation"))); - m_og->label_width = 12;//125; - m_og->set_grid_vgap(5); - - m_og->m_on_change = std::bind(&ObjectManipulation::on_change, this, std::placeholders::_1, std::placeholders::_2); - m_og->m_fill_empty_value = std::bind(&ObjectManipulation::on_fill_empty_value, this, std::placeholders::_1); - m_og->m_set_focus = [this](const std::string& opt_key) - { -#ifndef __APPLE__ - m_focused_option = opt_key; -#endif // __APPLE__ + // Load bitmaps to be used for the mirroring buttons: + m_mirror_bitmap_on = ScalableBitmap(parent, "mirroring_on"); + m_mirror_bitmap_off = ScalableBitmap(parent, "mirroring_off"); + m_mirror_bitmap_hidden = ScalableBitmap(parent, "mirroring_transparent.png"); - // needed to show the visual hints in 3D scene - wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, true); - }; + const int border = wxOSX ? 0 : 4; + const int em = wxGetApp().em_unit(); + m_main_grid_sizer = new wxFlexGridSizer(2, 3, 3); // "Name/label", "String name / Editors" + m_main_grid_sizer->SetFlexibleDirection(wxBOTH); - ConfigOptionDef def; + // Add "Name" label with warning icon + auto sizer = new wxBoxSizer(wxHORIZONTAL); - Line line = Line{ "Name", "Object name" }; - - auto manifold_warning_icon = [this](wxWindow* parent) { - m_fix_throught_netfab_bitmap = new wxStaticBitmap(parent, wxID_ANY, wxNullBitmap); - - if (is_windows10()) - m_fix_throught_netfab_bitmap->Bind(wxEVT_CONTEXT_MENU, [this](wxCommandEvent &e) + m_fix_throught_netfab_bitmap = new wxStaticBitmap(parent, wxID_ANY, wxNullBitmap); + if (is_windows10()) + m_fix_throught_netfab_bitmap->Bind(wxEVT_CONTEXT_MENU, [this](wxCommandEvent& e) { // if object/sub-object has no errors if (m_fix_throught_netfab_bitmap->GetBitmap().GetRefData() == wxNullBitmap.GetRefData()) @@ -155,248 +148,271 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : update_warning_icon_state(wxGetApp().obj_list()->get_mesh_errors_list()); }); - return m_fix_throught_netfab_bitmap; + sizer->Add(m_fix_throught_netfab_bitmap); + + auto name_label = new wxStaticText(m_parent, wxID_ANY, _(L("Name"))+":"); + set_font_and_background_style(name_label, wxGetApp().normal_font()); + name_label->SetToolTip(_(L("Object name"))); + sizer->Add(name_label); + + m_main_grid_sizer->Add(sizer); + + // Add name of the item + const wxSize name_size = wxSize(20 * em, wxDefaultCoord); + m_item_name = new wxStaticText(m_parent, wxID_ANY, "", wxDefaultPosition, name_size, wxST_ELLIPSIZE_MIDDLE); + set_font_and_background_style(m_item_name, wxGetApp().bold_font()); + + m_main_grid_sizer->Add(m_item_name, 0, wxEXPAND); + + // Add labels grid sizer + m_labels_grid_sizer = new wxFlexGridSizer(1, 3, 3); // "Name/label", "String name / Editors" + m_labels_grid_sizer->SetFlexibleDirection(wxBOTH); + + // Add world local combobox + m_word_local_combo = create_word_local_combo(parent); + m_word_local_combo->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent& evt) { + this->set_world_coordinates(evt.GetSelection() != 1); + }), m_word_local_combo->GetId()); + + // Small trick to correct layouting in different view_mode : + // Show empty string of a same height as a m_word_local_combo, when m_word_local_combo is hidden + m_word_local_combo_sizer = new wxBoxSizer(wxHORIZONTAL); + m_empty_str = new wxStaticText(parent, wxID_ANY, ""); + m_word_local_combo_sizer->Add(m_word_local_combo); + m_word_local_combo_sizer->Add(m_empty_str); + m_word_local_combo_sizer->SetMinSize(wxSize(-1, m_word_local_combo->GetBestHeight(-1))); + m_labels_grid_sizer->Add(m_word_local_combo_sizer); + + // Text trick to grid sizer layout: + // Height of labels should be equivalent to the edit boxes + int height = wxTextCtrl(parent, wxID_ANY, "Br").GetBestHeight(-1); +#ifdef __WXGTK__ + // On Linux button with bitmap has bigger height then regular button or regular TextCtrl + // It can cause a wrong alignment on show/hide of a reset buttons + const int bmp_btn_height = ScalableButton(parent, wxID_ANY, "undo") .GetBestHeight(-1); + if (bmp_btn_height > height) + height = bmp_btn_height; +#endif //__WXGTK__ + + auto add_label = [this, height](wxStaticText** label, const std::string& name, wxSizer* reciver = nullptr) + { + *label = new wxStaticText(m_parent, wxID_ANY, _(name) + ":"); + set_font_and_background_style(m_move_Label, wxGetApp().normal_font()); + + wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->SetMinSize(wxSize(-1, height)); + sizer->Add(*label, 0, wxALIGN_CENTER_VERTICAL); + + if (reciver) + reciver->Add(sizer); + else + m_labels_grid_sizer->Add(sizer); + + m_rescalable_sizers.push_back(sizer); }; - line.near_label_widget = manifold_warning_icon; - def.label = ""; - def.gui_type = "legend"; - def.tooltip = L("Object name"); -#ifdef __APPLE__ - def.width = 20; -#else - def.width = 22; -#endif - def.set_default_value(new ConfigOptionString{ " " }); - line.append_option(Option(def, "object_name")); - m_og->append_line(line); + // Add labels + add_label(&m_move_Label, L("Position")); + add_label(&m_rotate_Label, L("Rotation")); - const int field_width = 5; + // additional sizer for lock and labels "Scale" & "Size" + sizer = new wxBoxSizer(wxHORIZONTAL); - // Mirror button size: - const int mirror_btn_width = 3; + m_lock_bnt = new LockButton(parent, wxID_ANY); + m_lock_bnt->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event) { + event.Skip(); + wxTheApp->CallAfter([this]() { set_uniform_scaling(m_lock_bnt->IsLocked()); }); + }); + sizer->Add(m_lock_bnt, 0, wxALIGN_CENTER_VERTICAL); - // Legend for object modification - line = Line{ "", "" }; - def.label = ""; - def.type = coString; - def.width = field_width - mirror_btn_width;//field_width/*50*/; + auto v_sizer = new wxGridSizer(1, 3, 3); - // Load bitmaps to be used for the mirroring buttons: - m_mirror_bitmap_on = ScalableBitmap(parent, "mirroring_on"); - m_mirror_bitmap_off = ScalableBitmap(parent, "mirroring_off"); - m_mirror_bitmap_hidden = ScalableBitmap(parent, "mirroring_transparent.png"); + add_label(&m_scale_Label, L("Scale"), v_sizer); + wxStaticText* size_Label {nullptr}; + add_label(&size_Label, L("Size"), v_sizer); + if (wxOSX) set_font_and_background_style(size_Label, wxGetApp().normal_font()); + sizer->Add(v_sizer, 0, wxLEFT, border); + m_labels_grid_sizer->Add(sizer); + m_main_grid_sizer->Add(m_labels_grid_sizer, 0, wxEXPAND); + + + // Add editors grid sizer + wxFlexGridSizer* editors_grid_sizer = new wxFlexGridSizer(5, 3, 3); // "Name/label", "String name / Editors" + editors_grid_sizer->SetFlexibleDirection(wxBOTH); + + // Add Axes labels with icons static const char axes[] = { 'X', 'Y', 'Z' }; +// std::vector axes_color = {"#990000", "#009900","#000099"}; for (size_t axis_idx = 0; axis_idx < sizeof(axes); axis_idx++) { const char label = axes[axis_idx]; - def.set_default_value(new ConfigOptionString{ std::string(" ") + label }); - Option option(def, std::string() + label + "_axis_legend"); + + wxStaticText* axis_name = new wxStaticText(m_parent, wxID_ANY, wxString(label)); + set_font_and_background_style(axis_name, wxGetApp().bold_font()); +// axis_name->SetForegroundColour(wxColour(axes_color[axis_idx])); + + sizer = new wxBoxSizer(wxHORIZONTAL); + // Under OSX we use font, smaller than default font, so + // there is a next trick for an equivalent layout of coordinates combobox and axes labels in they own sizers + if (wxOSX) + sizer->SetMinSize(-1, m_word_local_combo->GetBestHeight(-1)); + sizer->Add(axis_name, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, border); // We will add a button to toggle mirroring to each axis: - auto mirror_button = [this, mirror_btn_width, axis_idx, label](wxWindow* parent) { - wxSize btn_size(em_unit(parent) * mirror_btn_width, em_unit(parent) * mirror_btn_width); - auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_off", wxEmptyString, btn_size, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW); - btn->SetToolTip(wxString::Format(_(L("Toggle %c axis mirroring")), (int)label)); - btn->SetBitmapDisabled_(m_mirror_bitmap_hidden); + auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_off", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW); + btn->SetToolTip(wxString::Format(_(L("Toggle %c axis mirroring")), (int)label)); + btn->SetBitmapDisabled_(m_mirror_bitmap_hidden); - m_mirror_buttons[axis_idx].first = btn; - m_mirror_buttons[axis_idx].second = mbShown; - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); + m_mirror_buttons[axis_idx].first = btn; + m_mirror_buttons[axis_idx].second = mbShown; - btn->Bind(wxEVT_BUTTON, [this, axis_idx](wxCommandEvent &e) { - Axis axis = (Axis)(axis_idx + X); - if (m_mirror_buttons[axis_idx].second == mbHidden) - return; + sizer->AddStretchSpacer(2); + sizer->Add(btn, 0, wxALIGN_CENTER_VERTICAL); - GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); - Selection& selection = canvas->get_selection(); + btn->Bind(wxEVT_BUTTON, [this, axis_idx](wxCommandEvent&) { + Axis axis = (Axis)(axis_idx + X); + if (m_mirror_buttons[axis_idx].second == mbHidden) + return; - if (selection.is_single_volume() || selection.is_single_modifier()) { - GLVolume* volume = const_cast(selection.get_volume(*selection.get_volume_idxs().begin())); - volume->set_volume_mirror(axis, -volume->get_volume_mirror(axis)); + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + Selection& selection = canvas->get_selection(); + + if (selection.is_single_volume() || selection.is_single_modifier()) { + GLVolume* volume = const_cast(selection.get_volume(*selection.get_volume_idxs().begin())); + volume->set_volume_mirror(axis, -volume->get_volume_mirror(axis)); + } + else if (selection.is_single_full_instance()) { + for (unsigned int idx : selection.get_volume_idxs()) { + GLVolume* volume = const_cast(selection.get_volume(idx)); + volume->set_instance_mirror(axis, -volume->get_instance_mirror(axis)); } - else if (selection.is_single_full_instance()) { - for (unsigned int idx : selection.get_volume_idxs()){ - GLVolume* volume = const_cast(selection.get_volume(idx)); - volume->set_instance_mirror(axis, -volume->get_instance_mirror(axis)); - } - } - else - return; + } + else + return; - // Update mirroring at the GLVolumes. - selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL); - selection.synchronize_unselected_volumes(); - // Copy mirroring values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing. - canvas->do_mirror(L("Set Mirror")); - UpdateAndShow(true); - }); + // Update mirroring at the GLVolumes. + selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL); + selection.synchronize_unselected_volumes(); + // Copy mirroring values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing. + canvas->do_mirror(L("Set Mirror")); + UpdateAndShow(true); + }); - return sizer; - }; - - option.side_widget = mirror_button; - line.append_option(option); + editors_grid_sizer->Add(sizer, 0, wxALIGN_CENTER_HORIZONTAL); } - line.near_label_widget = [this](wxWindow* parent) { - wxBitmapComboBox *combo = create_word_local_combo(parent); - combo->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent &evt) { this->set_world_coordinates(evt.GetSelection() != 1); }), combo->GetId()); - m_word_local_combo = combo; - return combo; - }; - m_og->append_line(line); - auto add_og_to_object_settings = [this, field_width](const std::string& option_name, const std::string& sidetext) + editors_grid_sizer->AddStretchSpacer(1); + editors_grid_sizer->AddStretchSpacer(1); + + // add EditBoxes + auto add_edit_boxes = [this, editors_grid_sizer](const std::string& opt_key, int axis) { - Line line = { _(option_name), "" }; - ConfigOptionDef def; - def.type = coFloat; - def.set_default_value(new ConfigOptionFloat(0.0)); - def.width = field_width/*50*/; + ManipulationEditor* editor = new ManipulationEditor(this, opt_key, axis); + m_editors.push_back(editor); - if (option_name == "Scale") { - // Add "uniform scaling" button in front of "Scale" option - line.near_label_widget = [this](wxWindow* parent) { - auto btn = new LockButton(parent, wxID_ANY); - btn->Bind(wxEVT_BUTTON, [btn, this](wxCommandEvent &event){ - event.Skip(); - wxTheApp->CallAfter([btn, this]() { set_uniform_scaling(btn->IsLocked()); }); - }); - m_lock_bnt = btn; - return btn; - }; - // Add reset scale button - auto reset_scale_button = [this](wxWindow* parent) { - auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo")); - btn->SetToolTip(_(L("Reset scale"))); - m_reset_scale_button = btn; - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn, wxBU_EXACTFIT); - btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Reset scale"))); - change_scale_value(0, 100.); - change_scale_value(1, 100.); - change_scale_value(2, 100.); - }); - return sizer; - }; - line.append_widget(reset_scale_button); - } - else if (option_name == "Rotation") { - // Add reset rotation button - auto reset_rotation_button = [this](wxWindow* parent) { - auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo")); - btn->SetToolTip(_(L("Reset rotation"))); - m_reset_rotation_button = btn; - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn, wxBU_EXACTFIT); - btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) { - GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); - Selection& selection = canvas->get_selection(); + editors_grid_sizer->Add(editor, 0, wxALIGN_CENTER_VERTICAL); + }; + + // add Units + auto add_unit_text = [this, parent, editors_grid_sizer, height](std::string unit) + { + wxStaticText* unit_text = new wxStaticText(parent, wxID_ANY, _(unit)); + set_font_and_background_style(unit_text, wxGetApp().normal_font()); - if (selection.is_single_volume() || selection.is_single_modifier()) { - GLVolume* volume = const_cast(selection.get_volume(*selection.get_volume_idxs().begin())); - volume->set_volume_rotation(Vec3d::Zero()); - } - else if (selection.is_single_full_instance()) { - for (unsigned int idx : selection.get_volume_idxs()){ - GLVolume* volume = const_cast(selection.get_volume(idx)); - volume->set_instance_rotation(Vec3d::Zero()); - } - } - else - return; + // Unit text should be the same height as labels + wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->SetMinSize(wxSize(-1, height)); + sizer->Add(unit_text, 0, wxALIGN_CENTER_VERTICAL); - // Update rotation at the GLVolumes. - selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL); - selection.synchronize_unselected_volumes(); - // Copy rotation values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing. - canvas->do_rotate(L("Reset Rotation")); - - UpdateAndShow(true); - }); - return sizer; - }; - line.append_widget(reset_rotation_button); - } - else if (option_name == "Position") { - // Add drop to bed button - auto drop_to_bed_button = [=](wxWindow* parent) { - auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "drop_to_bed")); - btn->SetToolTip(_(L("Drop to bed"))); - m_drop_to_bed_button = btn; - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn, wxBU_EXACTFIT); - btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) { - // ??? - GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); - Selection& selection = canvas->get_selection(); - - if (selection.is_single_volume() || selection.is_single_modifier()) { - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); - - const Geometry::Transformation& instance_trafo = volume->get_instance_transformation(); - Vec3d diff = m_cache.position - instance_trafo.get_matrix(true).inverse() * Vec3d(0., 0., get_volume_min_z(volume)); - - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Drop to bed"))); - change_position_value(0, diff.x()); - change_position_value(1, diff.y()); - change_position_value(2, diff.z()); - } - }); - return sizer; - }; - line.append_widget(drop_to_bed_button); - } - // Add empty bmp (Its size have to be equal to PrusaLockButton) in front of "Size" option to label alignment - else if (option_name == "Size") { - line.near_label_widget = [this](wxWindow* parent) { - return new wxStaticBitmap(parent, wxID_ANY, wxNullBitmap, wxDefaultPosition, - create_scaled_bitmap(m_parent, "one_layer_lock_on.png").GetSize()); - }; - } - - const std::string lower_name = boost::algorithm::to_lower_copy(option_name); - - for (const char *axis : { "_x", "_y", "_z" }) { - if (axis[1] == 'z') - def.sidetext = sidetext; - Option option = Option(def, lower_name + axis); - option.opt.full_width = true; - line.append_option(option); - } - - return line; + editors_grid_sizer->Add(sizer); + m_rescalable_sizers.push_back(sizer); }; - // Settings table - m_og->sidetext_width = 3; - m_og->append_line(add_og_to_object_settings(L("Position"), L("mm")), &m_move_Label); - m_og->append_line(add_og_to_object_settings(L("Rotation"), "°"), &m_rotate_Label); - m_og->append_line(add_og_to_object_settings(L("Scale"), "%"), &m_scale_Label); - m_og->append_line(add_og_to_object_settings(L("Size"), "mm")); + for (size_t axis_idx = 0; axis_idx < sizeof(axes); axis_idx++) + add_edit_boxes("position", axis_idx); + add_unit_text(L("mm")); - // call back for a rescale of button "Set uniform scale" - m_og->rescale_near_label_widget = [this](wxWindow* win) { - // rescale lock icon - auto *ctrl = dynamic_cast(win); - if (ctrl != nullptr) { - ctrl->msw_rescale(); - return; + // Add drop to bed button + m_drop_to_bed_button = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "drop_to_bed")); + m_drop_to_bed_button->SetToolTip(_(L("Drop to bed"))); + m_drop_to_bed_button->Bind(wxEVT_BUTTON, [=](wxCommandEvent& e) { + // ??? + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + Selection& selection = canvas->get_selection(); + + if (selection.is_single_volume() || selection.is_single_modifier()) { + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + + const Geometry::Transformation& instance_trafo = volume->get_instance_transformation(); + Vec3d diff = m_cache.position - instance_trafo.get_matrix(true).inverse() * Vec3d(0., 0., get_volume_min_z(volume)); + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Drop to bed"))); + change_position_value(0, diff.x()); + change_position_value(1, diff.y()); + change_position_value(2, diff.z()); } + }); + editors_grid_sizer->Add(m_drop_to_bed_button); - if (win == m_fix_throught_netfab_bitmap) + for (size_t axis_idx = 0; axis_idx < sizeof(axes); axis_idx++) + add_edit_boxes("rotation", axis_idx); + add_unit_text("°"); + + // Add reset rotation button + m_reset_rotation_button = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo")); + m_reset_rotation_button->SetToolTip(_(L("Reset rotation"))); + m_reset_rotation_button->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) { + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + Selection& selection = canvas->get_selection(); + + if (selection.is_single_volume() || selection.is_single_modifier()) { + GLVolume* volume = const_cast(selection.get_volume(*selection.get_volume_idxs().begin())); + volume->set_volume_rotation(Vec3d::Zero()); + } + else if (selection.is_single_full_instance()) { + for (unsigned int idx : selection.get_volume_idxs()) { + GLVolume* volume = const_cast(selection.get_volume(idx)); + volume->set_instance_rotation(Vec3d::Zero()); + } + } + else return; - // rescale "place" of the empty icon (to correct layout of the "Size" and "Scale") - if (dynamic_cast(win) != nullptr) - win->SetMinSize(create_scaled_bitmap(m_parent, "one_layer_lock_on.png").GetSize()); - }; + // Update rotation at the GLVolumes. + selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL); + selection.synchronize_unselected_volumes(); + // Copy rotation values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing. + canvas->do_rotate(L("Reset Rotation")); + + UpdateAndShow(true); + }); + editors_grid_sizer->Add(m_reset_rotation_button); + + for (size_t axis_idx = 0; axis_idx < sizeof(axes); axis_idx++) + add_edit_boxes("scale", axis_idx); + add_unit_text("%"); + + // Add reset scale button + m_reset_scale_button = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo")); + m_reset_scale_button->SetToolTip(_(L("Reset scale"))); + m_reset_scale_button->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Reset scale"))); + change_scale_value(0, 100.); + change_scale_value(1, 100.); + change_scale_value(2, 100.); + }); + editors_grid_sizer->Add(m_reset_scale_button); + + for (size_t axis_idx = 0; axis_idx < sizeof(axes); axis_idx++) + add_edit_boxes("size", axis_idx); + add_unit_text("mm"); + editors_grid_sizer->AddStretchSpacer(1); + + m_main_grid_sizer->Add(editors_grid_sizer, 1, wxEXPAND); + + m_og->sizer->Clear(true); + m_og->sizer->Add(m_main_grid_sizer, 1, wxEXPAND | wxALL, border); } - - void ObjectManipulation::Show(const bool show) { @@ -407,9 +423,9 @@ void ObjectManipulation::Show(const bool show) if (show && wxGetApp().get_mode() != comSimple) { // Show the label and the name of the STL in simple mode only. // Label "Name: " - m_og->get_grid_sizer()->Show(size_t(0), false); + m_main_grid_sizer->Show(size_t(0), false); // The actual name of the STL. - m_og->get_grid_sizer()->Show(size_t(1), false); + m_main_grid_sizer->Show(size_t(1), false); } } @@ -417,6 +433,7 @@ void ObjectManipulation::Show(const bool show) // Show the "World Coordinates" / "Local Coordintes" Combo in Advanced / Expert mode only. bool show_world_local_combo = wxGetApp().plater()->canvas3D()->get_selection().is_single_full_instance() && wxGetApp().get_mode() != comSimple; m_word_local_combo->Show(show_world_local_combo); + m_empty_str->Show(!show_world_local_combo); } } @@ -522,30 +539,40 @@ void ObjectManipulation::update_if_dirty() if (label_cache != new_label_localized) { label_cache = new_label_localized; widget->SetLabel(new_label_localized); + if (wxOSX) set_font_and_background_style(widget, wxGetApp().normal_font()); } }; update_label(m_cache.move_label_string, m_new_move_label_string, m_move_Label); update_label(m_cache.rotate_label_string, m_new_rotate_label_string, m_rotate_Label); update_label(m_cache.scale_label_string, m_new_scale_label_string, m_scale_Label); - char axis[2] = "x"; - for (int i = 0; i < 3; ++ i, ++ axis[0]) { - auto update = [this, i, &axis](Vec3d &cached, Vec3d &cached_rounded, const char *key, const Vec3d &new_value) { + enum ManipulationEditorKey + { + mePosition = 0, + meRotation, + meScale, + meSize + }; + + for (int i = 0; i < 3; ++ i) { + auto update = [this, i](Vec3d &cached, Vec3d &cached_rounded, ManipulationEditorKey key_id, const Vec3d &new_value) { wxString new_text = double_to_string(new_value(i), 2); double new_rounded; new_text.ToDouble(&new_rounded); if (std::abs(cached_rounded(i) - new_rounded) > EPSILON) { cached_rounded(i) = new_rounded; - m_og->set_value(std::string(key) + axis, new_text); + const int id = key_id*3+i; + if (id >= 0) m_editors[id]->set_value(new_text); } cached(i) = new_value(i); }; - update(m_cache.position, m_cache.position_rounded, "position_", m_new_position); - update(m_cache.scale, m_cache.scale_rounded, "scale_", m_new_scale); - update(m_cache.size, m_cache.size_rounded, "size_", m_new_size); - update(m_cache.rotation, m_cache.rotation_rounded, "rotation_", m_new_rotation); + update(m_cache.position, m_cache.position_rounded, mePosition, m_new_position); + update(m_cache.scale, m_cache.scale_rounded, meScale, m_new_scale); + update(m_cache.size, m_cache.size_rounded, meSize, m_new_size); + update(m_cache.rotation, m_cache.rotation_rounded, meRotation, m_new_rotation); } + if (selection.requires_uniform_scale()) { m_lock_bnt->SetLock(true); m_lock_bnt->SetToolTip(_(L("You cannot use non-uniform scaling mode for multiple objects/parts selection"))); @@ -607,7 +634,11 @@ void ObjectManipulation::update_reset_buttons_visibility() show_drop_to_bed = (std::abs(min_z) > EPSILON); } - wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed]{ + wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed] { + // There is a case (under OSX), when this function is called after the Manipulation panel is hidden + // So, let check if Manipulation panel is still shown for this moment + if (!this->IsShown()) + return; m_reset_rotation_button->Show(show_rotation); m_reset_scale_button->Show(show_scale); m_drop_to_bed_button->Show(show_drop_to_bed); @@ -673,20 +704,18 @@ void ObjectManipulation::update_mirror_buttons_visibility() #ifndef __APPLE__ void ObjectManipulation::emulate_kill_focus() { - if (m_focused_option.empty()) + if (!m_focused_editor) return; - // we need to use a copy because the value of m_focused_option is modified inside on_change() and on_fill_empty_value() - std::string option = m_focused_option; - - // see TextCtrl::propagate_value() - if (static_cast(m_og->get_fieldc(option, 0)->getWindow())->GetValue().empty()) - on_fill_empty_value(option); - else - on_change(option, 0); + m_focused_editor->kill_focus(this); } #endif // __APPLE__ +void ObjectManipulation::update_item_name(const wxString& item_name) +{ + m_item_name->SetLabel(item_name); +} + void ObjectManipulation::update_warning_icon_state(const wxString& tooltip) { m_fix_throught_netfab_bitmap->SetBitmap(tooltip.IsEmpty() ? wxNullBitmap : m_manifold_warning_bmp.bmp()); @@ -817,76 +846,21 @@ void ObjectManipulation::do_scale(int axis, const Vec3d &scale) const wxGetApp().plater()->canvas3D()->do_scale(L("Set Scale")); } -void ObjectManipulation::on_change(t_config_option_key opt_key, const boost::any& value) +void ObjectManipulation::on_change(const std::string& opt_key, int axis, double new_value) { - Field* field = m_og->get_field(opt_key); - bool enter_pressed = (field != nullptr) && field->get_enter_pressed(); - if (!enter_pressed) - { - // if the change does not come from the user pressing the ENTER key - // we need to hide the visual hints in 3D scene - wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); - -#ifndef __APPLE__ - m_focused_option = ""; -#endif // __APPLE__ - } - else - // if the change comes from the user pressing the ENTER key, restore the key state - field->set_enter_pressed(false); - if (!m_cache.is_valid()) return; - int axis = opt_key.back() - 'x'; - double new_value = boost::any_cast(m_og->get_value(opt_key)); - - if (boost::starts_with(opt_key, "position_")) + if (opt_key == "position") change_position_value(axis, new_value); - else if (boost::starts_with(opt_key, "rotation_")) + else if (opt_key == "rotation") change_rotation_value(axis, new_value); - else if (boost::starts_with(opt_key, "scale_")) + else if (opt_key == "scale") change_scale_value(axis, new_value); - else if (boost::starts_with(opt_key, "size_")) + else if (opt_key == "size") change_size_value(axis, new_value); } -void ObjectManipulation::on_fill_empty_value(const std::string& opt_key) -{ - // needed to hide the visual hints in 3D scene - wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); -#ifndef __APPLE__ - m_focused_option = ""; -#endif // __APPLE__ - - if (!m_cache.is_valid()) - return; - - const Vec3d *vec = nullptr; - Vec3d *rounded = nullptr; - if (boost::starts_with(opt_key, "position_")) { - vec = &m_cache.position; - rounded = &m_cache.position_rounded; - } else if (boost::starts_with(opt_key, "rotation_")) { - vec = &m_cache.rotation; - rounded = &m_cache.rotation_rounded; - } else if (boost::starts_with(opt_key, "scale_")) { - vec = &m_cache.scale; - rounded = &m_cache.scale_rounded; - } else if (boost::starts_with(opt_key, "size_")) { - vec = &m_cache.size; - rounded = &m_cache.size_rounded; - } else - assert(false); - - if (vec != nullptr) { - int axis = opt_key.back() - 'x'; - wxString new_text = double_to_string((*vec)(axis)); - m_og->set_value(opt_key, new_text); - new_text.ToDouble(&(*rounded)(axis)); - } -} - void ObjectManipulation::set_uniform_scaling(const bool new_value) { const Selection &selection = wxGetApp().plater()->canvas3D()->get_selection(); @@ -923,7 +897,10 @@ void ObjectManipulation::set_uniform_scaling(const bool new_value) void ObjectManipulation::msw_rescale() { + const int em = wxGetApp().em_unit(); + m_item_name->SetMinSize(wxSize(20*em, wxDefaultCoord)); msw_rescale_word_local_combo(m_word_local_combo); + m_word_local_combo_sizer->SetMinSize(wxSize(-1, m_word_local_combo->GetBestHeight(-1))); m_manifold_warning_bmp.msw_rescale(); const wxString& tooltip = m_fix_throught_netfab_bitmap->GetToolTipText(); @@ -936,12 +913,120 @@ void ObjectManipulation::msw_rescale() m_reset_scale_button->msw_rescale(); m_reset_rotation_button->msw_rescale(); m_drop_to_bed_button->msw_rescale(); + m_lock_bnt->msw_rescale(); for (int id = 0; id < 3; ++id) m_mirror_buttons[id].first->msw_rescale(); + // rescale label-heights + // Text trick to grid sizer layout: + // Height of labels should be equivalent to the edit boxes + const int height = wxTextCtrl(parent(), wxID_ANY, "Br").GetBestHeight(-1); + for (wxBoxSizer* sizer : m_rescalable_sizers) + sizer->SetMinSize(wxSize(-1, height)); + + // rescale edit-boxes + for (ManipulationEditor* editor : m_editors) + editor->msw_rescale(); + get_og()->msw_rescale(); } +static const char axes[] = { 'x', 'y', 'z' }; +ManipulationEditor::ManipulationEditor(ObjectManipulation* parent, + const std::string& opt_key, + int axis) : + wxTextCtrl(parent->parent(), wxID_ANY, wxEmptyString, wxDefaultPosition, + wxSize(5*int(wxGetApp().em_unit()), wxDefaultCoord), wxTE_PROCESS_ENTER), + m_opt_key(opt_key), + m_axis(axis) +{ + set_font_and_background_style(this, wxGetApp().normal_font()); +#ifdef __WXOSX__ + this->OSXDisableAllSmartSubstitutions(); +#endif // __WXOSX__ + + // A name used to call handle_sidebar_focus_event() + m_full_opt_name = m_opt_key+"_"+axes[axis]; + + // Reset m_enter_pressed flag to _false_, when value is editing + this->Bind(wxEVT_TEXT, [this](wxEvent&) { m_enter_pressed = false; }, this->GetId()); + + this->Bind(wxEVT_TEXT_ENTER, [this, parent](wxEvent&) + { + m_enter_pressed = true; + parent->on_change(m_opt_key, m_axis, get_value()); + }, this->GetId()); + + this->Bind(wxEVT_KILL_FOCUS, [this, parent](wxFocusEvent& e) + { + parent->set_focused_editor(nullptr); + + if (!m_enter_pressed) + kill_focus(parent); + + e.Skip(); + }, this->GetId()); + + this->Bind(wxEVT_SET_FOCUS, [this, parent](wxFocusEvent& e) + { + parent->set_focused_editor(this); + + // needed to show the visual hints in 3D scene + wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(m_full_opt_name, true); + e.Skip(); + }, this->GetId()); + + this->Bind(wxEVT_CHAR, ([this](wxKeyEvent& event) + { + // select all text using Ctrl+A + if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL)) + this->SetSelection(-1, -1); //select all + event.Skip(); + })); +} + +void ManipulationEditor::msw_rescale() +{ + const int em = wxGetApp().em_unit(); + SetMinSize(wxSize(5 * em, wxDefaultCoord)); +} + +double ManipulationEditor::get_value() +{ + wxString str = GetValue(); + + double value; + // Replace the first occurence of comma in decimal number. + str.Replace(",", ".", false); + if (str == ".") + value = 0.0; + + if ((str.IsEmpty() || !str.ToCDouble(&value)) && !m_valid_value.IsEmpty()) { + str = m_valid_value; + SetValue(str); + str.ToCDouble(&value); + } + + return value; +} + +void ManipulationEditor::set_value(const wxString& new_value) +{ + if (new_value.IsEmpty()) + return; + m_valid_value = new_value; + SetValue(m_valid_value); +} + +void ManipulationEditor::kill_focus(ObjectManipulation* parent) +{ + parent->on_change(m_opt_key, m_axis, get_value()); + + // if the change does not come from the user pressing the ENTER key + // we need to hide the visual hints in 3D scene + wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(m_full_opt_name, false); +} + } //namespace GUI } //namespace Slic3r diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index e4e190b5b..e6f99ab2a 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -16,6 +16,29 @@ namespace GUI { class Selection; +class ObjectManipulation; +class ManipulationEditor : public wxTextCtrl +{ + std::string m_opt_key; + int m_axis; + bool m_enter_pressed { false }; + wxString m_valid_value {wxEmptyString}; + + std::string m_full_opt_name; + +public: + ManipulationEditor(ObjectManipulation* parent, const std::string& opt_key, int axis); + ~ManipulationEditor() {} + + void msw_rescale(); + void set_value(const wxString& new_value); + void kill_focus(ObjectManipulation *parent); + +private: + double get_value(); +}; + + class ObjectManipulation : public OG_Settings { struct Cache @@ -53,6 +76,9 @@ class ObjectManipulation : public OG_Settings wxStaticText* m_scale_Label = nullptr; wxStaticText* m_rotate_Label = nullptr; + wxStaticText* m_item_name = nullptr; + wxStaticText* m_empty_str = nullptr; + // Non-owning pointers to the reset buttons, so we can hide and show them. ScalableButton* m_reset_scale_button = nullptr; ScalableButton* m_reset_rotation_button = nullptr; @@ -81,7 +107,7 @@ class ObjectManipulation : public OG_Settings Vec3d m_new_rotation; Vec3d m_new_scale; Vec3d m_new_size; - bool m_new_enabled; + bool m_new_enabled {true}; bool m_uniform_scale {true}; // Does the object manipulation panel work in World or Local coordinates? bool m_world_coordinates = true; @@ -92,10 +118,19 @@ class ObjectManipulation : public OG_Settings wxStaticBitmap* m_fix_throught_netfab_bitmap; #ifndef __APPLE__ - // Currently focused option name (empty if none) - std::string m_focused_option; + // Currently focused editor (nullptr if none) + ManipulationEditor* m_focused_editor {nullptr}; #endif // __APPLE__ + wxFlexGridSizer* m_main_grid_sizer; + wxFlexGridSizer* m_labels_grid_sizer; + + // sizers, used for msw_rescale + wxBoxSizer* m_word_local_combo_sizer; + std::vector m_rescalable_sizers; + + std::vector m_editors; + public: ObjectManipulation(wxWindow* parent); ~ObjectManipulation() {} @@ -122,8 +157,15 @@ public: void emulate_kill_focus(); #endif // __APPLE__ + void update_item_name(const wxString &item_name); void update_warning_icon_state(const wxString& tooltip); void msw_rescale(); + void on_change(const std::string& opt_key, int axis, double new_value); + void set_focused_editor(ManipulationEditor* focused_editor) { +#ifndef __APPLE__ + m_focused_editor = focused_editor; +#endif // __APPLE__ + } private: void reset_settings_value(); @@ -140,9 +182,6 @@ private: void change_scale_value(int axis, double value); void change_size_value(int axis, double value); void do_scale(int axis, const Vec3d &scale) const; - - void on_change(t_config_option_key opt_key, const boost::any& value); - void on_fill_empty_value(const std::string& opt_key); }; }} diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 284ec33fa..0809262b7 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -221,6 +221,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view m_choice_view_type->Append(_(L("Height"))); m_choice_view_type->Append(_(L("Width"))); m_choice_view_type->Append(_(L("Speed"))); + m_choice_view_type->Append(_(L("Fan speed"))); m_choice_view_type->Append(_(L("Volumetric flow rate"))); m_choice_view_type->Append(_(L("Tool"))); m_choice_view_type->Append(_(L("Filament"))); @@ -375,6 +376,8 @@ void Preview::load_print(bool keep_z_range) load_print_as_fff(keep_z_range); else if (tech == ptSLA) load_print_as_sla(); + + Layout(); } void Preview::reload_print(bool keep_volumes) @@ -490,18 +493,23 @@ void Preview::show_hide_ui_elements(const std::string& what) m_choice_view_type->Show(visible); } -void Preview::reset_sliders() +void Preview::reset_sliders(bool reset_all) { m_enabled = false; // reset_double_slider(); - m_double_slider_sizer->Hide((size_t)0); + if (reset_all) + m_double_slider_sizer->Hide((size_t)0); + else + m_double_slider_sizer->GetItem(size_t(0))->GetSizer()->Hide(1); } void Preview::update_sliders(const std::vector& layers_z, bool keep_z_range) { m_enabled = true; + update_double_slider(layers_z, keep_z_range); m_double_slider_sizer->Show((size_t)0); + Layout(); } @@ -558,12 +566,12 @@ void Preview::on_checkbox_legend(wxCommandEvent& evt) m_canvas_widget->Refresh(); } -void Preview::update_view_type() +void Preview::update_view_type(bool slice_completed) { const DynamicPrintConfig& config = wxGetApp().preset_bundle->project_config; - const wxString& choice = !config.option("colorprint_heights")->values.empty() && - wxGetApp().extruders_edited_cnt()==1 ? + const wxString& choice = !wxGetApp().plater()->model().custom_gcode_per_height.empty() /*&& + (wxGetApp().extruders_edited_cnt()==1 || !slice_completed) */? _(L("Color Print")) : config.option("wiping_volumes_matrix")->values.size() > 1 ? _(L("Tool")) : @@ -581,6 +589,8 @@ void Preview::update_view_type() void Preview::create_double_slider() { m_slider = new DoubleSlider(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 @@ -590,10 +600,11 @@ void Preview::create_double_slider() Bind(wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) { - wxGetApp().preset_bundle->project_config.option("colorprint_heights")->values = m_slider->GetTicksValues(); + Model& model = wxGetApp().plater()->model(); + model.custom_gcode_per_height = m_slider->GetTicksValues(); m_schedule_background_process(); - update_view_type(); + update_view_type(false); reload_print(); }); @@ -626,6 +637,24 @@ 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, + const std::vector& layers_z) +{ + // All ticks that would end up outside the slider range should be erased. + // TODO: this should be placed into more appropriate part of code, + // 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) + { + auto it = std::lower_bound(layers_z.begin(), layers_z.end(), val.height - DoubleSlider::epsilon()); + return it == layers_z.end(); + }), + ticks_from_model.end()); + if (ticks_from_model.size() != old_size) + m_schedule_background_process(); +} + void Preview::update_double_slider(const std::vector& layers_z, bool keep_z_range) { // Save the initial slider span. @@ -641,8 +670,8 @@ void Preview::update_double_slider(const std::vector& layers_z, bool kee bool snap_to_min = force_sliders_full_range || m_slider->is_lower_at_min(); bool snap_to_max = force_sliders_full_range || m_slider->is_higher_at_max(); - std::vector &ticks_from_config = (wxGetApp().preset_bundle->project_config.option("colorprint_heights"))->values; - check_slider_values(ticks_from_config, layers_z); + std::vector &ticks_from_model = wxGetApp().plater()->model().custom_gcode_per_height; + check_slider_values(ticks_from_model, layers_z); m_slider->SetSliderValues(layers_z); assert(m_slider->GetMinValue() == 0); @@ -664,33 +693,12 @@ void Preview::update_double_slider(const std::vector& layers_z, bool kee } m_slider->SetSelectionSpan(idx_low, idx_high); - m_slider->SetTicksValues(ticks_from_config); + m_slider->SetTicksValues(ticks_from_model); bool color_print_enable = (wxGetApp().plater()->printer_technology() == ptFFF); - if (color_print_enable) { - const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->printers.get_edited_preset().config; - if (cfg.opt("nozzle_diameter")->values.size() > 1) - color_print_enable = false; - } - m_slider->EnableTickManipulation(color_print_enable); -} -void Preview::check_slider_values(std::vector& ticks_from_config, - const std::vector &layers_z) -{ - // All ticks that would end up outside the slider range should be erased. - // TODO: this should be placed into more appropriate part of code, - // this function is e.g. not called when the last object is deleted - unsigned int old_size = ticks_from_config.size(); - ticks_from_config.erase(std::remove_if(ticks_from_config.begin(), ticks_from_config.end(), - [layers_z](double val) - { - auto it = std::lower_bound(layers_z.begin(), layers_z.end(), val - DoubleSlider::epsilon()); - return it == layers_z.end(); - }), - ticks_from_config.end()); - if (ticks_from_config.size() != old_size) - m_schedule_background_process(); + m_slider->EnableTickManipulation(color_print_enable); + m_slider->SetManipulationState(wxGetApp().extruders_edited_cnt()); } void Preview::reset_double_slider() @@ -751,7 +759,7 @@ void Preview::load_print_as_fff(bool keep_z_range) if (! has_layers) { - reset_sliders(); + reset_sliders(true); m_canvas->reset_legend_texture(); m_canvas_widget->Refresh(); return; @@ -774,17 +782,15 @@ 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) { - colors = GCodePreviewData::ColorPrintColors(); - if (! gcode_preview_data_valid) { - //FIXME accessing full_config() is pretty expensive. - // Only initialize color_print_values for the initial preview, not for the full preview where the color_print_values is extracted from the G-code. - const auto& config = wxGetApp().preset_bundle->project_config; - color_print_values = config.option("colorprint_heights")->values; - } + colors = wxGetApp().plater()->get_colors_for_color_print(); + colors.push_back("#808080"); // gray color for pause print or custom G-code + + if (!gcode_preview_data_valid) + color_print_values = wxGetApp().plater()->model().custom_gcode_per_height; } else if ((m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::Filament)) { @@ -807,37 +813,18 @@ void Preview::load_print_as_fff(bool keep_z_range) } else if (gcode_preview_data_valid || (m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::Tool)) { - const ConfigOptionStrings* extruders_opt = dynamic_cast(m_config->option("extruder_colour")); - const ConfigOptionStrings* filamemts_opt = dynamic_cast(m_config->option("filament_colour")); - unsigned int colors_count = std::max((unsigned int)extruders_opt->values.size(), (unsigned int)filamemts_opt->values.size()); - - unsigned char rgb[3]; - for (unsigned int i = 0; i < colors_count; ++i) - { - std::string color = m_config->opt_string("extruder_colour", i); - if (!PresetBundle::parse_color(color, rgb)) - { - color = m_config->opt_string("filament_colour", i); - if (!PresetBundle::parse_color(color, rgb)) - color = "#FFFFFF"; - } - - colors.emplace_back(color); - } + colors = wxGetApp().plater()->get_extruder_colors_from_plater_config(); color_print_values.clear(); } if (IsShown()) { + m_canvas->set_selected_extruder(0); if (gcode_preview_data_valid) { // Load the real G-code preview. m_canvas->load_gcode_preview(*m_gcode_preview_data, colors); m_loaded = true; } else { - // disable color change information for multi-material presets - if (wxGetApp().extruders_edited_cnt() > 1) - color_print_values.clear(); - // Load the initial preview based on slices, not the final G-code. m_canvas->load_preview(colors, color_print_values); } @@ -846,7 +833,7 @@ void Preview::load_print_as_fff(bool keep_z_range) std::vector zs = m_canvas->get_current_print_zs(true); if (zs.empty()) { // all layers filtered out - reset_sliders(); + reset_sliders(true); m_canvas_widget->Refresh(); } else update_sliders(zs, keep_z_range); @@ -877,7 +864,7 @@ void Preview::load_print_as_sla() n_layers = (unsigned int)zs.size(); if (n_layers == 0) { - reset_sliders(); + reset_sliders(true); m_canvas_widget->Refresh(); } diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 08d5991b4..b0dac4223 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -5,6 +5,7 @@ #include "libslic3r/Point.hpp" #include +#include "libslic3r/Model.hpp" class wxNotebook; class wxGLCanvas; @@ -12,6 +13,7 @@ class wxBoxSizer; class wxStaticText; class wxChoice; class wxComboCtrl; +class wxBitmapComboBox; class wxCheckBox; class DoubleSlider; @@ -101,7 +103,7 @@ class Preview : public wxPanel bool m_loaded; bool m_enabled; - DoubleSlider* m_slider {nullptr}; + DoubleSlider* m_slider {nullptr}; public: Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, @@ -128,7 +130,7 @@ public: void move_double_slider(wxKeyEvent& evt); void edit_double_slider(wxKeyEvent& evt); - void update_view_type(); + void update_view_type(bool slice_completed); bool is_loaded() const { return m_loaded; } @@ -140,7 +142,7 @@ private: void show_hide_ui_elements(const std::string& what); - void reset_sliders(); + void reset_sliders(bool reset_all); void update_sliders(const std::vector& layers_z, bool keep_z_range = false); void on_size(wxSizeEvent& evt); @@ -154,9 +156,9 @@ private: // Create/Update/Reset double slider on 3dPreview void create_double_slider(); + void check_slider_values(std::vector &ticks_from_model, + const std::vector &layers_z); void update_double_slider(const std::vector& layers_z, bool keep_z_range = false); - void check_slider_values(std::vector &ticks_from_config, - const std::vector &layers_z); void reset_double_slider(); // update DoubleSlider after keyDown in canvas void update_double_slider_from_canvas(wxKeyEvent& event); diff --git a/src/slic3r/GUI/GUI_Utils.cpp b/src/slic3r/GUI/GUI_Utils.cpp index d5753f2cc..5090382ce 100644 --- a/src/slic3r/GUI/GUI_Utils.cpp +++ b/src/slic3r/GUI/GUI_Utils.cpp @@ -55,7 +55,7 @@ void on_window_geometry(wxTopLevelWindow *tlw, std::function callback) #endif } -wxDEFINE_EVENT(EVT_DPI_CHANGED, DpiChangedEvent); +wxDEFINE_EVENT(EVT_DPI_CHANGED_SLICER, DpiChangedEvent); #ifdef _WIN32 template typename F::FN winapi_get_function(const wchar_t *dll, const char *fn_name) { diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index c47714e46..f7bebd577 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -50,7 +50,7 @@ struct DpiChangedEvent : public wxEvent { } }; -wxDECLARE_EVENT(EVT_DPI_CHANGED, DpiChangedEvent); +wxDECLARE_EVENT(EVT_DPI_CHANGED_SLICER, DpiChangedEvent); template class DPIAware : public P { @@ -75,7 +75,7 @@ public: // recalc_font(); - this->Bind(EVT_DPI_CHANGED, [this](const DpiChangedEvent &evt) { + this->Bind(EVT_DPI_CHANGED_SLICER, [this](const DpiChangedEvent &evt) { m_scale_factor = (float)evt.dpi / (float)DPI_DEFAULT; m_new_font_point_size = get_default_font_for_dpi(evt.dpi).GetPointSize(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 4356f6c36..dfcd2207f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -252,7 +252,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) const sla::SupportPoint& support_point = m_editing_mode ? m_editing_cache[i].support_point : m_normal_cache[i]; const bool& point_selected = m_editing_mode ? m_editing_cache[i].selected : false; - if (is_point_clipped(support_point.pos.cast())) + if (is_mesh_point_clipped(support_point.pos.cast())) continue; // First decide about the color of the point. @@ -335,14 +335,14 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) -bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point) const +bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const { if (m_clipping_plane_distance == 0.f) return false; Vec3d transformed_point = m_model_object->instances.front()->get_transformation().get_matrix() * point; transformed_point(2) += m_z_shift; - return m_clipping_plane->distance(transformed_point) < 0.; + return m_clipping_plane->is_point_clipped(transformed_point); } @@ -391,29 +391,17 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair hits; - std::vector normals; - m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, &hits, &normals); - - // We must also take care of the clipping plane (if active) - unsigned i = 0; - if (m_clipping_plane_distance != 0.f) { - for (i=0; i())) - break; + Vec3f hit; + Vec3f normal; + if (m_mesh_raycaster->unproject_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 (i==hits.size() || (hits.size()-i) % 2 != 0) { - // All hits are either clipped, or there is an odd number of unclipped - // hits - meaning the nearest must be from inside the mesh. + else return false; } - // Calculate and return both the point and the facet normal. - pos_and_normal = std::make_pair(hits[i], normals[i]); - return true; -} - // 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 @@ -481,20 +469,16 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous 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((trafo.get_matrix() * points[idx]).cast()); + points_inside.push_back(points[idx].cast()); // 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, - [this](const Vec3f& pt) { return is_point_clipped(pt.cast()); })) + for (size_t idx : m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get())) { - const sla::SupportPoint &support_point = m_editing_cache[points_idxs[idx]].support_point; - if (!is_point_clipped(support_point.pos.cast())) { - if (rectangle_status == GLSelectionRectangle::Deselect) - unselect_point(points_idxs[idx]); - else - select_point(points_idxs[idx]); - } - } + if (rectangle_status == GLSelectionRectangle::Deselect) + unselect_point(points_idxs[idx]); + else + select_point(points_idxs[idx]); + } return true; } @@ -1202,10 +1186,10 @@ void GLGizmoSlaSupports::get_data_from_backend() void GLGizmoSlaSupports::auto_generate() { - wxMessageDialog dlg(GUI::wxGetApp().plater(), _(L( - "Autogeneration will erase all manually edited points.\n\n" - "Are you sure you want to do it?\n" - )), _(L("Warning")), wxICON_WARNING | wxYES | wxNO); + wxMessageDialog dlg(GUI::wxGetApp().plater(), + _(L("Autogeneration will erase all manually edited points.")) + "\n\n" + + _(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) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Autogenerate support points"))); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index cf5245f19..15b2df80e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -125,7 +125,7 @@ private: mutable std::unique_ptr m_supports_clipper; std::vector get_config_options(const std::vector& keys) const; - bool is_point_clipped(const Vec3d& point) const; + bool is_mesh_point_clipped(const Vec3d& point) 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, diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 16fa30fa7..8ea2f176a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -417,6 +417,9 @@ bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt) bool GLGizmosManager::on_mouse(wxMouseEvent& evt) { + // used to set a right up event as processed when needed + static bool pending_right_up = false; + Point pos(evt.GetX(), evt.GetY()); Vec2d mouse_pos((double)evt.GetX(), (double)evt.GetY()); @@ -442,7 +445,14 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) else if (evt.MiddleUp()) m_mouse_capture.middle = false; else if (evt.RightUp()) + { m_mouse_capture.right = false; + if (pending_right_up) + { + pending_right_up = false; + processed = true; + } + } else if (evt.Dragging() && m_mouse_capture.any()) // if the button down was done on this toolbar, prevent from dragging into the scene processed = true; @@ -473,8 +483,12 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) } } else if (evt.RightDown() && (selected_object_idx != -1) && (m_current == SlaSupports) && 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 processed = true; @@ -941,7 +955,12 @@ bool GLGizmosManager::generate_icons_texture() const states.push_back(std::make_pair(0, false)); states.push_back(std::make_pair(0, true)); - bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, (unsigned int)(m_overlay_icons_size * m_overlay_scale), true); + unsigned int sprite_size_px = (unsigned int)(m_overlay_icons_size * m_overlay_scale); + // force even size + if (sprite_size_px % 2 != 0) + sprite_size_px += 1; + + bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, sprite_size_px, false); if (res) m_icons_texture_dirty = false; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index f649c98b2..0defb1348 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -114,8 +114,17 @@ public: m_serializing = true; + // Following is needed to know which to be turn on, but not actually modify + // m_current prematurely, so activate_gizmo is not confused. + EType old_current = m_current; ar(m_current); + EType new_current = m_current; + m_current = old_current; + // activate_gizmo call sets m_current and calls set_state for the gizmo + // it does nothing in case the gizmo is already activated + // it can safely be called for Undefined gizmo + activate_gizmo(new_current); if (m_current != Undefined) m_gizmos[m_current]->load(ar); } diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 8e4d9eebf..4463e646c 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -317,6 +317,22 @@ void ImGuiWrapper::text(const wxString &label) this->text(label_utf8.c_str()); } +bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float v_max, const char* format/* = "%.3f"*/, float power/* = 1.0f*/) +{ + return ImGui::SliderFloat(label, v, v_min, v_max, format, power); +} + +bool ImGuiWrapper::slider_float(const std::string& label, float* v, float v_min, float v_max, const char* format/* = "%.3f"*/, float power/* = 1.0f*/) +{ + return this->slider_float(label.c_str(), v, v_min, v_max, format, power); +} + +bool ImGuiWrapper::slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format/* = "%.3f"*/, float power/* = 1.0f*/) +{ + auto label_utf8 = into_u8(label); + return this->slider_float(label_utf8.c_str(), v, v_min, v_max, format, power); +} + bool ImGuiWrapper::combo(const wxString& label, const std::vector& options, int& selection) { // this is to force the label to the left of the widget: @@ -528,6 +544,9 @@ void ImGuiWrapper::init_style() // Slider set_color(ImGuiCol_SliderGrab, COL_ORANGE_DARK); set_color(ImGuiCol_SliderGrabActive, COL_ORANGE_LIGHT); + + // Separator + set_color(ImGuiCol_Separator, COL_ORANGE_LIGHT); } void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index c6550351e..7cce60367 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -66,6 +66,9 @@ public: void text(const char *label); void text(const std::string &label); void text(const wxString &label); + bool slider_float(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); + bool slider_float(const std::string& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); + bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); bool combo(const wxString& label, const std::vector& options, int& selection); // Use -1 to not mark any option as selected bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected); diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 59415f46c..d10e353dd 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -157,6 +157,7 @@ void KBShortcutsDialog::fill_shortcuts() 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. diff --git a/src/slic3r/GUI/LambdaObjectDialog.cpp b/src/slic3r/GUI/LambdaObjectDialog.cpp index 4d1cb0658..63c8d329c 100644 --- a/src/slic3r/GUI/LambdaObjectDialog.cpp +++ b/src/slic3r/GUI/LambdaObjectDialog.cpp @@ -192,7 +192,7 @@ ConfigOptionsGroupShp LambdaObjectDialog::init_modificator_options_page(const wx else panel->SetSizer(optgroup->sizer); - return optgroup; + return optgroup; } diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index aa7b3993b..e41067602 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -24,6 +24,7 @@ #include "PrintHostDialogs.hpp" #include "wxExtensions.hpp" #include "GUI_ObjectList.hpp" +#include "Mouse3DController.hpp" #include "I18N.hpp" #include @@ -261,7 +262,7 @@ bool MainFrame::can_export_supports() const const PrintObjects& objects = m_plater->sla_print().objects(); for (const SLAPrintObject* object : objects) { - if (object->has_mesh(slaposBasePool) || object->has_mesh(slaposSupportTree)) + if (object->has_mesh(slaposPad) || object->has_mesh(slaposSupportTree)) { can_export = true; break; @@ -686,6 +687,11 @@ void MainFrame::init_menubar() helpMenu->AppendSeparator(); append_menu_item(helpMenu, wxID_ANY, _(L("Keyboard Shortcuts")) + sep + "&?", _(L("Show the list of the keyboard shortcuts")), [this](wxCommandEvent&) { wxGetApp().keyboard_shortcuts(); }); +#if ENABLE_THUMBNAIL_GENERATOR_DEBUG + helpMenu->AppendSeparator(); + append_menu_item(helpMenu, wxID_ANY, _(L("DEBUG gcode thumbnails")), _(L("DEBUG ONLY - read the selected gcode file and generates png for the contained thumbnails")), + [this](wxCommandEvent&) { wxGetApp().gcode_thumbnails_debug(); }); +#endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG } // menubar @@ -921,7 +927,7 @@ void MainFrame::load_config_file() wxString file; if (dlg.ShowModal() == wxID_OK) file = dlg.GetPath(); - if (! file.IsEmpty() && this->load_config_file(file.ToUTF8().data())) { + if (! file.IsEmpty() && this->load_config_file(file.ToUTF8().data())) { wxGetApp().app_config->update_config_dir(get_dir_name(file)); m_last_config = file; } diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 449ab3a72..7cb6b61c8 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -151,8 +151,8 @@ Vec3f MeshRaycaster::AABBWrapper::get_hit_normal(const igl::Hit& hit) const } -bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, - const Camera& camera, std::vector* positions, std::vector* normals) const +bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, + Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane) const { const std::array& viewport = camera.get_viewport(); const Transform3d& model_mat = camera.get_view_matrix(); @@ -178,25 +178,30 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; }); - // Now stuff the points in the provided vector and calculate normals if asked about them: - if (positions != nullptr) { - positions->clear(); - if (normals != nullptr) - normals->clear(); - for (const igl::Hit& hit : hits) { - positions->push_back(m_AABB_wrapper->get_hit_pos(hit)); + unsigned i = 0; - if (normals != nullptr) - normals->push_back(m_AABB_wrapper->get_hit_normal(hit)); + // 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())) + break; + + if (i==hits.size() || (hits.size()-i) % 2 != 0) { + // All hits are either clipped, or there is an odd number of unclipped + // hits - meaning the nearest must be from inside the mesh. + return false; } } + // 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]); return true; } std::vector MeshRaycaster::get_unobscured_idxs(const Geometry::Transformation& trafo, const Camera& camera, const std::vector& points, - std::function fn_ignore_hit) const + const ClippingPlane* clipping_plane) const { std::vector out; @@ -205,19 +210,24 @@ std::vector MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo Vec3f direction_to_camera_mesh = (instance_matrix_no_translation_no_scaling.inverse().cast() * direction_to_camera).normalized().eval(); Vec3f scaling = trafo.get_scaling_factor().cast(); direction_to_camera_mesh = Vec3f(direction_to_camera_mesh(0)*scaling(0), direction_to_camera_mesh(1)*scaling(1), direction_to_camera_mesh(2)*scaling(2)); + const Transform3f inverse_trafo = trafo.get_matrix().inverse().cast(); for (size_t i=0; iis_point_clipped(pt.cast())) + continue; + bool is_obscured = false; // Cast a ray in the direction of the camera and look for intersection with the mesh: std::vector hits; - // Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies. + // 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), - pt + direction_to_camera_mesh * EPSILON, direction_to_camera_mesh, hits)) { + inverse_trafo * pt + direction_to_camera_mesh * EPSILON, direction_to_camera_mesh, hits)) { std::sort(hits.begin(), hits.end(), [](const igl::Hit& h1, const igl::Hit& h2) { return h1.t < h2.t; }); + // 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) @@ -226,11 +236,12 @@ std::vector MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo // Eradicate all hits that the caller wants to ignore for (unsigned j=0; jget_hit_pos(hit))) { + if (clipping_plane && clipping_plane->is_point_clipped(trafo.get_matrix() * m_AABB_wrapper->get_hit_pos(hit).cast())) { hits.erase(hits.begin()+j); --j; } } + // FIXME: the intersection could in theory be behind the camera, but as of now we only have camera direction. // Also, the threshold is in mesh coordinates, not in actual dimensions. if (! hits.empty()) diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index a2be2677b..e4c4c20d2 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -50,6 +50,7 @@ public: return (-get_normal().dot(pt) + m_data[3]); } + bool is_point_clipped(const Vec3d& point) const { return distance(point) < 0.; } void set_normal(const Vec3d& normal) { for (size_t i=0; i<3; ++i) m_data[i] = normal(i); } void set_offset(double offset) { m_data[3] = offset; } Vec3d get_normal() const { return Vec3d(m_data[0], m_data[1], m_data[2]); } @@ -98,10 +99,10 @@ public: void set_camera(const Camera& camera); bool unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, - std::vector* positions = nullptr, std::vector* normals = nullptr) const; + Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane = nullptr) const; std::vector get_unobscured_idxs(const Geometry::Transformation& trafo, const Camera& camera, - const std::vector& points, std::function fn_ignore_hit) const; + const std::vector& points, const ClippingPlane* clipping_plane = nullptr) const; Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const; diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp new file mode 100644 index 000000000..89513709d --- /dev/null +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -0,0 +1,821 @@ +#include "libslic3r/libslic3r.h" +#include "Mouse3DController.hpp" + +#include "Camera.hpp" +#include "GUI_App.hpp" +#include "PresetBundle.hpp" +#include "AppConfig.hpp" + +#include + +#include +#include +#include "I18N.hpp" + +#include + +// WARN: If updating these lists, please also update resources/udev/90-3dconnexion.rules + +static const std::vector _3DCONNEXION_VENDORS = +{ + 0x046d, // LOGITECH = 1133 // Logitech (3Dconnexion is made by Logitech) + 0x256F // 3DCONNECTION = 9583 // 3Dconnexion +}; + +// See: https://github.com/FreeSpacenav/spacenavd/blob/a9eccf34e7cac969ee399f625aef827f4f4aaec6/src/dev.c#L202 +static const std::vector _3DCONNEXION_DEVICES = +{ + 0xc603, /* 50691 spacemouse plus XT */ + 0xc605, /* 50693 cadman */ + 0xc606, /* 50694 spacemouse classic */ + 0xc621, /* 50721 spaceball 5000 */ + 0xc623, /* 50723 space traveller */ + 0xc625, /* 50725 space pilot */ + 0xc626, /* 50726 space navigator *TESTED* */ + 0xc627, /* 50727 space explorer */ + 0xc628, /* 50728 space navigator for notebooks*/ + 0xc629, /* 50729 space pilot pro*/ + 0xc62b, /* 50731 space mouse pro*/ + 0xc62e, /* 50734 spacemouse wireless (USB cable) *TESTED* */ + 0xc62f, /* 50735 spacemouse wireless receiver */ + 0xc631, /* 50737 spacemouse pro wireless *TESTED* */ + 0xc632, /* 50738 spacemouse pro wireless receiver */ + 0xc633, /* 50739 spacemouse enterprise */ + 0xc635, /* 50741 spacemouse compact *TESTED* */ + 0xc636, /* 50742 spacemouse module */ + 0xc640, /* 50752 nulooq */ + 0xc652, /* 50770 3Dconnexion universal receiver *TESTED* */ +}; + +namespace Slic3r { +namespace GUI { + +const double Mouse3DController::State::DefaultTranslationScale = 2.5; +const double Mouse3DController::State::MaxTranslationDeadzone = 0.2; +const double Mouse3DController::State::DefaultTranslationDeadzone = 0.5 * Mouse3DController::State::MaxTranslationDeadzone; +const float Mouse3DController::State::DefaultRotationScale = 1.0f; +const float Mouse3DController::State::MaxRotationDeadzone = (float)Mouse3DController::State::MaxTranslationDeadzone; +const float Mouse3DController::State::DefaultRotationDeadzone = 0.5f * Mouse3DController::State::MaxRotationDeadzone; + +Mouse3DController::State::State() + : m_buttons_enabled(false) + , m_translation_params(DefaultTranslationScale, DefaultTranslationDeadzone) + , m_rotation_params(DefaultRotationScale, DefaultRotationDeadzone) + , m_mouse_wheel_counter(0) +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + , m_translation_queue_max_size(0) + , m_rotation_queue_max_size(0) + , m_buttons_queue_max_size(0) +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +{ +} + +void Mouse3DController::State::append_translation(const Vec3d& translation) +{ + while (m_translation.queue.size() >= m_translation.max_size) + { + m_translation.queue.pop(); + } + m_translation.queue.push(translation); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_translation_queue_max_size = std::max(m_translation_queue_max_size, m_translation.queue.size()); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +} + +void Mouse3DController::State::append_rotation(const Vec3f& rotation) +{ + while (m_rotation.queue.size() >= m_rotation.max_size) + { + m_rotation.queue.pop(); + } + m_rotation.queue.push(rotation); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_rotation_queue_max_size = std::max(m_rotation_queue_max_size, m_rotation.queue.size()); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + if (rotation(0) != 0.0f) + ++m_mouse_wheel_counter; +} + +void Mouse3DController::State::append_button(unsigned int id) +{ + if (!m_buttons_enabled) + return; + + m_buttons.push(id); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, m_buttons.size()); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +} + +bool Mouse3DController::State::process_mouse_wheel() +{ + if (m_mouse_wheel_counter == 0) + return false; + else if (!m_rotation.queue.empty()) + { + --m_mouse_wheel_counter; + return true; + } + + m_mouse_wheel_counter = 0; + return true; +} + +void Mouse3DController::State::set_queues_max_size(size_t size) +{ + if (size > 0) + { + m_translation.max_size = size; + m_rotation.max_size = size; + +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_translation_queue_max_size = 0; + m_rotation_queue_max_size = 0; + m_buttons_queue_max_size = 0; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + } +} + +bool Mouse3DController::State::apply(Camera& camera) +{ + if (!wxGetApp().IsActive()) + return false; + + bool ret = false; + + if (has_translation()) + { + const Vec3d& translation = m_translation.queue.front(); + 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())); + m_translation.queue.pop(); + ret = true; + } + + if (has_rotation()) + { + 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); + m_rotation.queue.pop(); + ret = true; + } + + if (m_buttons_enabled && has_button()) + { + unsigned int button = m_buttons.front(); + switch (button) + { + case 0: { camera.update_zoom(1.0); break; } + case 1: { camera.update_zoom(-1.0); break; } + default: { break; } + } + m_buttons.pop(); + ret = true; + } + + return ret; +} + +Mouse3DController::Mouse3DController() + : m_initialized(false) + , m_device(nullptr) + , m_device_str("") + , m_running(false) + , m_settings_dialog(false) +{ + m_last_time = std::chrono::high_resolution_clock::now(); +} + +void Mouse3DController::init() +{ + if (m_initialized) + return; + + // Initialize the hidapi library + int res = hid_init(); + if (res != 0) + { + BOOST_LOG_TRIVIAL(error) << "Unable to initialize hidapi library"; + return; + } + + m_initialized = true; +} + +void Mouse3DController::shutdown() +{ + if (!m_initialized) + return; + + stop(); + disconnect_device(); + + // Finalize the hidapi library + hid_exit(); + m_initialized = false; +} + +bool Mouse3DController::apply(Camera& camera) +{ + if (!m_initialized) + return false; + + std::lock_guard lock(m_mutex); + + // check if the user unplugged the device + if (!m_running && is_device_connected()) + { + disconnect_device(); + // hides the settings dialog if the user re-plug the device + m_settings_dialog = false; + } + + // check if the user plugged the device + if (connect_device()) + start(); + + return is_device_connected() ? m_state.apply(camera) : false; +} + +void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const +{ + if (!m_running || !m_settings_dialog) + return; + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + + imgui.set_next_window_pos(0.5f * (float)canvas_width, 0.5f * (float)canvas_height, ImGuiCond_Always, 0.5f, 0.5f); + imgui.set_next_window_bg_alpha(0.5f); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + + imgui.begin(_(L("3Dconnexion settings")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); + + const ImVec4& color = ImGui::GetStyleColorVec4(ImGuiCol_Separator); + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text(_(L("Device:"))); + ImGui::PopStyleColor(); + ImGui::SameLine(); + imgui.text(m_device_str); + + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text(_(L("Speed:"))); + ImGui::PopStyleColor(); + + float translation_scale = (float)m_state.get_translation_scale() / State::DefaultTranslationScale; + if (imgui.slider_float(_(L("Translation")) + "##1", &translation_scale, 0.5f, 2.0f, "%.1f")) + m_state.set_translation_scale(State::DefaultTranslationScale * (double)translation_scale); + + float rotation_scale = m_state.get_rotation_scale() / State::DefaultRotationScale; + if (imgui.slider_float(_(L("Rotation")) + "##1", &rotation_scale, 0.5f, 2.0f, "%.1f")) + m_state.set_rotation_scale(State::DefaultRotationScale * rotation_scale); + + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text(_(L("Deadzone:"))); + ImGui::PopStyleColor(); + + float translation_deadzone = (float)m_state.get_translation_deadzone(); + if (imgui.slider_float(_(L("Translation")) + "##2", &translation_deadzone, 0.0f, (float)State::MaxTranslationDeadzone, "%.2f")) + m_state.set_translation_deadzone((double)translation_deadzone); + + float rotation_deadzone = m_state.get_rotation_deadzone(); + if (imgui.slider_float(_(L("Rotation")) + "##2", &rotation_deadzone, 0.0f, State::MaxRotationDeadzone, "%.2f")) + m_state.set_rotation_deadzone(rotation_deadzone); + +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + ImGui::Separator(); + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text("DEBUG:"); + imgui.text("Vectors:"); + ImGui::PopStyleColor(); + Vec3f translation = m_state.get_translation().cast(); + Vec3f rotation = m_state.get_rotation(); + ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); + ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); + + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text("Queue size:"); + ImGui::PopStyleColor(); + + int translation_size[2] = { (int)m_state.get_translation_queue_size(), (int)m_state.get_translation_queue_max_size() }; + int rotation_size[2] = { (int)m_state.get_rotation_queue_size(), (int)m_state.get_rotation_queue_max_size() }; + int buttons_size[2] = { (int)m_state.get_buttons_queue_size(), (int)m_state.get_buttons_queue_max_size() }; + + ImGui::InputInt2("Translation##4", translation_size, ImGuiInputTextFlags_ReadOnly); + ImGui::InputInt2("Rotation##4", rotation_size, ImGuiInputTextFlags_ReadOnly); + ImGui::InputInt2("Buttons", buttons_size, ImGuiInputTextFlags_ReadOnly); + + int queue_size = (int)m_state.get_queues_max_size(); + if (ImGui::InputInt("Max size", &queue_size, 1, 1, ImGuiInputTextFlags_ReadOnly)) + { + if (queue_size > 0) + m_state.set_queues_max_size(queue_size); + } + + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text("Camera:"); + ImGui::PopStyleColor(); + Vec3f target = wxGetApp().plater()->get_camera().get_target().cast(); + ImGui::InputFloat3("Target", target.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + + imgui.end(); + + ImGui::PopStyleVar(); +} + +bool Mouse3DController::connect_device() +{ + static const long long DETECTION_TIME_MS = 2000; // seconds + + if (is_device_connected()) + return false; + + // check time since last detection took place + if (std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - m_last_time).count() < DETECTION_TIME_MS) + return false; + + m_last_time = std::chrono::high_resolution_clock::now(); + + // Enumerates devices + hid_device_info* devices = hid_enumerate(0, 0); + if (devices == nullptr) + { + BOOST_LOG_TRIVIAL(error) << "Unable to enumerate HID devices"; + return false; + } + + // Searches for 1st connected 3Dconnexion device + struct DeviceData + { + std::string path; + unsigned short usage_page; + unsigned short usage; + + DeviceData() + : path(""), usage_page(0), usage(0) + {} + DeviceData(const std::string& path, unsigned short usage_page, unsigned short usage) + : path(path), usage_page(usage_page), usage(usage) + {} + + bool has_valid_usage() const { return (usage_page == 1) && (usage == 8); } + }; + +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + hid_device_info* cur = devices; + std::cout << std::endl << "======================================================================================================================================" << std::endl; + std::cout << "Detected devices:" << std::endl; + while (cur != nullptr) + { + std::cout << "\""; + std::wcout << ((cur->manufacturer_string != nullptr) ? cur->manufacturer_string : L"Unknown"); + std::cout << "/"; + std::wcout << ((cur->product_string != nullptr) ? cur->product_string : L"Unknown"); + std::cout << "\" code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")"; + std::cout << " serial number: '"; + std::wcout << ((cur->serial_number != nullptr) ? cur->serial_number : L"Unknown"); + std::cout << "' usage page: " << cur->usage_page << " usage: " << cur->usage << " interface number: " << cur->interface_number << std::endl; + + cur = cur->next; + } +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + + // When using 3Dconnexion universal receiver, multiple devices are detected sharing the same vendor_id and product_id. + // To choose from them the right one we use: + // On Windows and Mac: usage_page == 1 and usage == 8 + // On Linux: as usage_page and usage are not defined (see hidapi.h) we try all detected devices until one is succesfully open + // When only a single device is detected, as for wired connections, vendor_id and product_id are enough + + // First we count all the valid devices from the enumerated list, + + hid_device_info* current = devices; + typedef std::pair DeviceIds; + typedef std::vector DeviceDataList; + typedef std::map DetectedDevices; + DetectedDevices detected_devices; +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << std::endl << "Detected 3D connexion devices:" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + while (current != nullptr) + { + unsigned short vendor_id = 0; + unsigned short product_id = 0; + + for (size_t i = 0; i < _3DCONNEXION_VENDORS.size(); ++i) + { + if (_3DCONNEXION_VENDORS[i] == current->vendor_id) + { + vendor_id = current->vendor_id; + break; + } + } + + if (vendor_id != 0) + { + for (size_t i = 0; i < _3DCONNEXION_DEVICES.size(); ++i) + { + if (_3DCONNEXION_DEVICES[i] == current->product_id) + { + product_id = current->product_id; + DeviceIds detected_device(vendor_id, product_id); + DetectedDevices::iterator it = detected_devices.find(detected_device); + if (it == detected_devices.end()) + it = detected_devices.insert(DetectedDevices::value_type(detected_device, DeviceDataList())).first; + + it->second.emplace_back(current->path, current->usage_page, current->usage); + +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::wcout << "\"" << ((current->manufacturer_string != nullptr) ? current->manufacturer_string : L"Unknown"); + std::cout << "/"; + std::wcout << ((current->product_string != nullptr) ? current->product_string : L"Unknown"); + std::cout << "\" code: " << current->vendor_id << "/" << current->product_id << " (" << std::hex << current->vendor_id << "/" << current->product_id << std::dec << ")"; + std::cout << " serial number: '"; + std::wcout << ((current->serial_number != nullptr) ? current->serial_number : L"Unknown"); + std::cout << "' usage page: " << current->usage_page << " usage: " << current->usage << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + } + } + } + + current = current->next; + } + + // Free enumerated devices + hid_free_enumeration(devices); + + if (detected_devices.empty()) + return false; + + std::string path = ""; + unsigned short vendor_id = 0; + unsigned short product_id = 0; + + // Then we'll decide the choosing logic to apply in dependence of the device count and operating system + + for (const DetectedDevices::value_type& device : detected_devices) + { + if (device.second.size() == 1) + { +#ifdef __linux__ + hid_device* test_device = hid_open(device.first.first, device.first.second, nullptr); + if (test_device != nullptr) + { + hid_close(test_device); +#else + if (device.second.front().has_valid_usage()) + { +#endif // __linux__ + vendor_id = device.first.first; + product_id = device.first.second; + break; + } + } + else + { + bool found = false; +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + for (const DeviceData& data : device.second) + { +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << "Test device: " << std::hex << device.first.first << std::dec << "/" << std::hex << device.first.second << std::dec << " \"" << data.path << "\""; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +#ifdef __linux__ + hid_device* test_device = hid_open_path(data.path.c_str()); + if (test_device != nullptr) + { + path = data.path; + vendor_id = device.first.first; + product_id = device.first.second; + found = true; +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << "-> PASSED" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + hid_close(test_device); + break; + } +#else + if (data.has_valid_usage()) + { + path = data.path; + vendor_id = device.first.first; + product_id = device.first.second; + found = true; +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << "-> PASSED" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + break; + } +#endif // __linux__ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + else + std::cout << "-> NOT PASSED" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + } + + if (found) + break; + } + } + + if (path.empty()) + { + if ((vendor_id != 0) && (product_id != 0)) + { + // Open the 3Dconnexion device using vendor_id and product_id +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << std::endl << "Opening device: " << std::hex << vendor_id << std::dec << "/" << std::hex << product_id << std::dec << " using hid_open()" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_device = hid_open(vendor_id, product_id, nullptr); + } + else + return false; + } + else + { + // Open the 3Dconnexion device using the device path +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << std::endl << "Opening device: " << std::hex << vendor_id << std::dec << "/" << std::hex << product_id << std::dec << "\"" << path << "\" using hid_open_path()" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_device = hid_open_path(path.c_str()); + } + + if (m_device != nullptr) + { + std::vector manufacturer(1024, 0); + hid_get_manufacturer_string(m_device, manufacturer.data(), 1024); + m_device_str = boost::nowide::narrow(manufacturer.data()); + + std::vector product(1024, 0); + hid_get_product_string(m_device, product.data(), 1024); + m_device_str += "/" + boost::nowide::narrow(product.data()); + + BOOST_LOG_TRIVIAL(info) << "Connected device: " << m_device_str; + +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << std::endl << "Connected device:" << std::endl; + std::cout << "Manufacturer/product: " << m_device_str << std::endl; + std::cout << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")" << std::endl; + std::cout << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")" << std::endl; + std::cout << "Path................: '" << path << "'" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + + // get device parameters from the config, if present + double translation_speed = 1.0; + float rotation_speed = 1.0; + double translation_deadzone = State::DefaultTranslationDeadzone; + float rotation_deadzone = State::DefaultRotationDeadzone; + 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); + // clamp to valid values + m_state.set_translation_scale(State::DefaultTranslationScale * std::max(0.5, std::min(2.0, translation_speed))); + m_state.set_translation_deadzone(std::max(0.0, std::min(State::MaxTranslationDeadzone, translation_deadzone))); + m_state.set_rotation_scale(State::DefaultRotationScale * std::max(0.5f, std::min(2.0f, rotation_speed))); + m_state.set_rotation_deadzone(std::max(0.0f, std::min(State::MaxRotationDeadzone, rotation_deadzone))); + } +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + else + { + std::cout << std::endl << "Unable to connect to device:" << std::endl; + std::cout << "Manufacturer/product: " << m_device_str << std::endl; + std::cout << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")" << std::endl; + std::cout << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")" << std::endl; + std::cout << "Path................: '" << path << "'" << std::endl; + } +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + + return (m_device != nullptr); +} + +void Mouse3DController::disconnect_device() +{ + if (!is_device_connected()) + return; + + // Stop the secondary thread, if running + if (m_thread.joinable()) + m_thread.join(); + + // Store current device parameters into the config + 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()); + wxGetApp().app_config->save(); + + // Close the 3Dconnexion device + hid_close(m_device); + m_device = nullptr; + + BOOST_LOG_TRIVIAL(info) << "Disconnected device: " << m_device_str; + + m_device_str = ""; +} + +void Mouse3DController::start() +{ + if (!is_device_connected() || m_running) + return; + + m_thread = std::thread(&Mouse3DController::run, this); +} + +void Mouse3DController::run() +{ + m_running = true; + while (m_running) + { + collect_input(); + } +} + +void Mouse3DController::collect_input() +{ + DataPacket packet = { 0 }; + int res = hid_read_timeout(m_device, packet.data(), packet.size(), 100); + if (res < 0) + { + // An error occourred (device detached from pc ?) + stop(); + return; + } + + if (!wxGetApp().IsActive()) + return; + + std::lock_guard lock(m_mutex); + + bool updated = false; + + if (res == 7) + updated = handle_packet(packet); + else if (res == 13) + updated = handle_wireless_packet(packet); + else if ((res == 3) && (packet[0] == 3)) + // On Mac button packets can be 3 bytes long + updated = handle_packet(packet); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + else if (res > 0) + std::cout << "Got unknown data packet of length: " << res << ", code:" << (int)packet[0] << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + + if (updated) + // ask for an idle event to update 3D scene + wxWakeUpIdle(); +} + +bool Mouse3DController::handle_packet(const DataPacket& packet) +{ + switch (packet[0]) + { + case 1: // Translation + { + if (handle_packet_translation(packet)) + return true; + + break; + } + case 2: // Rotation + { + if (handle_packet_rotation(packet, 1)) + return true; + + break; + } + case 3: // Button + { + if (handle_packet_button(packet, packet.size() - 1)) + return true; + + break; + } + case 23: // Battery charge + { +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << m_device_str << " - battery level: " << (int)packet[1] << " percent" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + break; + } + default: + { +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << "Got unknown data packet of code: " << (int)packet[0] << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + break; + } + } + + return false; +} + +bool Mouse3DController::handle_wireless_packet(const DataPacket& packet) +{ + switch (packet[0]) + { + case 1: // Translation + Rotation + { + bool updated = handle_packet_translation(packet); + updated |= handle_packet_rotation(packet, 7); + + if (updated) + return true; + + break; + } + case 3: // Button + { + if (handle_packet_button(packet, 12)) + return true; + + break; + } + case 23: // Battery charge + { +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << m_device_str << " - battery level: " << (int)packet[1] << " percent" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + break; + } + default: + { +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << "Got unknown data packet of code: " << (int)packet[0] << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + break; + } + } + + return false; +} + +double convert_input(unsigned char first, unsigned char second, double deadzone) +{ + short value = first | second << 8; + double ret = (double)value / 350.0; + return (std::abs(ret) > deadzone) ? ret : 0.0; +} + +bool Mouse3DController::handle_packet_translation(const DataPacket& packet) +{ + double deadzone = m_state.get_translation_deadzone(); + Vec3d translation(-convert_input(packet[1], packet[2], deadzone), + convert_input(packet[3], packet[4], deadzone), + convert_input(packet[5], packet[6], deadzone)); + + if (!translation.isApprox(Vec3d::Zero())) + { + m_state.append_translation(translation); + return true; + } + + return false; +} + +bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigned int first_byte) +{ + double deadzone = (double)m_state.get_rotation_deadzone(); + 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)); + + if (!rotation.isApprox(Vec3f::Zero())) + { + m_state.append_rotation(rotation); + return true; + } + + return false; +} + +bool Mouse3DController::handle_packet_button(const DataPacket& packet, unsigned int packet_size) +{ + unsigned int data = 0; + for (unsigned int i = 1; i < packet_size; ++i) + { + data |= packet[i] << 8 * (i - 1); + } + + const std::bitset<32> data_bits{ data }; + for (size_t i = 0; i < data_bits.size(); ++i) + { + if (data_bits.test(i)) + { + m_state.append_button((unsigned int)i); + return true; + } + } + + return false; +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp new file mode 100644 index 000000000..cc03d4a24 --- /dev/null +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -0,0 +1,175 @@ +#ifndef slic3r_Mouse3DController_hpp_ +#define slic3r_Mouse3DController_hpp_ + +// Enabled debug output to console and extended imgui dialog +#define ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT 0 + +#include "libslic3r/Point.hpp" + +#include "hidapi.h" + +#include +#include +#include +#include +#include + +namespace Slic3r { +namespace GUI { + +struct Camera; + +class Mouse3DController +{ + class State + { + public: + static const double DefaultTranslationScale; + static const double MaxTranslationDeadzone; + static const double DefaultTranslationDeadzone; + static const float DefaultRotationScale; + static const float MaxRotationDeadzone; + static const float DefaultRotationDeadzone; + + private: + template + struct CustomParameters + { + Number scale; + Number deadzone; + + CustomParameters(Number scale, Number deadzone) : scale(scale), deadzone(deadzone) {} + }; + + template + struct InputQueue + { + size_t max_size; + std::queue queue; + + // The default value of 5 for max_size seems to work fine on all platforms + // The effects of changing this value can be tested by setting ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT to 1 + // and playing with the imgui dialog which shows by pressing CTRL+M + InputQueue() : max_size(5) {} + }; + + InputQueue m_translation; + InputQueue m_rotation; + std::queue m_buttons; + + bool m_buttons_enabled; + + CustomParameters m_translation_params; + CustomParameters m_rotation_params; + + // 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 + // by triggering unwanted zoom in/out of the scene + // The following variable is used to count the potential mouse wheel events triggered and is updated by: + // Mouse3DController::collect_input() through the call to the append_rotation() method + // GLCanvas3D::on_mouse_wheel() through the call to the process_mouse_wheel() method + // GLCanvas3D::on_idle() through the call to the apply() method + unsigned int m_mouse_wheel_counter; + +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + size_t m_translation_queue_max_size; + size_t m_rotation_queue_max_size; + size_t m_buttons_queue_max_size; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + + public: + State(); + + void append_translation(const Vec3d& translation); + void append_rotation(const Vec3f& rotation); + void append_button(unsigned int id); + + bool has_translation() const { return !m_translation.queue.empty(); } + bool has_rotation() const { return !m_rotation.queue.empty(); } + bool has_button() const { return !m_buttons.empty(); } + + bool process_mouse_wheel(); + + double get_translation_scale() const { return m_translation_params.scale; } + void set_translation_scale(double scale) { m_translation_params.scale = scale; } + + float get_rotation_scale() const { return m_rotation_params.scale; } + void set_rotation_scale(float scale) { m_rotation_params.scale = scale; } + + double get_translation_deadzone() const { return m_translation_params.deadzone; } + void set_translation_deadzone(double deadzone) { m_translation_params.deadzone = deadzone; } + + float get_rotation_deadzone() const { return m_rotation_params.deadzone; } + void set_rotation_deadzone(float deadzone) { m_rotation_params.deadzone = deadzone; } + +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + Vec3d get_translation() const { return has_translation() ? m_translation.queue.front() : Vec3d::Zero(); } + Vec3f get_rotation() const { return has_rotation() ? m_rotation.queue.front() : Vec3f::Zero(); } + unsigned int get_button() const { return has_button() ? m_buttons.front() : 0; } + + unsigned int get_translation_queue_size() const { return (unsigned int)m_translation.queue.size(); } + unsigned int get_rotation_queue_size() const { return (unsigned int)m_rotation.queue.size(); } + unsigned int get_buttons_queue_size() const { return (unsigned int)m_buttons.size(); } + + size_t get_translation_queue_max_size() const { return m_translation_queue_max_size; } + size_t get_rotation_queue_max_size() const { return m_rotation_queue_max_size; } + size_t get_buttons_queue_max_size() const { return m_buttons_queue_max_size; } +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + + size_t get_queues_max_size() const { return m_translation.max_size; } + void set_queues_max_size(size_t size); + + // return true if any change to the camera took place + bool apply(Camera& camera); + }; + + bool m_initialized; + mutable State m_state; + std::mutex m_mutex; + std::thread m_thread; + hid_device* m_device; + std::string m_device_str; + bool m_running; + bool m_settings_dialog; + std::chrono::time_point m_last_time; + +public: + Mouse3DController(); + + void init(); + void shutdown(); + + bool is_device_connected() const { return m_device != nullptr; } + bool is_running() const { return m_running; } + + bool process_mouse_wheel() { std::lock_guard lock(m_mutex); return m_state.process_mouse_wheel(); } + + bool apply(Camera& camera); + + bool is_settings_dialog_shown() const { return m_settings_dialog; } + void show_settings_dialog(bool show) { m_settings_dialog = show && is_running(); } + void render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const; + +private: + bool connect_device(); + void disconnect_device(); + void start(); + void stop() { m_running = false; } + + // secondary thread methods + void run(); + void collect_input(); + + typedef std::array DataPacket; + bool handle_packet(const DataPacket& packet); + bool handle_wireless_packet(const DataPacket& packet); + bool handle_packet_translation(const DataPacket& packet); + bool handle_packet_rotation(const DataPacket& packet, unsigned int first_byte); + bool handle_packet_button(const DataPacket& packet, unsigned int packet_size); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_Mouse3DController_hpp_ + diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 1041a71b1..4e874b1a1 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -133,7 +133,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n m_options_mode.push_back(option_set[0].opt.mode); // if we have a single option with no label, no sidetext just add it directly to sizer - if (option_set.size() == 1 && label_width == 0 && option_set.front().opt.full_width && + if (option_set.size() == 1 && label_width == 0 && option_set.front().opt.full_width && option_set.front().opt.label.empty() && option_set.front().opt.sidetext.size() == 0 && option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) { @@ -233,7 +233,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n add_undo_buttuns_to_sizer(sizer, field); if (is_window_field(field)) - sizer->Add(field->getWindow(), option.opt.full_width ? 1 : 0, //(option.opt.full_width ? wxEXPAND : 0) | + sizer->Add(field->getWindow(), option.opt.full_width ? 1 : 0, //(option.opt.full_width ? wxEXPAND : 0) | wxBOTTOM | wxTOP | (option.opt.full_width ? wxEXPAND : wxALIGN_CENTER_VERTICAL), (wxOSX || !staticbox) ? 0 : 2); if (is_sizer_field(field)) sizer->Add(field->getSizer(), 1, /*(*/option.opt.full_width ? wxEXPAND : /*0) |*/ wxALIGN_CENTER_VERTICAL, 0); diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index cc3d89b1f..6089e18f5 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -175,7 +175,7 @@ public: staticbox(title!=""), extra_column(extra_clmn) { if (staticbox) { stb = new wxStaticBox(_parent, wxID_ANY, title); - stb->SetBackgroundStyle(wxBG_STYLE_PAINT); + if (!wxOSX) stb->SetBackgroundStyle(wxBG_STYLE_PAINT); stb->SetFont(wxGetApp().bold_font()); } else stb = nullptr; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 94383624a..9d730e581 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -31,6 +32,9 @@ #include "libslic3r/Format/AMF.hpp" #include "libslic3r/Format/3mf.hpp" #include "libslic3r/GCode/PreviewData.hpp" +#if ENABLE_THUMBNAIL_GENERATOR +#include "libslic3r/GCode/ThumbnailData.hpp" +#endif // ENABLE_THUMBNAIL_GENERATOR #include "libslic3r/Model.hpp" #include "libslic3r/Polygon.hpp" #include "libslic3r/Print.hpp" @@ -61,6 +65,7 @@ #include "GUI_Preview.hpp" #include "3DBed.hpp" #include "Camera.hpp" +#include "Mouse3DController.hpp" #include "Tab.hpp" #include "PresetBundle.hpp" #include "BackgroundSlicingProcess.hpp" @@ -71,6 +76,7 @@ #include "../Utils/PrintHost.hpp" #include "../Utils/FixModelByWin10.hpp" #include "../Utils/UndoRedo.hpp" +#include "../Utils/Thread.hpp" #include // Needs to be last because reasons :-/ #include "WipeTowerDialog.hpp" @@ -81,6 +87,9 @@ using Slic3r::_3DScene; using Slic3r::Preset; using Slic3r::PrintHostJob; +#if ENABLE_THUMBNAIL_GENERATOR +static const std::pair THUMBNAIL_SIZE_3MF = { 256, 256 }; +#endif // ENABLE_THUMBNAIL_GENERATOR namespace Slic3r { namespace GUI { @@ -174,7 +183,7 @@ void ObjectInfo::msw_rescale() manifold_warning_icon->SetBitmap(create_scaled_bitmap(nullptr, "exclamation")); } -enum SlisedInfoIdx +enum SlicedInfoIdx { siFilament_m, siFilament_mm3, @@ -191,7 +200,7 @@ class SlicedInfo : public wxStaticBoxSizer { public: SlicedInfo(wxWindow *parent); - void SetTextAndShow(SlisedInfoIdx idx, const wxString& text, const wxString& new_label=""); + void SetTextAndShow(SlicedInfoIdx idx, const wxString& text, const wxString& new_label=""); private: std::vector> info_vec; @@ -221,7 +230,7 @@ SlicedInfo::SlicedInfo(wxWindow *parent) : init_info_label(_(L("Used Filament (mm³)"))); init_info_label(_(L("Used Filament (g)"))); init_info_label(_(L("Used Material (unit)"))); - init_info_label(_(L("Cost"))); + init_info_label(_(L("Cost (money)"))); init_info_label(_(L("Estimated printing time"))); init_info_label(_(L("Number of tool changes"))); @@ -229,7 +238,7 @@ SlicedInfo::SlicedInfo(wxWindow *parent) : this->Show(false); } -void SlicedInfo::SetTextAndShow(SlisedInfoIdx idx, const wxString& text, const wxString& new_label/*=""*/) +void SlicedInfo::SetTextAndShow(SlicedInfoIdx idx, const wxString& text, const wxString& new_label/*=""*/) { const bool show = text != "N/A"; if (show) @@ -251,11 +260,18 @@ wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(15 * auto selected_item = this->GetSelection(); auto marker = reinterpret_cast(this->GetClientData(selected_item)); - if (marker == LABEL_ITEM_MARKER || marker == LABEL_ITEM_CONFIG_WIZARD) { + if (marker >= LABEL_ITEM_MARKER && marker < LABEL_ITEM_MAX) { this->SetSelection(this->last_selected); evt.StopPropagation(); - if (marker == LABEL_ITEM_CONFIG_WIZARD) - wxTheApp->CallAfter([]() { Slic3r::GUI::config_wizard(Slic3r::GUI::ConfigWizard::RR_USER); }); + if (marker >= LABEL_ITEM_WIZARD_PRINTERS) { + ConfigWizard::StartPage sp = ConfigWizard::SP_WELCOME; + switch (marker) { + case LABEL_ITEM_WIZARD_PRINTERS: sp = ConfigWizard::SP_PRINTERS; break; + case LABEL_ITEM_WIZARD_FILAMENTS: sp = ConfigWizard::SP_FILAMENTS; break; + case LABEL_ITEM_WIZARD_MATERIALS: sp = ConfigWizard::SP_MATERIALS; break; + } + wxTheApp->CallAfter([sp]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, sp); }); + } } else if ( this->last_selected != selected_item || wxGetApp().get_tab(this->preset_type)->get_presets()->current_is_dirty() ) { this->last_selected = selected_item; @@ -336,7 +352,10 @@ wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(15 * // Call select_preset() only if there is new preset and not just modified if ( !boost::algorithm::ends_with(selected_preset, Preset::suffix_modified()) ) - tab->select_preset(selected_preset); + { + const std::string& preset_name = wxGetApp().preset_bundle->filaments.get_preset_name_by_alias(selected_preset); + tab->select_preset(/*selected_preset*/preset_name); + } } })); } @@ -521,12 +540,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) : const std::vector &init_matrix = (project_config.option("wiping_volumes_matrix"))->values; const std::vector &init_extruders = (project_config.option("wiping_volumes_extruders"))->values; - const DynamicPrintConfig* config = &wxGetApp().preset_bundle->printers.get_edited_preset().config; - std::vector extruder_colours = (config->option("extruder_colour"))->values; - const std::vector& filament_colours = (wxGetApp().plater()->get_plater_config()->option("filament_colour"))->values; - for (size_t i=0; i extruder_colours = wxGetApp().plater()->get_extruder_colors_from_plater_config(); WipingDialog dlg(parent, cast(init_matrix), cast(init_extruders), extruder_colours); @@ -1118,12 +1132,10 @@ void Sidebar::show_info_sizer() } } -void Sidebar::show_sliced_info_sizer(const bool show) +void Sidebar::update_sliced_info_sizer() { - wxWindowUpdateLocker freeze_guard(this); - - p->sliced_info->Show(show); - if (show) { + if (p->sliced_info->IsShown(size_t(0))) + { if (p->plater->printer_technology() == ptSLA) { const SLAPrintStatistics& ps = p->plater->sla_print().print_statistics(); @@ -1139,7 +1151,18 @@ void Sidebar::show_sliced_info_sizer(const bool show) wxString::Format("%.2f", (ps.objects_used_material + ps.support_used_material) / 1000); p->sliced_info->SetTextAndShow(siMateril_unit, info_text, new_label); - p->sliced_info->SetTextAndShow(siCost, "N/A"/*wxString::Format("%.2f", ps.total_cost)*/); + wxString str_total_cost = "N/A"; + + DynamicPrintConfig* cfg = wxGetApp().get_tab(Preset::TYPE_SLA_MATERIAL)->get_config(); + if (cfg->option("bottle_cost")->getFloat() > 0.0 && + cfg->option("bottle_volume")->getFloat() > 0.0) + { + double material_cost = cfg->option("bottle_cost")->getFloat() / + cfg->option("bottle_volume")->getFloat(); + str_total_cost = wxString::Format("%.2f", material_cost*(ps.objects_used_material + ps.support_used_material) / 1000); + } + p->sliced_info->SetTextAndShow(siCost, str_total_cost); + wxString t_est = std::isnan(ps.estimated_print_time) ? "N/A" : get_time_dhms(float(ps.estimated_print_time)); p->sliced_info->SetTextAndShow(siEstimatedTime, t_est, _(L("Estimated printing time")) + " :"); @@ -1207,12 +1230,21 @@ void Sidebar::show_sliced_info_sizer(const bool show) } // if there is a wipe tower, insert number of toolchanges info into the array: - p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, is_wipe_tower ? wxString::Format("%.d", p->plater->fff_print().wipe_tower_data().number_of_toolchanges) : "N/A"); + p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, is_wipe_tower ? wxString::Format("%.d", ps.total_toolchanges) : "N/A"); // Hide non-FFF sliced info parameters p->sliced_info->SetTextAndShow(siMateril_unit, "N/A"); } } +} + +void Sidebar::show_sliced_info_sizer(const bool show) +{ + wxWindowUpdateLocker freeze_guard(this); + + p->sliced_info->Show(show); + if (show) + update_sliced_info_sizer(); Layout(); p->scrolled->Refresh(); @@ -1368,6 +1400,7 @@ struct Plater::priv Sidebar *sidebar; Bed3D bed; Camera camera; + Mouse3DController mouse3d_controller; View3D* view3D; GLToolbar view_toolbar; Preview *preview; @@ -1426,7 +1459,7 @@ struct Plater::priv class Job : public wxEvtHandler { int m_range = 100; - std::future m_ftr; + boost::thread m_thread; priv * m_plater = nullptr; std::atomic m_running{false}, m_canceled{false}; bool m_finalized = false; @@ -1467,7 +1500,8 @@ struct Plater::priv // 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()) plater().update((unsigned int)UpdateParams::FORCE_FULL_SCREEN_REFRESH); + if (!was_canceled()) + plater().update(unsigned(UpdateParams::FORCE_FULL_SCREEN_REFRESH)); } public: @@ -1496,9 +1530,9 @@ struct Plater::priv } Job(const Job &) = delete; - Job(Job &&) = default; + Job(Job &&) = delete; Job &operator=(const Job &) = delete; - Job &operator=(Job &&) = default; + Job &operator=(Job &&) = delete; virtual void process() = 0; @@ -1522,7 +1556,7 @@ struct Plater::priv wxBeginBusyCursor(); try { // Execute the job - m_ftr = std::async(std::launch::async, &Job::run, this); + m_thread = create_thread([this] { this->run(); }); } catch (std::exception &) { update_status(status_range(), _(L("ERROR: not enough resources to " @@ -1538,14 +1572,13 @@ struct Plater::priv // 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) const + bool join(int timeout_ms = 0) { - if (!m_ftr.valid()) return true; + if (!m_thread.joinable()) return true; if (timeout_ms <= 0) - m_ftr.wait(); - else if (m_ftr.wait_for(std::chrono::milliseconds( - timeout_ms)) == std::future_status::timeout) + m_thread.join(); + else if (!m_thread.try_join_for(boost::chrono::milliseconds(timeout_ms))) return false; return true; @@ -1577,7 +1610,8 @@ struct Plater::priv size_t count = 0; // To know how much space to reserve for (auto obj : model.objects) count += obj->instances.size(); - m_selected.clear(), m_unselected.clear(); + m_selected.clear(); + m_unselected.clear(); m_selected.reserve(count + 1 /* for optional wti */); m_unselected.reserve(count + 1 /* for optional wti */); } @@ -1595,7 +1629,8 @@ struct Plater::priv ap.bed_idx = ap.translation.x() / bed_stride(); ap.setter = [obj, this](const ArrangePolygon &p) { if (p.is_arranged()) { - auto t = p.translation; t.x() += p.bed_idx * bed_stride(); + auto t = p.translation; + t.x() += p.bed_idx * bed_stride(); obj->apply_arrange_result(t, p.rotation); } }; @@ -1626,7 +1661,8 @@ struct Plater::priv obj_sel(model.objects.size(), nullptr); for (auto &s : plater().get_selection().get_content()) - if (s.first < int(obj_sel.size())) obj_sel[s.first] = &s.second; + if (s.first < int(obj_sel.size())) + obj_sel[size_t(s.first)] = &s.second; // Go through the objects and check if inside the selection for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) { @@ -1636,7 +1672,8 @@ struct Plater::priv std::vector inst_sel(mo->instances.size(), false); if (instlist) - for (auto inst_id : *instlist) inst_sel[inst_id] = true; + for (auto inst_id : *instlist) + inst_sel[size_t(inst_id)] = true; for (size_t i = 0; i < inst_sel.size(); ++i) { ArrangePolygon &&ap = get_arrange_poly(mo->instances[i]); @@ -1908,6 +1945,12 @@ struct Plater::priv bool can_fix_through_netfabb() const; bool can_set_instance_to_object() const; bool can_mirror() const; + bool can_reload_from_disk() const; + +#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); + void generate_thumbnails(ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background); +#endif // ENABLE_THUMBNAIL_GENERATOR void msw_rescale_object_menu(); @@ -1944,7 +1987,6 @@ private: * */ std::string m_last_fff_printer_profile_name; std::string m_last_sla_printer_profile_name; - bool m_update_objects_list_on_loading{ true }; }; const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)", std::regex::icase); @@ -1977,6 +2019,17 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) background_process.set_fff_print(&fff_print); background_process.set_sla_print(&sla_print); background_process.set_gcode_preview_data(&gcode_preview_data); +#if ENABLE_THUMBNAIL_GENERATOR + background_process.set_thumbnail_cb([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) + { + std::packaged_task task([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) { + generate_thumbnails(thumbnails, sizes, printable_only, parts_only, show_bed, transparent_background); + }); + std::future result = task.get_future(); + wxTheApp->CallAfter([&]() { task(thumbnails, sizes, printable_only, parts_only, show_bed, transparent_background); }); + result.wait(); + }); +#endif // ENABLE_THUMBNAIL_GENERATOR background_process.set_slicing_completed_event(EVT_SLICING_COMPLETED); background_process.set_finished_event(EVT_PROCESS_COMPLETED); // Default printer technology for default config. @@ -2046,6 +2099,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_RESETGIZMOS, [this](SimpleEvent&) { reset_all_gizmos(); }); view3D_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); }); view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); }); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + 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); }); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // 3DScene/Toolbar: view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this); @@ -2095,12 +2153,16 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // updates camera type from .ini file camera.set_type(get_config("use_perspective_camera")); + mouse3d_controller.init(); + // Initialize the Undo / Redo stack with a first snapshot. this->take_snapshot(_(L("New Project"))); } Plater::priv::~priv() { + mouse3d_controller.shutdown(); + if (config != nullptr) delete config; } @@ -2223,7 +2285,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ for (size_t i = 0; i < input_files.size(); i++) { const auto &path = input_files[i]; const auto filename = path.filename(); - const auto dlg_info = wxString::Format(_(L("Processing input file %s\n")), from_path(filename)); + const auto dlg_info = wxString::Format(_(L("Processing input file %s")), from_path(filename)) + "\n"; dlg.Update(100 * i / input_files.size(), dlg_info); const bool type_3mf = std::regex_match(path.string(), pattern_3mf); @@ -2264,6 +2326,8 @@ std::vector Plater::priv::load_files(const std::vector& input_ // and place the loaded config over the base. config += std::move(config_loaded); } + + this->model.custom_gcode_per_height = model.custom_gcode_per_height; } if (load_config) @@ -2295,17 +2359,17 @@ std::vector Plater::priv::load_files(const std::vector& input_ if (! is_project_file) { if (model.looks_like_multipart_object()) { wxMessageDialog msg_dlg(q, _(L( - "This file contains several objects positioned at multiple heights. " + "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?\n" - )), _(L("Multi-part object detected")), wxICON_WARNING | wxYES | wxNO); + "this file as a single object having multiple parts?")) + "\n", + _(L("Multi-part object detected")), wxICON_WARNING | wxYES | wxNO); if (msg_dlg.ShowModal() == wxID_YES) { model.convert_multipart_object(nozzle_dmrs->values.size()); } } } else if ((wxGetApp().get_mode() == comSimple) && (type_3mf || type_any_amf) && model_has_advanced_features(model)) { - wxMessageDialog msg_dlg(q, _(L("This file cannot be loaded in a simple mode. Do you want to switch to an advanced mode?\n")), + wxMessageDialog msg_dlg(q, _(L("This file cannot be loaded in a simple mode. Do you want to switch to an advanced mode?"))+"\n", _(L("Detected advanced data")), wxICON_WARNING | wxYES | wxNO); if (msg_dlg.ShowModal() == wxID_YES) { @@ -2350,8 +2414,8 @@ std::vector Plater::priv::load_files(const std::vector& input_ wxMessageDialog msg_dlg(q, _(L( "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" - )), _(L("Multi-part object detected")), wxICON_WARNING | wxYES | wxNO); + "these files to represent a single object having multiple parts?")) + "\n", + _(L("Multi-part object detected")), wxICON_WARNING | wxYES | wxNO); if (msg_dlg.ShowModal() == wxID_YES) { new_model->convert_multipart_object(nozzle_dmrs->values.size()); } @@ -2470,12 +2534,9 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode _(L("Object too large?"))); } - if (m_update_objects_list_on_loading) - { for (const size_t idx : obj_idxs) { wxGetApp().obj_list()->add_object_to_list(idx); } - } update(); object_list_changed(); @@ -2685,8 +2746,7 @@ void Plater::priv::reset() // The hiding of the slicing results, if shown, is not taken care by the background process, so we do it here this->sidebar->show_sliced_info_sizer(false); - auto& config = wxGetApp().preset_bundle->project_config; - config.option("colorprint_heights")->values.clear(); + model.custom_gcode_per_height.clear(); } void Plater::priv::mirror(Axis axis) @@ -2765,9 +2825,8 @@ void Plater::priv::ArrangeJob::process() { try { arrangement::arrange(m_selected, m_unselected, min_d, bedshape, [this, count](unsigned st) { - if (st > - 0) // will not finalize after last one - update_status(count - st, arrangestr); + if (st > 0) // will not finalize after last one + update_status(int(count - st), arrangestr); }, [this]() { return was_canceled(); }); } catch(std::exception& /*e*/) { @@ -2918,6 +2977,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool this->update_print_volume_state(); // Apply new config to the possibly running background task. bool was_running = this->background_process.running(); + this->background_process.set_force_update_print_regions(force_validation); Print::ApplyStatus invalidated = this->background_process.apply(this->q->model(), wxGetApp().preset_bundle->full_config()); // Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile. @@ -3099,89 +3159,115 @@ void Plater::priv::update_sla_scene() void Plater::priv::reload_from_disk() { - Plater::TakeSnapshot snapshot(q, _(L("Reload from Disk"))); + Plater::TakeSnapshot snapshot(q, _(L("Reload from disk"))); - auto& selection = get_selection(); - const auto obj_orig_idx = selection.get_object_idx(); - if (selection.is_wipe_tower() || obj_orig_idx == -1) { return; } - int instance_idx = selection.get_instance_idx(); + const Selection& selection = get_selection(); - auto *object_orig = model.objects[obj_orig_idx]; - std::vector input_paths(1, object_orig->input_file); - - // disable render to avoid to show intermediate states - view3D->get_canvas3d()->enable_render(false); - - // disable update of objects list while loading to avoid to show intermediate states - m_update_objects_list_on_loading = false; - - const auto new_idxs = load_files(input_paths, true, false); - if (new_idxs.empty()) - { - // error while loading - view3D->get_canvas3d()->enable_render(true); + if (selection.is_wipe_tower()) return; - } - for (const auto idx : new_idxs) + // struct to hold selected ModelVolumes by their indices + struct SelectedVolume { - ModelObject *object = model.objects[idx]; - object->config.apply(object_orig->config); + int object_idx; + int volume_idx; - object->clear_instances(); - for (const ModelInstance *instance : object_orig->instances) + // operators needed by std::algorithms + bool operator < (const SelectedVolume& other) const { return (object_idx < other.object_idx) || ((object_idx == other.object_idx) && (volume_idx < other.volume_idx)); } + bool operator == (const SelectedVolume& other) const { return (object_idx == other.object_idx) && (volume_idx == other.volume_idx); } + }; + std::vector selected_volumes; + + // collects selected ModelVolumes + const std::set& selected_volumes_idxs = selection.get_volume_idxs(); + for (unsigned int idx : selected_volumes_idxs) + { + const GLVolume* v = selection.get_volume(idx); + int v_idx = v->volume_idx(); + if (v_idx >= 0) { - object->add_instance(*instance); - } + int o_idx = v->object_idx(); + if ((0 <= o_idx) && (o_idx < (int)model.objects.size())) + selected_volumes.push_back({ o_idx, v_idx }); + } + } + std::sort(selected_volumes.begin(), selected_volumes.end()); + selected_volumes.erase(std::unique(selected_volumes.begin(), selected_volumes.end()), selected_volumes.end()); - for (const ModelVolume* v : object_orig->volumes) + // collects paths of files to load + std::vector input_paths; + for (const SelectedVolume& v : selected_volumes) + { + const ModelVolume* volume = model.objects[v.object_idx]->volumes[v.volume_idx]; + if (!volume->source.input_file.empty() && boost::filesystem::exists(volume->source.input_file)) + input_paths.push_back(volume->source.input_file); + } + std::sort(input_paths.begin(), input_paths.end()); + input_paths.erase(std::unique(input_paths.begin(), input_paths.end()), input_paths.end()); + + // load one file at a time + for (size_t i = 0; i < input_paths.size(); ++i) { - if (v->is_modifier()) - object->add_volume(*v); - } - - Vec3d offset = object_orig->origin_translation - object->origin_translation; - - if (object->volumes.size() == object_orig->volumes.size()) + const auto& path = input_paths[i].string(); + Model new_model; + try { - for (size_t i = 0; i < object->volumes.size(); i++) + new_model = Model::read_from_file(path, nullptr, true, false); + for (ModelObject* model_object : new_model.objects) { - object->volumes[i]->config.apply(object_orig->volumes[i]->config); - object->volumes[i]->translate(offset); + model_object->center_around_origin(); + model_object->ensure_on_bed(); + } + } + catch (std::exception&) + { + // error while loading + view3D->get_canvas3d()->enable_render(true); + return; + } + + // update the selected volumes whose source is the current file + for (const SelectedVolume& old_v : selected_volumes) + { + ModelObject* old_model_object = model.objects[old_v.object_idx]; + ModelVolume* old_volume = old_model_object->volumes[old_v.volume_idx]; + int new_volume_idx = old_volume->source.volume_idx; + int new_object_idx = old_volume->source.object_idx; + + if (old_volume->source.input_file == path) + { + if (new_object_idx < (int)new_model.objects.size()) + { + ModelObject* new_model_object = new_model.objects[new_object_idx]; + if (new_volume_idx < (int)new_model_object->volumes.size()) + { + old_model_object->add_volume(*new_model_object->volumes[new_volume_idx]); + ModelVolume* new_volume = old_model_object->volumes.back(); + new_volume->set_new_unique_id(); + new_volume->config.apply(old_volume->config); + new_volume->set_type(old_volume->type()); + new_volume->set_material_id(old_volume->material_id()); + new_volume->set_transformation(old_volume->get_transformation()); + new_volume->translate(new_volume->get_transformation().get_matrix(true) * (new_volume->source.mesh_offset - old_volume->source.mesh_offset)); + std::swap(old_model_object->volumes[old_v.volume_idx], old_model_object->volumes.back()); + old_model_object->delete_volume(old_model_object->volumes.size() - 1); } } - - // XXX: Restore more: layer_height_ranges, layer_height_profile (?) + } + } } - // re-enable update of objects list - m_update_objects_list_on_loading = true; + model.adjust_min_z(); - // puts the new objects into the list - for (const auto idx : new_idxs) - { - wxGetApp().obj_list()->add_object_to_list(idx); - } - - remove(obj_orig_idx); + // update 3D scene + update(); // new GLVolumes have been created at this point, so update their printable state for (size_t i = 0; i < model.objects.size(); ++i) { view3D->get_canvas3d()->update_instance_printable_state_for_object(i); } - - // re-enable render - view3D->get_canvas3d()->enable_render(true); - - // the previous call to remove() clears the selection - // select newly added objects - selection.clear(); - for (const auto idx : new_idxs) - { - selection.add_instance((unsigned int)idx - 1, instance_idx, false); } -} void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/) { @@ -3249,6 +3335,7 @@ void Plater::priv::set_current_panel(wxPanel* panel) } else view3D->reload_scene(true); } + // sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) view3D->set_as_dirty(); view_toolbar.select_item("3D"); @@ -3263,6 +3350,7 @@ void Plater::priv::set_current_panel(wxPanel* panel) this->q->reslice(); // keeps current gcode preview, if any preview->reload_print(true); + preview->set_canvas_as_dirty(); view_toolbar.select_item("Preview"); } @@ -3285,9 +3373,10 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt) //! combo->GetStringSelection().ToUTF8().data()); const std::string& selected_string = combo->GetString(combo->GetSelection()).ToUTF8().data(); + const std::string preset_name = wxGetApp().preset_bundle->get_preset_name_by_alias(preset_type, selected_string); if (preset_type == Preset::TYPE_FILAMENT) { - wxGetApp().preset_bundle->set_filament_preset(idx, selected_string); + wxGetApp().preset_bundle->set_filament_preset(idx, preset_name); } // TODO: ? @@ -3297,7 +3386,7 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt) } else { wxWindowUpdateLocker noUpdates(sidebar->presets_panel()); - wxGetApp().get_tab(preset_type)->select_preset(selected_string); + wxGetApp().get_tab(preset_type)->select_preset(preset_name); } // update plater with new config @@ -3308,7 +3397,6 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt) * and for SLA presets they should be deleted */ if (preset_type == Preset::TYPE_PRINTER) -// wxGetApp().obj_list()->update_settings_items(); wxGetApp().obj_list()->update_object_list_by_printer_technology(); } @@ -3323,7 +3411,7 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) this->statusbar()->set_progress(evt.status.percent); this->statusbar()->set_status_text(_(evt.status.text) + wxString::FromUTF8("…")); } - if (evt.status.flags & (PrintBase::SlicingStatus::RELOAD_SCENE || PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS)) { + if (evt.status.flags & (PrintBase::SlicingStatus::RELOAD_SCENE | PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS)) { switch (this->printer_technology) { case ptFFF: this->update_fff_scene(); @@ -3565,6 +3653,26 @@ bool Plater::priv::init_object_menu() return true; } +#if ENABLE_THUMBNAIL_GENERATOR +void Plater::priv::generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) +{ + view3D->get_canvas3d()->render_thumbnail(data, w, h, printable_only, parts_only, show_bed, transparent_background); +} + +void Plater::priv::generate_thumbnails(ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) +{ + thumbnails.clear(); + for (const Vec2d& size : sizes) + { + thumbnails.push_back(ThumbnailData()); + Point isize(size); // round to ints + generate_thumbnail(thumbnails.back(), isize.x(), isize.y(), printable_only, parts_only, show_bed, transparent_background); + if (!thumbnails.back().is_valid()) + thumbnails.pop_back(); + } +} +#endif // ENABLE_THUMBNAIL_GENERATOR + void Plater::priv::msw_rescale_object_menu() { for (MenuWithSeparators* menu : { &object_menu, &sla_object_menu, &part_menu, &default_menu }) @@ -3605,6 +3713,9 @@ 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); + 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); + sidebar->obj_list()->append_menu_item_export_stl(menu); } else { @@ -3631,8 +3742,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(); - append_menu_item(menu, wxID_ANY, _(L("Reload from Disk")), _(L("Reload the selected file from Disk")), - [this](wxCommandEvent&) { 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, [this]() { return can_reload_from_disk(); }, q); 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); }); @@ -3787,6 +3898,48 @@ bool Plater::priv::can_mirror() const return get_selection().is_from_single_instance(); } +bool Plater::priv::can_reload_from_disk() const +{ + // struct to hold selected ModelVolumes by their indices + struct SelectedVolume + { + int object_idx; + int volume_idx; + + // operators needed by std::algorithms + bool operator < (const SelectedVolume& other) const { return (object_idx < other.object_idx) || ((object_idx == other.object_idx) && (volume_idx < other.volume_idx)); } + bool operator == (const SelectedVolume& other) const { return (object_idx == other.object_idx) && (volume_idx == other.volume_idx); } + }; + std::vector selected_volumes; + + const Selection& selection = get_selection(); + + // collects selected ModelVolumes + const std::set& selected_volumes_idxs = selection.get_volume_idxs(); + for (unsigned int idx : selected_volumes_idxs) + { + const GLVolume* v = selection.get_volume(idx); + int v_idx = v->volume_idx(); + if (v_idx >= 0) + selected_volumes.push_back({ v->object_idx(), v_idx }); + } + std::sort(selected_volumes.begin(), selected_volumes.end()); + selected_volumes.erase(std::unique(selected_volumes.begin(), selected_volumes.end()), selected_volumes.end()); + + // collects paths of files to load + std::vector paths; + for (const SelectedVolume& v : selected_volumes) + { + const ModelVolume* volume = model.objects[v.object_idx]->volumes[v.volume_idx]; + if (!volume->source.input_file.empty() && boost::filesystem::exists(volume->source.input_file)) + paths.push_back(volume->source.input_file); + } + std::sort(paths.begin(), paths.end()); + paths.erase(std::unique(paths.begin(), paths.end()), paths.end()); + + return !paths.empty(); +} + void Plater::priv::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model) { bool new_shape = bed.set_shape(shape, custom_texture, custom_model); @@ -4030,6 +4183,7 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator // Disable layer editing before the Undo / Redo jump. if (!new_variable_layer_editing_active && view3D->is_layers_editing_enabled()) view3D->get_canvas3d()->force_main_toolbar_left_action(view3D->get_canvas3d()->get_main_toolbar_item_id("layersediting")); + // Make a copy of the snapshot, undo/redo could invalidate the iterator const UndoRedo::Snapshot snapshot_copy = *it_snapshot; // Do the jump in time. @@ -4289,11 +4443,10 @@ void Plater::increase_instances(size_t num) sidebar().obj_list()->increase_object_instances(obj_idx, was_one_instance ? num + 1 : num); - if (p->get_config("autocenter") == "1") { + if (p->get_config("autocenter") == "1") p->arrange(); - } else { + p->update(); - } p->get_selection().add_instance(obj_idx, (int)model_object->instances.size() - 1); @@ -4469,10 +4622,10 @@ void Plater::export_stl(bool extended, bool selection_only) bool is_left_handed = object->is_left_handed(); TriangleMesh pad_mesh; - bool has_pad_mesh = object->has_mesh(slaposBasePool); + bool has_pad_mesh = object->has_mesh(slaposPad); if (has_pad_mesh) { - pad_mesh = object->get_mesh(slaposBasePool); + pad_mesh = object->get_mesh(slaposPad); pad_mesh.transform(mesh_trafo_inv); } @@ -4559,7 +4712,13 @@ 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_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 // Success p->statusbar()->set_status_text(wxString::Format(_(L("3MF file exported to %s")), path)); p->set_project_filename(path); @@ -4570,6 +4729,11 @@ void Plater::export_3mf(const boost::filesystem::path& output_path) } } +void Plater::reload_from_disk() +{ + p->reload_from_disk(); +} + bool Plater::has_toolpaths_to_export() const { return p->preview->get_canvas3d()->has_toolpaths_to_export(); @@ -4626,7 +4790,7 @@ void Plater::reslice() p->show_action_buttons(true); // update type of preview - p->preview->update_view_type(); + p->preview->update_view_type(true); } void Plater::reslice_SLA_supports(const ModelObject &object, bool postpone_error_messages) @@ -4648,7 +4812,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 = slaposBasePool; + task.to_object_step = slaposPad; } this->p->background_process.set_task(task); // and let the background processing start. @@ -4718,7 +4882,7 @@ bool Plater::undo_redo_string_getter(const bool is_undo, int idx, const char** o const std::vector& ss_stack = p->undo_redo_stack().snapshots(); const int idx_in_ss_stack = p->get_active_snapshot_index() + (is_undo ? -(++idx) : idx); - if (0 < idx_in_ss_stack && idx_in_ss_stack < ss_stack.size() - 1) { + if (0 < idx_in_ss_stack && (size_t)idx_in_ss_stack < ss_stack.size() - 1) { *out_text = ss_stack[idx_in_ss_stack].name.c_str(); return true; } @@ -4731,7 +4895,7 @@ void Plater::undo_redo_topmost_string_getter(const bool is_undo, std::string& ou const std::vector& ss_stack = p->undo_redo_stack().snapshots(); const int idx_in_ss_stack = p->get_active_snapshot_index() + (is_undo ? -1 : 0); - if (0 < idx_in_ss_stack && idx_in_ss_stack < ss_stack.size() - 1) { + if (0 < idx_in_ss_stack && (size_t)idx_in_ss_stack < ss_stack.size() - 1) { out_text = ss_stack[idx_in_ss_stack].name; return; } @@ -4792,6 +4956,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config) filament_colors.push_back(filaments.find_preset(filament_preset, true)->config.opt_string("filament_colour", (unsigned)0)); p->config->option(opt_key)->values = filament_colors; + p->sidebar->obj_list()->update_extruder_colors(); continue; } } @@ -4817,6 +4982,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config) else if(opt_key == "extruder_colour") { update_scheduled = true; p->preview->set_number_extruders(p->config->option(opt_key)->values.size()); + p->sidebar->obj_list()->update_extruder_colors(); } else if(opt_key == "max_print_height") { update_scheduled = true; } @@ -4844,6 +5010,36 @@ void Plater::on_config_change(const DynamicPrintConfig &config) this->p->schedule_background_process(); } +void Plater::force_filament_colors_update() +{ + bool update_scheduled = false; + DynamicPrintConfig* config = p->config; + const std::vector filament_presets = wxGetApp().preset_bundle->filament_presets; + if (filament_presets.size() > 1 && + p->config->option("filament_colour")->values.size() == filament_presets.size()) + { + const PresetCollection& filaments = wxGetApp().preset_bundle->filaments; + std::vector filament_colors; + filament_colors.reserve(filament_presets.size()); + + for (const std::string& filament_preset : filament_presets) + filament_colors.push_back(filaments.find_preset(filament_preset, true)->config.opt_string("filament_colour", (unsigned)0)); + + if (config->option("filament_colour")->values != filament_colors) { + config->option("filament_colour")->values = filament_colors; + update_scheduled = true; + } + } + + if (update_scheduled) { + update(); + p->sidebar->obj_list()->update_extruder_colors(); + } + + if (p->main_frame->is_loaded()) + this->p->schedule_background_process(); +} + void Plater::on_activate() { #ifdef __linux__ @@ -4865,6 +5061,36 @@ const DynamicPrintConfig* Plater::get_plater_config() const return p->config; } +std::vector Plater::get_extruder_colors_from_plater_config() const +{ + const Slic3r::DynamicPrintConfig* config = &wxGetApp().preset_bundle->printers.get_edited_preset().config; + std::vector extruder_colors; + if (!config->has("extruder_colour")) // in case of a SLA print + return extruder_colors; + + extruder_colors = (config->option("extruder_colour"))->values; + if (!wxGetApp().plater()) + return extruder_colors; + + const std::vector& filament_colours = (p->config->option("filament_colour"))->values; + for (size_t i = 0; i < extruder_colors.size(); ++i) + if (extruder_colors[i] == "" && i < filament_colours.size()) + extruder_colors[i] = filament_colours[i]; + + return extruder_colors; +} + +std::vector Plater::get_colors_for_color_print() const +{ + std::vector colors = get_extruder_colors_from_plater_config(); + + for (const Model::CustomGCode& code : p->model.custom_gcode_per_height) + if (code.gcode == ColorChangeCode) + colors.push_back(code.color); + + return colors; +} + wxString Plater::get_project_filename(const wxString& extension) const { return p->get_project_filename(extension); @@ -5022,6 +5248,16 @@ const Camera& Plater::get_camera() const return p->camera; } +const Mouse3DController& Plater::get_mouse3d_controller() const +{ + return p->mouse3d_controller; +} + +Mouse3DController& Plater::get_mouse3d_controller() +{ + return p->mouse3d_controller; +} + bool Plater::can_delete() const { return p->can_delete(); } bool Plater::can_delete_all() const { return p->can_delete_all(); } bool Plater::can_increase_instances() const { return p->can_increase_instances(); } @@ -5067,6 +5303,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(); } +bool Plater::can_reload_from_disk() const { return p->can_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 26dcb5ac3..5c36dbf5e 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -41,6 +41,7 @@ class ObjectSettings; class ObjectLayers; class ObjectList; class GLCanvas3D; +class Mouse3DController; using t_optgroups = std::vector >; @@ -56,8 +57,12 @@ public: ScalableButton* edit_btn { nullptr }; enum LabelItemType { - LABEL_ITEM_MARKER = 0x4d, - LABEL_ITEM_CONFIG_WIZARD = 0x4e + LABEL_ITEM_MARKER = 0xffffff01, + LABEL_ITEM_WIZARD_PRINTERS, + LABEL_ITEM_WIZARD_FILAMENTS, + LABEL_ITEM_WIZARD_MATERIALS, + + LABEL_ITEM_MAX, }; void set_label_marker(int item, LabelItemType label_item_type = LABEL_ITEM_MARKER); @@ -108,6 +113,7 @@ public: void update_objects_list_extruder_column(size_t extruders_count); void show_info_sizer(); void show_sliced_info_sizer(const bool show); + void update_sliced_info_sizer(); void enable_buttons(bool enable); void set_btn_label(const ActionButtonType btn_type, const wxString& label) const; bool show_reslice(bool show) const; @@ -183,6 +189,7 @@ public: void export_stl(bool extended = false, bool selection_only = false); void export_amf(); void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path()); + void reload_from_disk(); bool has_toolpaths_to_export() const; void export_toolpaths_to_obj() const; void reslice(); @@ -211,9 +218,12 @@ public: void on_extruders_change(size_t extruders_count); void on_config_change(const DynamicPrintConfig &config); + void force_filament_colors_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; void update_object_menu(); @@ -247,10 +257,13 @@ public: bool can_copy_to_clipboard() const; bool can_undo() const; bool can_redo() const; + bool can_reload_from_disk() const; void msw_rescale(); const Camera& get_camera() const; + const Mouse3DController& get_mouse3d_controller() const; + Mouse3DController& get_mouse3d_controller(); // ROII wrapper for suppressing the Undo / Redo snapshot to be taken. class SuppressSnapshots diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index f650d5087..113d4d013 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -99,6 +99,9 @@ static const std::unordered_map pre_family_model_map { VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem::path &path, bool load_all) { static const std::string printer_model_key = "printer_model:"; + static const std::string filaments_section = "default_filaments"; + static const std::string materials_section = "default_sla_materials"; + const std::string id = path.stem().string(); if (! boost::filesystem::exists(path)) { @@ -107,6 +110,7 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem VendorProfile res(id); + // Helper to get compulsory fields auto get_or_throw = [&](const ptree &tree, const std::string &key) -> ptree::const_assoc_iterator { auto res = tree.find(key); @@ -116,6 +120,7 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem return res; }; + // Load the header const auto &vendor_section = get_or_throw(tree, "vendor")->second; res.name = get_or_throw(vendor_section, "name")->second.data(); auto full_name_node = vendor_section.find("full_name"); @@ -146,6 +151,7 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem res.config_version = std::move(*config_version); } + // Load URLs const auto config_update_url = vendor_section.find("config_update_url"); if (config_update_url != vendor_section.not_found()) { res.config_update_url = config_update_url->second.data(); @@ -160,6 +166,7 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem return res; } + // Load printer models for (auto §ion : tree) { if (boost::starts_with(section.first, printer_model_key)) { VendorProfile::PrinterModel model; @@ -200,6 +207,24 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem } } + // Load filaments and sla materials to be installed by default + const auto filaments = tree.find(filaments_section); + if (filaments != tree.not_found()) { + for (auto &pair : filaments->second) { + if (pair.second.data() == "1") { + res.default_filaments.insert(pair.first); + } + } + } + const auto materials = tree.find(materials_section); + if (materials != tree.not_found()) { + for (auto &pair : materials->second) { + if (pair.second.data() == "1") { + res.default_sla_materials.insert(pair.first); + } + } + } + return res; } @@ -238,27 +263,13 @@ std::string Preset::remove_suffix_modified(const std::string &name) name; } -void Preset::set_num_extruders(DynamicPrintConfig &config, unsigned int num_extruders) -{ - const auto &defaults = FullPrintConfig::defaults(); - for (const std::string &key : Preset::nozzle_options()) { - if (key == "default_filament_profile") - continue; - auto *opt = config.option(key, false); - assert(opt != nullptr); - //assert(opt->is_vector()); - if (opt != nullptr && opt->is_vector()) - static_cast(opt)->resize(num_extruders, defaults.option(key)); - } -} - // Update new extruder fields at the printer profile. void Preset::normalize(DynamicPrintConfig &config) { auto *nozzle_diameter = dynamic_cast(config.option("nozzle_diameter")); if (nozzle_diameter != nullptr) // Loaded the FFF Printer settings. Verify, that all extruder dependent values have enough values. - set_num_extruders(config, (unsigned int)nozzle_diameter->values.size()); + config.set_num_extruders((unsigned int)nozzle_diameter->values.size()); if (config.option("filament_diameter") != nullptr) { // This config contains single or multiple filament presets. // Ensure that the filament preset vector options contain the correct number of values. @@ -369,10 +380,17 @@ bool Preset::update_compatible(const Preset &active_printer, const DynamicPrintC void Preset::set_visible_from_appconfig(const AppConfig &app_config) { if (vendor == nullptr) { return; } + + if (type == TYPE_PRINTER) { const std::string &model = config.opt_string("printer_model"); const std::string &variant = config.opt_string("printer_variant"); if (model.empty() || variant.empty()) { return; } is_visible = app_config.get_variant(vendor->id, model, variant); + } else if (type == TYPE_FILAMENT) { + is_visible = app_config.has("filaments", name); + } else if (type == TYPE_SLA_MATERIAL) { + is_visible = app_config.has("sla_materials", name); +} } const std::vector& Preset::print_options() @@ -438,7 +456,9 @@ const std::vector& Preset::print_options() "hole_to_polyhole", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", - "single_extruder_multi_material_priming", "compatible_printers", "compatible_printers_condition", "inherits", + "single_extruder_multi_material_priming", + "wipe_tower_no_sparse_layers", + "compatible_printers", "compatible_printers_condition", "inherits", "infill_dense", "infill_dense_algo", "no_perimeter_unsupported_algo", "support_material_solid_first_layer" @@ -488,7 +508,7 @@ const std::vector& Preset::filament_options() "filament_retract_length", "filament_retract_lift", "filament_retract_lift_above", "filament_retract_lift_below", "filament_retract_speed", "filament_deretract_speed", "filament_retract_restart_extra", "filament_retract_before_travel", "filament_retract_layer_change", "filament_wipe", "filament_retract_before_wipe", // Profile compatibility - "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits" + "filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits" //merill adds , "filament_wipe_advanced_pigment" , "chamber_temperature" @@ -518,7 +538,7 @@ const std::vector& Preset::printer_options() "machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e", "machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e", "machine_min_extruding_rate", "machine_min_travel_rate", - "machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e", + "machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e", "fan_speedup_time" }; s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end()); @@ -530,17 +550,7 @@ const std::vector& Preset::printer_options() // of the nozzle_diameter vector. const std::vector& Preset::nozzle_options() { - // ConfigOptionFloats, ConfigOptionPercents, ConfigOptionBools, ConfigOptionStrings - static std::vector s_opts { - "nozzle_diameter", "min_layer_height", "max_layer_height", "extruder_offset", - "retract_length", "retract_lift", "retract_lift_above", "retract_lift_below", - "retract_lift_not_last_layer", - "retract_speed", "deretract_speed", - "retract_before_wipe", "retract_restart_extra", "retract_before_travel", "wipe", - "retract_layer_change", "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour", - "default_filament_profile" - }; - return s_opts; + return print_config_def.extruder_option_keys(); } const std::vector& Preset::sla_print_options() @@ -571,11 +581,13 @@ const std::vector& Preset::sla_print_options() "pad_enable", "pad_wall_thickness", "pad_wall_height", + "pad_brim_size", "pad_max_merge_distance", // "pad_edge_radius", "pad_wall_slope", "pad_object_gap", "pad_around_object", + "pad_around_object_everywhere", "pad_object_connector_stride", "pad_object_connector_width", "pad_object_connector_penetration", @@ -594,11 +606,17 @@ const std::vector& Preset::sla_material_options() static std::vector s_opts; if (s_opts.empty()) { s_opts = { + "material_type", "initial_layer_height", + "bottle_cost", + "bottle_volume", + "bottle_weight", + "material_density", "exposure_time", "initial_exposure_time", "material_correction", "material_notes", + "material_vendor", "default_sla_material_profile", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits" @@ -886,6 +904,8 @@ void PresetCollection::save_current_preset(const std::string &new_name) preset.is_external = false; // The newly saved preset will be activated -> make it visible. preset.is_visible = true; + // Just system presets have aliases + preset.alias.clear(); } // 2) Activate the saved preset. this->select_preset_by_name(new_name, true); @@ -914,6 +934,21 @@ bool PresetCollection::delete_current_preset() return true; } +bool PresetCollection::delete_preset(const std::string& name) +{ + auto it = this->find_preset_internal(name); + + const Preset& preset = *it; + if (preset.is_default) + return false; + if (!preset.is_external && !preset.is_system) { + // Erase the preset file. + boost::nowide::remove(preset.file.c_str()); + } + m_presets.erase(it); + return true; +} + void PresetCollection::load_bitmap_default(wxWindow *window, const std::string &file_name) { // XXX: See note in PresetBundle::load_compatible_bitmaps() @@ -964,6 +999,20 @@ const Preset* PresetCollection::get_preset_parent(const Preset& child) const return (preset == nullptr/* || preset->is_default */|| preset->is_external) ? nullptr : preset; } +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; + } + + return alias; +} + const std::string& PresetCollection::get_suffix_modified() { return g_suffix_modified; } @@ -1039,7 +1088,7 @@ void PresetCollection::update_platter_ui(GUI::PresetComboBox *ui) // Otherwise fill in the list from scratch. ui->Freeze(); ui->Clear(); - size_t selected_preset_item = 0; + size_t selected_preset_item = INT_MAX; // some value meaning that no one item is selected const Preset &selected_preset = this->get_selected_preset(); // Show wide icons if the currently selected preset is not compatible with the current printer, @@ -1058,6 +1107,7 @@ void PresetCollection::update_platter_ui(GUI::PresetComboBox *ui) std::map nonsys_presets; wxString selected = ""; + wxString tooltip = ""; if (!this->m_presets.front().is_visible) ui->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap)); for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++ i) { @@ -1086,27 +1136,36 @@ void PresetCollection::update_platter_ui(GUI::PresetComboBox *ui) bmp = m_bitmap_cache->insert(bitmap_key, bmps); } - 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); - if (i == m_idx_selected) - selected_preset_item = ui->GetCount() - 1; - } - else - { - nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), bmp/*preset.is_compatible*/); - if (i == m_idx_selected) - selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()); - } - if (i + 1 == m_num_default_presets) + 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); + if (i == m_idx_selected || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX) { + selected_preset_item = ui->GetCount() - 1; + tooltip = wxString::FromUTF8(preset.name.c_str()); + } + } + else + { + nonsys_presets.emplace(wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), bmp/*preset.is_compatible*/); + if (i == m_idx_selected) { + selected = wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? g_suffix_modified : "")).c_str()); + tooltip = wxString::FromUTF8(preset.name.c_str()); + } + } + if (i + 1 == m_num_default_presets) ui->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap)); - } + } if (!nonsys_presets.empty()) { ui->set_label_marker(ui->Append(PresetCollection::separator(L("User presets")), wxNullBitmap)); for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { ui->Append(it->first, *it->second); - if (it->first == selected) + if (it->first == selected || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX) selected_preset_item = ui->GetCount() - 1; } } @@ -1132,11 +1191,20 @@ void PresetCollection::update_platter_ui(GUI::PresetComboBox *ui) bmps.emplace_back(m_bitmap_add ? *m_bitmap_add : wxNullBitmap); 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_CONFIG_WIZARD); + 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); } + /* But, if selected_preset_item is still equal to INT_MAX, it means that + * there is no presets added to the list. + * So, select last combobox item ("Add/Remove preset") + */ + if (selected_preset_item == INT_MAX) + selected_preset_item = ui->GetCount() - 1; + ui->SetSelection(selected_preset_item); - ui->SetToolTip(ui->GetString(selected_preset_item)); + ui->SetToolTip(tooltip.IsEmpty() ? ui->GetString(selected_preset_item) : tooltip); ui->check_selection(); ui->Thaw(); @@ -1151,7 +1219,7 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati return 0; ui->Freeze(); ui->Clear(); - size_t selected_preset_item = 0; + size_t selected_preset_item = INT_MAX; // some value meaning that no one item is selected /* It's supposed that standard size of an icon is 16px*16px for 100% scaled display. * So set sizes for solid_colored(empty) icons used for preset @@ -1186,7 +1254,9 @@ 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); - if (i == m_idx_selected) + if (i == m_idx_selected || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX) selected_preset_item = ui->GetCount() - 1; } else @@ -1203,7 +1273,9 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati ui->Append(PresetCollection::separator(L("User presets")), wxNullBitmap); for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { ui->Append(it->first, *it->second); - if (it->first == selected) + if (it->first == selected || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX) selected_preset_item = ui->GetCount() - 1; } } @@ -1218,6 +1290,14 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati } ui->Append(PresetCollection::separator("Add a new printer"), *bmp); } + + /* But, if selected_preset_item is still equal to INT_MAX, it means that + * there is no presets added to the list. + * So, select last combobox item ("Add/Remove preset") + */ + if (selected_preset_item == INT_MAX) + selected_preset_item = ui->GetCount() - 1; + ui->SetSelection(selected_preset_item); ui->SetToolTip(ui->GetString(selected_preset_item)); ui->Thaw(); @@ -1378,7 +1458,7 @@ bool PresetCollection::select_preset_by_name_strict(const std::string &name) } // Merge one vendor's presets with the other vendor's presets, report duplicates. -std::vector PresetCollection::merge_presets(PresetCollection &&other, const std::set &new_vendors) +std::vector PresetCollection::merge_presets(PresetCollection &&other, const VendorMap &new_vendors) { std::vector duplicates; for (Preset &preset : other.m_presets) { @@ -1389,9 +1469,9 @@ std::vector PresetCollection::merge_presets(PresetCollection &&othe if (it == m_presets.end() || it->name != preset.name) { if (preset.vendor != nullptr) { // Re-assign a pointer to the vendor structure in the new PresetBundle. - auto it = new_vendors.find(*preset.vendor); + auto it = new_vendors.find(preset.vendor->id); assert(it != new_vendors.end()); - preset.vendor = &(*it); + preset.vendor = &it->second; } this->m_presets.emplace(it, std::move(preset)); } else diff --git a/src/slic3r/GUI/Preset.hpp b/src/slic3r/GUI/Preset.hpp index ebd707c3b..fc104c294 100644 --- a/src/slic3r/GUI/Preset.hpp +++ b/src/slic3r/GUI/Preset.hpp @@ -2,6 +2,8 @@ #define slic3r_Preset_hpp_ #include +#include +#include #include #include @@ -73,9 +75,14 @@ public: }; std::vector models; + std::set default_filaments; + std::set default_sla_materials; + VendorProfile() {} VendorProfile(std::string id) : id(std::move(id)) {} + // Load VendorProfile from an ini file. + // If `load_all` is false, only the header with basic info (name, version, URLs) is loaded. static VendorProfile from_ini(const boost::filesystem::path &path, bool load_all=true); static VendorProfile from_ini(const boost::property_tree::ptree &tree, const boost::filesystem::path &path, bool load_all=true); @@ -86,6 +93,12 @@ public: bool operator==(const VendorProfile &rhs) const { return this->id == rhs.id; } }; +// Note: it is imporant that map is used here rather than unordered_map, +// because we need iterators to not be invalidated, +// because Preset and the ConfigWizard hold pointers to VendorProfiles. +// XXX: maybe set is enough (cf. changes in Wizard) +typedef std::map VendorMap; + class Preset { public: @@ -137,6 +150,9 @@ public: // Configuration data, loaded from a file, or set from the defaults. DynamicPrintConfig config; + // Alias of the preset + std::string alias = ""; + void save(); // Return a label of this preset, consisting of a name and a "(modified)" suffix, if this preset is dirty. @@ -191,7 +207,7 @@ public: void set_visible_from_appconfig(const AppConfig &app_config); // Resize the extruder specific fields, initialize them with the content of the 1st extruder. - void set_num_extruders(unsigned int n) { set_num_extruders(this->config, n); } + void set_num_extruders(unsigned int n) { this->config.set_num_extruders(n); } // Sort lexicographically by a preset name. The preset name shall be unique across a single PresetCollection. bool operator<(const Preset &other) const { return this->name < other.name; } @@ -216,8 +232,6 @@ public: protected: friend class PresetCollection; friend class PresetBundle; - // Resize the extruder specific vectors () - static void set_num_extruders(DynamicPrintConfig &config, unsigned int n); static std::string remove_suffix_modified(const std::string &name); }; @@ -278,6 +292,9 @@ public: // Delete the current preset, activate the first visible preset. // returns true if the preset was deleted successfully. bool delete_current_preset(); + // Delete the current preset, activate the first visible preset. + // returns true if the preset was deleted successfully. + 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); @@ -314,6 +331,8 @@ public: Preset& get_edited_preset() { return m_edited_preset; } const Preset& get_edited_preset() const { return m_edited_preset; } + const std::string& get_preset_name_by_alias(const std::string& alias) const; + // used to update preset_choice from Tab const std::deque& get_presets() const { return m_presets; } int get_idx_selected() { return m_idx_selected; } @@ -432,7 +451,7 @@ protected: bool select_preset_by_name_strict(const std::string &name); // Merge one vendor's presets with the other vendor's presets, report duplicates. - std::vector merge_presets(PresetCollection &&other, const std::set &new_vendors); + std::vector merge_presets(PresetCollection &&other, const VendorMap &new_vendors); private: PresetCollection(); diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index 49ea42162..926ef7fa0 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -41,6 +42,8 @@ static std::vector s_project_options { "wiping_volumes_matrix" }; +const char *PresetBundle::PRUSA_BUNDLE = "PrusaResearch"; + PresetBundle::PresetBundle() : prints(Preset::TYPE_PRINT, Preset::print_options(), static_cast(FullPrintConfig::defaults())), filaments(Preset::TYPE_FILAMENT, Preset::filament_options(), static_cast(FullPrintConfig::defaults())), @@ -93,6 +96,7 @@ PresetBundle::PresetBundle() : preset.config.optptr("printer_vendor", true); preset.config.optptr("printer_model", true); preset.config.optptr("printer_variant", true); + preset.config.optptr("thumbnails", true); if (i == 0) { preset.config.optptr("default_print_profile", true); preset.config.option("default_filament_profile", true)->values = { "" }; @@ -194,7 +198,7 @@ void PresetBundle::setup_directories() } } -void PresetBundle::load_presets(const AppConfig &config, const std::string &preferred_model_id) +void PresetBundle::load_presets(AppConfig &config, const std::string &preferred_model_id) { // First load the vendor specific system presets. std::string errors_cummulative = this->load_system_presets(); @@ -279,7 +283,7 @@ std::string PresetBundle::load_system_presets() errors_cummulative += "\n"; } } - if (first) { + if (first) { // No config bundle loaded, reset. this->reset(false); } @@ -325,13 +329,85 @@ void PresetBundle::load_installed_printers(const AppConfig &config) } } +const std::string& PresetBundle::get_preset_name_by_alias( const Preset::Type& preset_type, const std::string& alias) const +{ + // there are not aliases for Printers profiles + if (preset_type == Preset::TYPE_PRINTER || preset_type == Preset::TYPE_INVALID) + return alias; + + const PresetCollection& presets = preset_type == Preset::TYPE_PRINT ? prints : + preset_type == Preset::TYPE_SLA_PRINT ? sla_prints : + preset_type == Preset::TYPE_FILAMENT ? filaments : + sla_materials; + + return presets.get_preset_name_by_alias(alias); +} + +void PresetBundle::load_installed_filaments(AppConfig &config) +{ + if (! config.has_section(AppConfig::SECTION_FILAMENTS)) { + std::unordered_set comp_filaments; + + for (const Preset &printer : printers) { + if (! printer.is_visible || printer.printer_technology() != ptFFF) { + continue; + } + + for (const Preset &filament : filaments) { + if (filament.is_compatible_with_printer(printer)) { + comp_filaments.insert(&filament); + } + } + } + + for (const auto &filament: comp_filaments) { + config.set(AppConfig::SECTION_FILAMENTS, filament->name, "1"); + } + } + + for (auto &preset : filaments) { + preset.set_visible_from_appconfig(config); + } +} + +void PresetBundle::load_installed_sla_materials(AppConfig &config) +{ + if (! config.has_section(AppConfig::SECTION_MATERIALS)) { + std::unordered_set comp_sla_materials; + + for (const Preset &printer : printers) { + if (! printer.is_visible || printer.printer_technology() != ptSLA) { + continue; + } + + for (const Preset &material : sla_materials) { + if (material.is_compatible_with_printer(printer)) { + comp_sla_materials.insert(&material); + } + } + } + + for (const auto &material: comp_sla_materials) { + config.set(AppConfig::SECTION_MATERIALS, material->name, "1"); + } + } + + for (auto &preset : sla_materials) { + preset.set_visible_from_appconfig(config); + } +} + // Load selections (current print, current filaments, current printer) from config.ini // This is done on application start up or after updates are applied. -void PresetBundle::load_selections(const AppConfig &config, const std::string &preferred_model_id) +void PresetBundle::load_selections(AppConfig &config, const std::string &preferred_model_id) { // Update visibility of presets based on application vendor / model / variant configuration. this->load_installed_printers(config); + // Update visibility of filament and sla material presets + this->load_installed_filaments(config); + this->load_installed_sla_materials(config); + // Parse the initial print / filament / printer profile names. std::string initial_print_profile_name = remove_ini_suffix(config.get("presets", "print")); std::string initial_sla_print_profile_name = remove_ini_suffix(config.get("presets", "sla_print")); @@ -1032,9 +1108,9 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla auto vp = VendorProfile::from_ini(tree, path); if (vp.num_variants() == 0) return 0; - vendor_profile = &(*this->vendors.insert(vp).first); + vendor_profile = &this->vendors.insert({vp.id, vp}).first->second; } - + if (flags & LOAD_CFGBUNDLE_VENDOR_ONLY) { return 0; } @@ -1060,6 +1136,7 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla PresetCollection *presets = nullptr; std::vector *loaded = nullptr; std::string preset_name; + std::string alias_name; if (boost::starts_with(section.first, "print:")) { presets = &this->prints; loaded = &loaded_prints; @@ -1068,6 +1145,12 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla presets = &this->filaments; loaded = &loaded_filaments; preset_name = section.first.substr(9); + + for (const auto& item : section.second) + if (boost::starts_with(item.first, "alias")) { + alias_name = item.second.data(); + break; + } } else if (boost::starts_with(section.first, "sla_print:")) { presets = &this->sla_prints; loaded = &loaded_sla_prints; @@ -1076,6 +1159,9 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla presets = &this->sla_materials; loaded = &loaded_sla_materials; preset_name = section.first.substr(13); + + int end_pos = preset_name.find_first_of("0."); + alias_name = preset_name.substr(0, end_pos-1); } else if (boost::starts_with(section.first, "printer:")) { presets = &this->printers; loaded = &loaded_printers; @@ -1221,6 +1307,14 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla loaded.is_system = true; loaded.vendor = vendor_profile; } + + // next step of an preset name aliasing + int end_pos = preset_name.find_first_of("@"); + if (end_pos != std::string::npos) + alias_name = preset_name.substr(0, end_pos - 1); + + loaded.alias = alias_name.empty() ? preset_name : alias_name; + ++ presets_loaded; } } @@ -1476,7 +1570,8 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, GUI::Pr // Fill in the list from scratch. ui->Freeze(); ui->Clear(); - size_t selected_preset_item = 0; + size_t selected_preset_item = INT_MAX; // some value meaning that no one item is selected + const Preset *selected_preset = this->filaments.find_preset(this->filament_presets[idx_extruder]); // Show wide icons if the currently selected preset is not compatible with the current printer, // and draw a red flag in front of the selected preset. @@ -1506,6 +1601,8 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, GUI::Pr // set a bitmap height to m_bitmapLock->GetHeight() const int icon_height = m_bitmapLock->GetHeight();//2 * icon_unit; //16 * scale_f + 0.5f; + wxString tooltip = ""; + for (int i = this->filaments().front().is_visible ? 0 : 1; i < int(this->filaments().size()); ++i) { const Preset &preset = this->filaments.preset(i); bool selected = this->filament_presets[idx_extruder] == preset.name; @@ -1546,18 +1643,25 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, GUI::Pr bitmap = m_bitmapCache->insert(bitmap_key, bmps); } - if (preset.is_default || preset.is_system) { - ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), + 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 ? Preset::suffix_modified() : "")).c_str()), (bitmap == 0) ? wxNullBitmap : *bitmap); - if (selected) + if (selected || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX ) { selected_preset_item = ui->GetCount() - 1; + tooltip = wxString::FromUTF8(preset.name.c_str()); + } } else { - nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), + nonsys_presets.emplace(wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), (bitmap == 0) ? &wxNullBitmap : bitmap); - if (selected) - selected_str = wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()); + if (selected) { + selected_str = wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()); + tooltip = wxString::FromUTF8(preset.name.c_str()); + } } if (preset.is_default) ui->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap)); @@ -1568,12 +1672,25 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, GUI::Pr ui->set_label_marker(ui->Append(PresetCollection::separator(L("User presets")), wxNullBitmap)); for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { ui->Append(it->first, *it->second); - if (it->first == selected_str) + if (it->first == selected_str || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX) { selected_preset_item = ui->GetCount() - 1; + } } } + + ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add/Remove filaments")), wxNullBitmap), 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. + * So, select last combobox item ("Add/Remove filaments") + */ + if (selected_preset_item == INT_MAX) + selected_preset_item = ui->GetCount() - 1; + ui->SetSelection(selected_preset_item); - ui->SetToolTip(ui->GetString(selected_preset_item)); + ui->SetToolTip(tooltip.IsEmpty() ? ui->GetString(selected_preset_item) : tooltip); ui->check_selection(); ui->Thaw(); diff --git a/src/slic3r/GUI/PresetBundle.hpp b/src/slic3r/GUI/PresetBundle.hpp index f351f66ac..5205042c5 100644 --- a/src/slic3r/GUI/PresetBundle.hpp +++ b/src/slic3r/GUI/PresetBundle.hpp @@ -4,7 +4,9 @@ #include "AppConfig.hpp" #include "Preset.hpp" +#include #include +#include #include class wxWindow; @@ -31,7 +33,7 @@ public: // Load ini files of all types (print, filament, printer) from Slic3r::data_dir() / presets. // Load selections (current print, current filaments, current printer) from config.ini // This is done just once on application start up. - void load_presets(const AppConfig &config, const std::string &preferred_model_id = ""); + void load_presets(AppConfig &config, const std::string &preferred_model_id = ""); // Export selections (current print, current filaments, current printer) into config.ini void export_selections(AppConfig &config); @@ -52,7 +54,8 @@ 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; + // std::set vendors; + VendorMap vendors; struct ObsoletePresets { std::vector prints; @@ -131,19 +134,27 @@ public: void load_default_preset_bitmaps(wxWindow *window); + // Set the is_visible flag for printer vendors, printer models and printer variants + // based on the user configuration. + // If the "vendor" section is missing, enable all models and variants of the particular vendor. + void load_installed_printers(const AppConfig &config); + + const std::string& get_preset_name_by_alias(const Preset::Type& preset_type, const std::string& alias) const; + + static const char *PRUSA_BUNDLE; private: std::string load_system_presets(); // Merge one vendor's presets with the other vendor's presets, report duplicates. std::vector merge_presets(PresetBundle &&other); - // Set the "enabled" flag for printer vendors, printer models and printer variants - // based on the user configuration. - // If the "vendor" section is missing, enable all models and variants of the particular vendor. - void load_installed_printers(const AppConfig &config); + // Set the is_visible flag for filaments and sla materials, + // apply defaults based on enabled printers when no filaments/materials are installed. + void load_installed_filaments(AppConfig &config); + void load_installed_sla_materials(AppConfig &config); // Load selections (current print, current filaments, current printer) from config.ini // This is done just once on application start up. - void load_selections(const AppConfig &config, const std::string &preferred_model_id = ""); + void load_selections(AppConfig &config, const std::string &preferred_model_id = ""); // Load print, filament & printer presets from a config. If it is an external config, then the name is extracted from the external path. // and the external config is just referenced, not stored into user profile directory. diff --git a/src/slic3r/GUI/PresetHints.cpp b/src/slic3r/GUI/PresetHints.cpp index 6e48a4d70..6eb3e0f2f 100644 --- a/src/slic3r/GUI/PresetHints.cpp +++ b/src/slic3r/GUI/PresetHints.cpp @@ -32,11 +32,11 @@ std::string PresetHints::cooling_description(const Preset &preset) % slowdown_below_layer_time % max_fan_speed % slowdown_below_layer_time % min_print_speed).str(); if (fan_below_layer_time > slowdown_below_layer_time) { - out += (boost::format(_utf8(L("\nIf estimated layer time is greater, but still below ~%1%s, " + out += "\n" + (boost::format(_utf8(L("If estimated layer time is greater, but still below ~%1%s, " "fan will run at a proportionally decreasing speed between %2%%% and %3%%%."))) % fan_below_layer_time % max_fan_speed % min_fan_speed).str(); } - out += _utf8(L("\nDuring the other layers, fan")) + " "; + out += "\n" + _utf8(L("During the other layers, fan")) + " "; } else { out = _utf8(L("Fan")) + " "; } diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 40fbbbac6..13be48289 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -410,7 +410,7 @@ void Selection::set_deserialized(EMode mode, const std::vectorselected = false; m_list.clear(); @@ -472,7 +472,7 @@ void Selection::volumes_changed(const std::vector &map_volume_old_to_new for (unsigned int idx : m_list) if (map_volume_old_to_new[idx] != size_t(-1)) { unsigned int new_idx = (unsigned int)map_volume_old_to_new[idx]; - assert((*m_volumes)[new_idx]->selected); + (*m_volumes)[new_idx]->selected = true; list_new.insert(new_idx); } m_list = std::move(list_new); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 9cb02a2d4..0c6396307 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -226,12 +226,12 @@ void Tab::create_preset_tab() m_treectrl->Bind(wxEVT_TREE_SEL_CHANGED, &Tab::OnTreeSelChange, this); m_treectrl->Bind(wxEVT_KEY_DOWN, &Tab::OnKeyDown, this); - m_presets_choice->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent e) { - //! Because of The MSW and GTK version of wxBitmapComboBox derived from wxComboBox, - //! but the OSX version derived from wxOwnerDrawnCombo, instead of: - //! select_preset(m_presets_choice->GetStringSelection().ToUTF8().data()); - //! we doing next: - int selected_item = m_presets_choice->GetSelection(); + m_presets_choice->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent e) { + //! Because of The MSW and GTK version of wxBitmapComboBox derived from wxComboBox, + //! but the OSX version derived from wxOwnerDrawnCombo, instead of: + //! select_preset(m_presets_choice->GetStringSelection().ToUTF8().data()); + //! we doing next: + int selected_item = m_presets_choice->GetSelection(); if (m_selected_preset_item == size_t(selected_item) && !m_presets->current_is_dirty()) return; if (selected_item >= 0) { @@ -239,11 +239,11 @@ void Tab::create_preset_tab() if (selected_string.find(PresetCollection::separator_head()) == 0 /*selected_string == "------- System presets -------" || selected_string == "------- User presets -------"*/) { - m_presets_choice->SetSelection(m_selected_preset_item); - if (wxString::FromUTF8(selected_string.c_str()) == PresetCollection::separator(L("Add a new printer"))) - wxTheApp->CallAfter([]() { Slic3r::GUI::config_wizard(Slic3r::GUI::ConfigWizard::RR_USER); }); - return; - } + m_presets_choice->SetSelection(m_selected_preset_item); + if (wxString::FromUTF8(selected_string.c_str()) == PresetCollection::separator(L("Add a new printer"))) + wxTheApp->CallAfter([]() { wxGetApp().run_wizard(ConfigWizard::RR_USER); }); + return; + } m_selected_preset_item = selected_item; select_preset(selected_string); } @@ -998,7 +998,12 @@ void Tab::update_preset_description_line() default: break; } } - } + else + { + description_line += "\n\n\t" + _(L("full profile name")) + ": \n\t\t" + parent->name; + description_line += "\n\t" + _(L("symbolic profile name")) + ": \n\t\t" + parent->alias; + } + } m_parent_preset_description_line->SetText(description_line, false); } @@ -1292,6 +1297,7 @@ void TabPrint::build() optgroup->append_single_option_line("wipe_tower_width"); optgroup->append_single_option_line("wipe_tower_rotation_angle"); optgroup->append_single_option_line("wipe_tower_bridging"); + optgroup->append_single_option_line("wipe_tower_no_sparse_layers"); optgroup->append_single_option_line("single_extruder_multi_material_priming"); optgroup = page->new_optgroup(_(L("Advanced"))); @@ -1923,7 +1929,10 @@ void TabPrinter::build_fff() optgroup->append_single_option_line("single_extruder_multi_material"); optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value) { - size_t extruders_count = boost::any_cast(optgroup->get_value("extruders_count")); + // optgroup->get_value() return int for def.type == coInt, + // Thus, there should be boost::any_cast ! + // Otherwise, boost::any_cast causes an "unhandled unknown exception" + size_t extruders_count = size_t(boost::any_cast(optgroup->get_value("extruders_count"))); wxTheApp->CallAfter([this, opt_key, value, extruders_count]() { if (opt_key == "extruders_count" || opt_key == "single_extruder_multi_material") { extruders_count_changed(extruders_count); @@ -3113,7 +3122,8 @@ void Tab::save_preset(std::string name /*= ""*/) if (name.empty()) { const Preset &preset = m_presets->get_selected_preset(); auto default_name = preset.is_default ? "Untitled" : - preset.is_system ? (boost::format(_utf8(L("%1% - Copy"))) % preset.name).str() : +// preset.is_system ? (boost::format(_utf8(L("%1% - Copy"))) % preset.name).str() : + preset.is_system ? (boost::format(_CTX_utf8(L_CONTEXT("%1% - Copy", "PresetName"), "PresetName")) % preset.name).str() : preset.name; bool have_extention = boost::iends_with(default_name, ".ini"); @@ -3148,7 +3158,19 @@ void Tab::save_preset(std::string name /*= ""*/) show_error(this, _(L("Cannot overwrite an external profile."))); return; } - } + if (existing/* && name != preset.name*/) + { + wxString msg_text = GUI::from_u8((boost::format(_utf8(L("Preset with name \"%1%\" already exist."))) % name).str()); + msg_text += "\n" + _(L("Replace?")); + wxMessageDialog dialog(nullptr, msg_text, _(L("Warning")), wxICON_WARNING | wxYES | wxNO); + + if (dialog.ShowModal() == wxID_NO) + return; + + // Remove the preset from the list. + m_presets->delete_preset(name); + } + } // Save the preset into Slic3r::data_dir / presets / section_name / preset_name.ini m_presets->save_current_preset(name); @@ -3164,6 +3186,12 @@ void Tab::save_preset(std::string name /*= ""*/) if (m_type == Preset::TYPE_PRINTER) static_cast(this)->m_initial_extruders_count = static_cast(this)->m_extruders_count; update_changed_ui(); + + /* If filament preset is saved for multi-material printer preset, + * there are cases when filament comboboxs are updated for old (non-modified) colors, + * but in full_config a filament_colors option aren't.*/ + if (m_type == Preset::TYPE_FILAMENT && wxGetApp().extruders_edited_cnt() > 1) + wxGetApp().plater()->force_filament_colors_update(); } // Called for a currently selected preset. @@ -3530,8 +3558,41 @@ void TabSLAMaterial::build() auto page = add_options_page(_(L("Material")), "resin"); - auto optgroup = page->new_optgroup(_(L("Layers"))); -// optgroup->append_single_option_line("layer_height"); + auto optgroup = page->new_optgroup(_(L("Material"))); + optgroup->append_single_option_line("bottle_cost"); + optgroup->append_single_option_line("bottle_volume"); + optgroup->append_single_option_line("bottle_weight"); + optgroup->append_single_option_line("material_density"); + + optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value) + { + DynamicPrintConfig new_conf = *m_config; + + if (opt_key == "bottle_volume") { + double new_bottle_weight = boost::any_cast(value)/(new_conf.option("material_density")->getFloat() * 1000); + new_conf.set_key_value("bottle_weight", new ConfigOptionFloat(new_bottle_weight)); + } + if (opt_key == "bottle_weight") { + double new_bottle_volume = boost::any_cast(value)*(new_conf.option("material_density")->getFloat() * 1000); + new_conf.set_key_value("bottle_volume", new ConfigOptionFloat(new_bottle_volume)); + } + if (opt_key == "material_density") { + double new_bottle_volume = new_conf.option("bottle_weight")->getFloat() * boost::any_cast(value) * 1000; + new_conf.set_key_value("bottle_volume", new ConfigOptionFloat(new_bottle_volume)); + } + + load_config(new_conf); + + update_dirty(); + on_value_change(opt_key, value); + + if (opt_key == "bottle_volume" || opt_key == "bottle_cost") { + wxGetApp().sidebar().update_sliced_info_sizer(); + wxGetApp().sidebar().Layout(); + } + }; + + optgroup = page->new_optgroup(_(L("Layers"))); optgroup->append_single_option_line("initial_layer_height"); optgroup = page->new_optgroup(_(L("Exposure"))); @@ -3662,12 +3723,14 @@ void TabSLAPrint::build() optgroup->append_single_option_line("pad_enable"); optgroup->append_single_option_line("pad_wall_thickness"); optgroup->append_single_option_line("pad_wall_height"); + optgroup->append_single_option_line("pad_brim_size"); optgroup->append_single_option_line("pad_max_merge_distance"); // TODO: Disabling this parameter for the beta release // optgroup->append_single_option_line("pad_edge_radius"); optgroup->append_single_option_line("pad_wall_slope"); optgroup->append_single_option_line("pad_around_object"); + optgroup->append_single_option_line("pad_around_object_everywhere"); optgroup->append_single_option_line("pad_object_gap"); optgroup->append_single_option_line("pad_object_connector_stride"); optgroup->append_single_option_line("pad_object_connector_width"); diff --git a/src/slic3r/GUI/UpdateDialogs.cpp b/src/slic3r/GUI/UpdateDialogs.cpp index 4ab6edf48..360fa1fdd 100644 --- a/src/slic3r/GUI/UpdateDialogs.cpp +++ b/src/slic3r/GUI/UpdateDialogs.cpp @@ -156,8 +156,8 @@ MsgDataIncompatible::MsgDataIncompatible(const std::unordered_mapWrap(CONTENT_WIDTH * wxGetApp().em_unit()); content_sizer->Add(text); diff --git a/src/slic3r/GUI/WipeTowerDialog.cpp b/src/slic3r/GUI/WipeTowerDialog.cpp index 460683f77..539422545 100644 --- a/src/slic3r/GUI/WipeTowerDialog.cpp +++ b/src/slic3r/GUI/WipeTowerDialog.cpp @@ -371,7 +371,7 @@ void WipingPanel::toggle_advanced(bool user_action) { else m_advanced = !advanced_matches_simple(); // if called from constructor, show what is appropriate - (m_advanced ? m_page_advanced : m_page_simple)->Show(); + (m_advanced ? m_page_advanced : m_page_simple)->Show(); (!m_advanced ? m_page_advanced : m_page_simple)->Hide(); m_widget_button->SetLabel(m_advanced ? _(L("Show simplified settings")) : _(L("Show advanced settings"))); diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 04cdcae8c..9c0a27e56 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -7,9 +7,11 @@ #include "libslic3r/Model.hpp" #include +#include #include #include #include +#include #include @@ -21,9 +23,12 @@ #include "libslic3r/Config.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); @@ -403,6 +408,23 @@ int em_unit(wxWindow* win) return Slic3r::GUI::wxGetApp().em_unit(); } +static float get_svg_scale_factor(wxWindow *win) +{ +#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; +#else + return 1.0f; +#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*/) @@ -446,6 +468,100 @@ 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*/) +{ + // Create the bitmap with color bars. + std::vector bmps; + std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); + + if (colors.empty()) + return bmps; + + unsigned char rgb[3]; + + /* It's supposed that standard size of an icon is 36px*16px for 100% scaled display. + * So set sizes for solid_colored icons used for filament preset + * and scale them in respect to em_unit value + */ + const double em = Slic3r::GUI::wxGetApp().em_unit(); + const int icon_width = lround((thin_icon ? 1 : 3.2) * em); + const int icon_height = lround(1.6 * em); + + for (const std::string& color : colors) + { + 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); + 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)); + } + bmps.emplace_back(bitmap); + } + + return bmps; +} + + +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/* = ""*/, + wxPoint pos/* = wxDefaultPosition*/, + wxSize size/* = wxDefaultSize*/, + bool use_thin_icon/* = false*/) +{ + std::vector icons = get_extruder_color_icons(use_thin_icon); + + if (!*ctrl) + *ctrl = new wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, pos, size, + 0, nullptr, wxCB_READONLY); + else + { + (*ctrl)->SetPosition(pos); + (*ctrl)->SetMinSize(size); + (*ctrl)->SetSize(size); + (*ctrl)->Clear(); + } + if (first_item.empty()) + (*ctrl)->Hide(); // to avoid unwanted rendering before layout (ExtruderSequenceDialog) + + if (icons.empty() && !first_item.empty()) { + (*ctrl)->Append(_(first_item), wxNullBitmap); + return; + } + + // For ObjectList we use short extruder name (just a number) + const bool use_full_item_name = dynamic_cast(parent) == nullptr; + + int i = 0; + wxString str = _(L("Extruder")); + for (wxBitmap* bmp : icons) { + if (i == 0) { + if (!first_item.empty()) + (*ctrl)->Append(_(first_item), *bmp); + ++i; + } + + (*ctrl)->Append(use_full_item_name ? wxString::Format("%s %d", str, i) : std::to_string(i), *bmp); + ++i; + } + (*ctrl)->SetSelection(0); +} + + // ***************************************************************************** // ---------------------------------------------------------------------------- // ObjectDataViewModelNode @@ -478,7 +594,7 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent m_idx = parent->GetChildCount(); m_name = wxString::Format(_(L("Instance %d")), m_idx + 1); - set_action_icon(); + set_action_and_extruder_icons(); } else if (type == itLayerRoot) { @@ -513,7 +629,7 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent m_name = _(L("Range")) + label_range + "(" + _(L("mm")) + ")"; m_bmp = create_scaled_bitmap(nullptr, LAYER_ICON); // FIXME: pass window ptr - set_action_icon(); + set_action_and_extruder_icons(); init_container(); } @@ -526,11 +642,19 @@ bool ObjectDataViewModelNode::valid() } #endif /* NDEBUG */ -void ObjectDataViewModelNode::set_action_icon() +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) @@ -540,7 +664,6 @@ void ObjectDataViewModelNode::set_printable_icon(PrintIndicator printable) create_scaled_bitmap(nullptr, m_printable == piPrintable ? "eye_open.png" : "eye_closed.png"); } -Slic3r::GUI::BitmapCache *m_bitmap_cache = nullptr; void ObjectDataViewModelNode::update_settings_digest_bitmaps() { m_bmp = m_empty_bmp; @@ -606,8 +729,10 @@ bool ObjectDataViewModelNode::SetValue(const wxVariant& variant, unsigned col) m_name = data.GetText(); return true; } case colExtruder: { - const wxString & val = variant.GetString(); - m_extruder = val == "0" ? _(L("default")) : val; + 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; @@ -670,7 +795,7 @@ wxDataViewItem ObjectDataViewModel::Add(const wxString &name, if (has_errors) root->m_bmp = *m_warning_bmp; - m_objects.push_back(root); + m_objects.push_back(root); // notify control wxDataViewItem child((void*)root); wxDataViewItem parent((void*)NULL); @@ -721,7 +846,7 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent root->SetBitmap(*m_warning_bmp); // notify control - const wxDataViewItem child((void*)node); + const wxDataViewItem child((void*)node); ItemAdded(parent_item, child); root->m_volumes_cnt++; @@ -964,7 +1089,7 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item) node_parent->GetChildren().Remove(node); if (id > 0) { - if(id == node_parent->GetChildCount()) id--; + if (size_t(id) == node_parent->GetChildCount()) id--; ret_item = wxDataViewItem(node_parent->GetChildren().Item(id)); } @@ -1380,6 +1505,51 @@ t_layer_height_range ObjectDataViewModel::GetLayerRangeByItem(const wxDataViewIt 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()); @@ -1476,6 +1646,24 @@ wxBitmap& ObjectDataViewModel::GetBitmap(const wxDataViewItem &item) const 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()); @@ -1490,7 +1678,7 @@ void ObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem &ite variant << DataViewBitmapText(node->m_name, node->m_bmp); break; case colExtruder: - variant = node->m_extruder; + variant << DataViewBitmapText(node->m_extruder, node->m_extruder_bmp); break; case colEditing: variant << node->m_action_icon; @@ -1516,6 +1704,22 @@ bool ObjectDataViewModel::SetValue(const wxVariant &variant, const int item_idx, 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) @@ -1841,7 +2045,7 @@ void ObjectDataViewModel::DeleteWarningIcon(const wxDataViewItem& item, const bo } //----------------------------------------------------------------------------- -// PrusaDataViewBitmapText +// DataViewBitmapText //----------------------------------------------------------------------------- wxIMPLEMENT_DYNAMIC_CLASS(DataViewBitmapText, wxObject) @@ -1967,6 +2171,109 @@ bool BitmapTextRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value 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 // ---------------------------------------------------------------------------- @@ -1992,26 +2299,31 @@ DoubleSlider::DoubleSlider( wxWindow *parent, 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, "up_half_circle.png", 16, true)); - m_bmp_thumb_lower = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "left_half_circle.png" ) : ScalableBitmap(this, "down_half_circle.png", 16, true)); - m_thumb_size = m_bmp_thumb_lower.bmp().GetSize(); + const float scale_factor = get_svg_scale_factor(this); - m_bmp_add_tick_on = ScalableBitmap(this, "colorchange_add_on.png"); - m_bmp_add_tick_off = ScalableBitmap(this, "colorchange_add_off.png"); - m_bmp_del_tick_on = ScalableBitmap(this, "colorchange_delete_on.png"); - m_bmp_del_tick_off = ScalableBitmap(this, "colorchange_delete_off.png"); - m_tick_icon_dim = m_bmp_add_tick_on.bmp().GetSize().x; + 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_one_layer_lock_on = ScalableBitmap(this, "one_layer_lock_on.png"); - m_bmp_one_layer_lock_off = ScalableBitmap(this, "one_layer_lock_off.png"); - m_bmp_one_layer_unlock_on = ScalableBitmap(this, "one_layer_unlock_on.png"); - m_bmp_one_layer_unlock_off = ScalableBitmap(this, "one_layer_unlock_off.png"); - m_lock_icon_dim = m_bmp_one_layer_lock_on.bmp().GetSize().x; + 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 = m_bmp_revert.bmp().GetSize().x; + 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_pause_print_msg = _utf8(L("Place bearings in slots and resume")); // slider events Bind(wxEVT_PAINT, &DoubleSlider::OnPaint, this); @@ -2030,7 +2342,7 @@ DoubleSlider::DoubleSlider( wxWindow *parent, // control's view variables SLIDER_MARGIN = 4 + Slic3r::GUI::wxGetApp().em_unit(); - DARK_ORANGE_PEN = wxPen(wxColour(253, 84, 2)); + DARK_ORANGE_PEN = wxPen(wxColour(237, 107, 33)); ORANGE_PEN = wxPen(wxColour(253, 126, 66)); LIGHT_ORANGE_PEN = wxPen(wxColour(254, 177, 139)); @@ -2068,6 +2380,8 @@ void DoubleSlider::msw_rescale() 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(); @@ -2215,41 +2529,45 @@ double DoubleSlider::get_double_value(const SelectedSlider& selection) return m_values[selection == ssLower ? m_lower_value : m_higher_value]; } -std::vector DoubleSlider::GetTicksValues() const +using t_custom_code = Slic3r::Model::CustomGCode; +std::vector DoubleSlider::GetTicksValues() const { - std::vector values; + std::vector values; const int val_size = m_values.size(); if (!m_values.empty()) - for (int tick : m_ticks) { - if (tick > val_size) + for (const TICK_CODE& tick : m_ticks_) { + if (tick.tick > val_size) break; - values.push_back(m_values[tick]); + values.push_back(t_custom_code(m_values[tick.tick], tick.gcode, tick.extruder, tick.color)); } return values; } -void DoubleSlider::SetTicksValues(const std::vector& heights) +void DoubleSlider::SetTicksValues(const std::vector& heights) { if (m_values.empty()) return; - const bool was_empty = m_ticks.empty(); + const bool was_empty = m_ticks_.empty(); - m_ticks.clear(); + m_ticks_.clear(); for (auto h : heights) { - auto it = std::lower_bound(m_values.begin(), m_values.end(), h - epsilon()); + auto it = std::lower_bound(m_values.begin(), m_values.end(), h.height - epsilon()); if (it == m_values.end()) continue; - m_ticks.insert(it-m_values.begin()); + m_ticks_.insert(TICK_CODE(it-m_values.begin(), h.gcode, h.extruder, h.color)); } - if (!was_empty && m_ticks.empty()) + 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 wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); + + Refresh(); + Update(); } void DoubleSlider::get_lower_and_higher_position(int& lower_pos, int& higher_pos) @@ -2300,17 +2618,20 @@ void DoubleSlider::render() // //higher slider: // draw_thumb(dc, higher_pos, ssHigher); - // draw both sliders - draw_thumbs(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) @@ -2322,7 +2643,7 @@ void DoubleSlider::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoin return; wxBitmap* icon = m_is_action_icon_focesed ? &m_bmp_add_tick_off.bmp() : &m_bmp_add_tick_on.bmp(); - if (m_ticks.find(tick) != m_ticks.end()) + if (m_ticks_.find(tick) != m_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; @@ -2396,7 +2717,7 @@ void DoubleSlider::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedS } else { x_draw = pos.x - int(0.5*m_thumb_size.x); - y_draw = pos.y+1; + y_draw = pos.y - int(0.5*m_thumb_size.y); } } else{ @@ -2406,7 +2727,7 @@ void DoubleSlider::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedS } else { x_draw = pos.x - int(0.5*m_thumb_size.x); - y_draw = pos.y - m_thumb_size.y; + 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); @@ -2465,14 +2786,26 @@ void DoubleSlider::draw_ticks(wxDC& dc) int height, width; get_size(&width, &height); const wxCoord mid = is_horizontal() ? 0.5*height : 0.5*width; - for (auto tick : m_ticks) + for (auto tick : m_ticks_) { - const wxCoord pos = get_position_from_value(tick); + 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*/); + + // Draw icon for "Pause print" or "Custom Gcode" + if (tick.gcode != Slic3r::ColorChangeCode && tick.gcode != Slic3r::ExtruderChangeCode) + { + wxBitmap icon = create_scaled_bitmap(this, tick.gcode == Slic3r::PausePrintCode ? "pause_print" : "edit_gcode"); + + 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); + } } } @@ -2484,47 +2817,42 @@ void DoubleSlider::draw_colored_band(wxDC& dc) int height, width; get_size(&width, &height); - wxRect main_band = m_rect_lower_thumb; - if (is_horizontal()) { - main_band.SetLeft(SLIDER_MARGIN); - main_band.SetRight(width - SLIDER_MARGIN + 1); - } - else { - const int cut = 2; - main_band.x += cut; - main_band.width -= 2*cut; - main_band.SetTop(SLIDER_MARGIN); - main_band.SetBottom(height - SLIDER_MARGIN + 1); - } + const wxCoord mid = is_horizontal() ? 0.5 * height : 0.5 * width; - if (m_ticks.empty()) { - dc.SetPen(GetParent()->GetBackgroundColour()); - dc.SetBrush(GetParent()->GetBackgroundColour()); - dc.DrawRectangle(main_band); - return; - } + 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); - const std::vector& colors = Slic3r::GCodePreviewData::ColorPrintColors(); - const size_t colors_cnt = colors.size(); - - wxColour clr(colors[0]); - dc.SetPen(clr); - dc.SetBrush(clr); - dc.DrawRectangle(main_band); - - size_t i = 1; - for (auto tick : m_ticks) - { - if (i == colors_cnt) - i = 0; - const wxCoord pos = get_position_from_value(tick); - is_horizontal() ? main_band.SetLeft(SLIDER_MARGIN + pos) : - main_band.SetBottom(pos-1); - - clr = wxColour(colors[i]); + auto draw_band = [](wxDC& dc, const wxColour& clr, const wxRect& band_rc) { dc.SetPen(clr); dc.SetBrush(clr); - dc.DrawRectangle(main_band); + dc.DrawRectangle(band_rc); + }; + + const std::vector& colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); + int colors_cnt = colors.size(); + + const wxColour bg_clr = GetParent()->GetBackgroundColour(); + + wxColour clr = m_state == msSingleExtruder ? wxColour(colors[0]) : bg_clr; + draw_band(dc, clr, main_band); + + size_t i = 1; + for (auto tick : m_ticks_) + { + if ( (m_state == msSingleExtruder && tick.gcode != Slic3r::ColorChangeCode) || + (m_state == msMultiExtruder && tick.gcode != Slic3r::ExtruderChangeCode) ) + continue; + + const wxCoord pos = get_position_from_value(tick.tick); + is_horizontal() ? main_band.SetLeft(SLIDER_MARGIN + pos) : + main_band.SetBottom(pos - 1); + + clr = (m_state == msMultiExtruder && tick.color.empty()) ? bg_clr : + m_state == msMultiExtruder ? wxColour(colors[std::min(colors_cnt - 1, tick.extruder-1)]) : wxColour(tick.color); + draw_band(dc, clr, main_band); i++; } } @@ -2550,7 +2878,7 @@ void DoubleSlider::draw_one_layer_icon(wxDC& dc) void DoubleSlider::draw_revert_icon(wxDC& dc) { - if (m_ticks.empty() || !m_is_enabled_tick_manipulation) + if (m_ticks_.empty() || !m_is_enabled_tick_manipulation) return; int width, height; @@ -2566,9 +2894,27 @@ void DoubleSlider::draw_revert_icon(wxDC& dc) 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_state != msMultiExtruder) + 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, m_thumb_size.y); + 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 @@ -2602,16 +2948,16 @@ bool DoubleSlider::is_point_in_rect(const wxPoint& pt, const wxRect& rect) int DoubleSlider::is_point_near_tick(const wxPoint& pt) { - for (auto tick : m_ticks) { - const wxCoord pos = get_position_from_value(tick); + for (auto tick : m_ticks_) { + const wxCoord pos = get_position_from_value(tick.tick); if (is_horizontal()) { if (pos - 4 <= pt.x && pt.x <= pos + 4) - return tick; + return tick.tick; } else { if (pos - 4 <= pt.y && pt.y <= pos + 4) - return tick; + return tick.tick; } } return -1; @@ -2663,9 +3009,13 @@ void DoubleSlider::OnLeftDown(wxMouseEvent& event) m_selection == ssLower ? correct_lower_value() : correct_higher_value(); if (!m_selection) m_selection = ssHigher; - m_ticks.clear(); + m_ticks_.clear(); wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); } + else if (is_point_in_rect(pos, m_rect_cog_icon) && m_state == msMultiExtruder) { + // show dialog for set extruder sequence + m_edit_extruder_sequence = true; + } else detect_selected_slider(pos); @@ -2678,7 +3028,8 @@ void DoubleSlider::OnLeftDown(wxMouseEvent& event) 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 (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; @@ -2718,6 +3069,38 @@ void DoubleSlider::correct_higher_value() 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_.find(tick); + tooltip = tick_code_it == m_ticks_.end() ? (m_state == msSingleExtruder ? + _(L("For add color change use left mouse button click")) : + _(L("For add change extruder use left mouse button click"))) + "\n" + + _(L("For add another code use right mouse button click")) : + tick_code_it->gcode == Slic3r::ColorChangeCode ? ( m_state == msSingleExtruder ? + _(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::ExtruderChangeCode ? + 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; @@ -2726,11 +3109,14 @@ void DoubleSlider::OnMotion(wxMouseEvent& event) const wxPoint pos = event.GetLogicalPosition(dc); m_is_one_layer_icon_focesed = is_point_in_rect(pos, m_rect_one_layer_icon); - bool is_revert_icon_focused = false; + 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); - is_revert_icon_focused = !m_ticks.empty() && is_point_in_rect(pos, m_rect_revert_icon); + 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) { @@ -2751,10 +3137,7 @@ void DoubleSlider::OnMotion(wxMouseEvent& event) event.Skip(); // Set tooltips with information for each icon - const wxString tooltip = m_is_one_layer_icon_focesed ? _(L("One layer mode")) : - m_is_action_icon_focesed ? _(L("Add/Del color change")) : - is_revert_icon_focused ? _(L("Discard all color changes")) : ""; - this->SetToolTip(tooltip); + this->SetToolTip(get_tooltip(icon_focus)); if (action) { @@ -2771,6 +3154,43 @@ void DoubleSlider::OnLeftUp(wxMouseEvent& event) return; this->ReleaseMouse(); m_is_left_down = false; + + if (m_show_context_menu) + { + if (m_state == msMultiExtruder) + { + wxMenu menu; + const int extruders_cnt = Slic3r::GUI::wxGetApp().extruders_edited_cnt(); + if (extruders_cnt > 1) + { + const int initial_extruder = get_extruder_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value); + + wxMenu* change_extruder_menu = new wxMenu(); + + for (int i = 0; i <= extruders_cnt; i++) { + const wxString item_name = i == 0 ? _(L("Default")) : wxString::Format(_(L("Extruder %d")), i); + + append_menu_radio_item(change_extruder_menu, wxID_ANY, item_name, "", + [this, i](wxCommandEvent&) { change_extruder(i); }, &menu)->Check(i == initial_extruder); + } + + wxMenuItem* change_extruder_menu_item = menu.AppendSubMenu(change_extruder_menu, _(L("Change extruder")), _(L("Use another extruder"))); + change_extruder_menu_item->SetBitmap(create_scaled_bitmap(nullptr, "change_extruder")); + } + + Slic3r::GUI::wxGetApp().plater()->PopupMenu(&menu); + } + else + add_code(Slic3r::ColorChangeCode); + + m_show_context_menu = false; + } + + if (m_edit_extruder_sequence) { + edit_extruder_sequence(); + m_edit_extruder_sequence = false; + } + Refresh(); Update(); event.Skip(); @@ -2821,21 +3241,36 @@ void DoubleSlider::action_tick(const TicksAction action) const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - if (action == taOnIcon) { - if (!m_ticks.insert(tick).second) - m_ticks.erase(tick); + const auto it = m_ticks_.find(tick); + + if (it != m_ticks_.end()) // erase this tick + { + if (action == taAdd) + return; + m_ticks_.erase(TICK_CODE(tick)); + + wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); + Refresh(); + Update(); + return; } - else { - const auto it = m_ticks.find(tick); - if (it == m_ticks.end() && action == taAdd) - m_ticks.insert(tick); - else if (it != m_ticks.end() && action == taDel) - m_ticks.erase(tick); + + if (action == taDel) + return; + if (action == taAdd) + { + // OnChar() is called immediately after OnKeyDown(), which can cause call of add_code() twice. + // To avoid this case we should suppress second add_code() call. + if (m_suppress_add_code) + return; + m_suppress_add_code = true; + if (m_state != msMultiExtruder) + add_code(Slic3r::ColorChangeCode); + m_suppress_add_code = false; + return; } - wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); - Refresh(); - Update(); + m_show_context_menu = true; } void DoubleSlider::OnWheel(wxMouseEvent& event) @@ -2910,6 +3345,27 @@ void DoubleSlider::OnRightDown(wxMouseEvent& event) 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_.find(tick); + if (it == m_ticks_.end()) + { + // show context menu on OnRightUp() + m_show_context_menu = true; + return; + } + if (it->gcode != Slic3r::ExtruderChangeCode) + { + // show "Edit" and "Delete" menu on OnRightUp() + m_show_edit_menu = true; + return; + } + } + detect_selected_slider(event.GetLogicalPosition(dc)); if (!m_selection) return; @@ -2919,13 +3375,29 @@ void DoubleSlider::OnRightDown(wxMouseEvent& event) else m_lower_value = m_higher_value; - m_is_right_down = m_is_one_layer = true; + // 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) +{ + if (m_ticks_.empty()) + return 0; + + auto it = m_ticks_.lower_bound(tick); + while (it != m_ticks_.begin()) { + --it; + if(it->gcode == Slic3r::ExtruderChangeCode) + return it->extruder; + } + + return 0; +} + void DoubleSlider::OnRightUp(wxMouseEvent& event) { if (!HasCapture()) @@ -2933,11 +3405,296 @@ void DoubleSlider::OnRightUp(wxMouseEvent& event) this->ReleaseMouse(); m_is_right_down = m_is_one_layer = false; + if (m_show_context_menu) { + wxMenu menu; + + if (m_state == msMultiExtruder) + { + const int extruders_cnt = Slic3r::GUI::wxGetApp().extruders_edited_cnt(); + if (extruders_cnt > 1) + { + const int initial_extruder = get_extruder_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value); + + wxMenu* change_extruder_menu = new wxMenu(); + wxMenu* add_color_change_menu = new wxMenu(); + + for (int i = 0; i <= extruders_cnt; i++) { + const wxString item_name = i == 0 ? _(L("Default")) : wxString::Format(_(L("Extruder %d")), i); + + append_menu_radio_item(change_extruder_menu, wxID_ANY, item_name, "", + [this, i](wxCommandEvent&) { change_extruder(i); }, &menu)->Check(i == initial_extruder); + + if (i==0) // don't use M600 for default extruder, if multimaterial print is selected + continue; + append_menu_item(add_color_change_menu, wxID_ANY, item_name, "", + [this, i](wxCommandEvent&) { add_code(Slic3r::ColorChangeCode, i); }, "", &menu); + } + + wxMenuItem* change_extruder_menu_item = menu.AppendSubMenu(change_extruder_menu, _(L("Change extruder")), _(L("Use another extruder"))); + change_extruder_menu_item->SetBitmap(create_scaled_bitmap(nullptr, "change_extruder")); + + 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(nullptr, "colorchange_add_m")); + } + } + else + append_menu_item(&menu, wxID_ANY, _(L("Add color change")) + " (M600)", "", + [this](wxCommandEvent&) { add_code(Slic3r::ColorChangeCode); }, "colorchange_add_m", &menu); + + append_menu_item(&menu, wxID_ANY, _(L("Add pause print")) + " (M601)", "", + [this](wxCommandEvent&) { add_code(Slic3r::PausePrintCode); }, "pause_print", &menu); + + append_menu_item(&menu, wxID_ANY, _(L("Add custom G-code")), "", + [this](wxCommandEvent&) { add_code(""); }, "edit_gcode", &menu); + + 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_.find(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&) { action_tick(taDel); }, "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(std::string code, int selected_extruder/* = -1*/) +{ + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + // if on this Z doesn't exist tick + auto it = m_ticks_.find(tick); + if (it == m_ticks_.end()) + { + std::string color = ""; + if (code == Slic3r::ColorChangeCode) + { + std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); + + if (m_state == msSingleExtruder && !m_ticks_.empty()) { + auto before_tick_it = std::lower_bound(m_ticks_.begin(), m_ticks_.end(), tick); + while (before_tick_it != m_ticks_.begin()) { + --before_tick_it; + if (before_tick_it->gcode == Slic3r::ColorChangeCode) { + color = before_tick_it->color; + break; + } + } + + if (color.empty()) + color = colors[0]; + } + else + color = colors[selected_extruder > 0 ? selected_extruder-1 : 0]; + + color = get_new_color(color); + if (color.empty()) + return; + } + 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(m_pause_print_msg, m_values[tick]); + if (color.empty()) + return; + m_pause_print_msg = color; + } + else if (code.empty()) + { + code = get_custom_code(m_custom_gcode, m_values[tick]); + if (code.empty()) + return; + m_custom_gcode = code; + } + + int extruder = 1; + if (m_state == msMultiExtruder) { + if (code == Slic3r::ColorChangeCode && selected_extruder >= 0) + extruder = selected_extruder; + else + extruder = get_extruder_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value); + } + + m_ticks_.insert(TICK_CODE(tick, code, extruder, color)); + + wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); + Refresh(); + Update(); + } +} + +void DoubleSlider::edit_tick() +{ + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + // if on this Z exists tick + std::set::iterator it = m_ticks_.find(tick); + if (it != m_ticks_.end()) + { + 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, m_values[it->tick]); + else + edited_value = get_custom_code(it->gcode, m_values[it->tick]); + + if (edited_value.empty()) + return; + + TICK_CODE changed_tick = *it; + if (it->gcode == Slic3r::ColorChangeCode || it->gcode == Slic3r::PausePrintCode) { + if (it->color == edited_value) + return; + changed_tick.color = edited_value; + } + else { + if (it->gcode == edited_value) + return; + changed_tick.gcode = edited_value; + } + + m_ticks_.erase(it); + m_ticks_.insert(changed_tick); + + wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); + } +} + +void DoubleSlider::change_extruder(int extruder) +{ + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + + std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); + + // if on this Y doesn't exist tick + if (m_ticks_.find(tick) == m_ticks_.end()) + { + m_ticks_.insert(TICK_CODE(tick, Slic3r::ExtruderChangeCode, extruder, extruder == 0 ? "" : colors[extruder-1])); + + wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); + Refresh(); + Update(); + } +} + +void DoubleSlider::edit_extruder_sequence() +{ + 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; + + auto it = m_ticks_.begin(); + while (it != m_ticks_.end()) { + if (it->gcode == Slic3r::ExtruderChangeCode) + it = m_ticks_.erase(it); + else + ++it; + } + + 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) + { + int cur_extruder = m_extruders_sequence.extruders[extruder]; + m_ticks_.insert(TICK_CODE(tick, Slic3r::ExtruderChangeCode, 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 it = std::lower_bound(m_values.begin(), m_values.end(), value - epsilon()); + + if (it == m_values.end()) + break; + + tick = it - m_values.begin(); + } + else + tick += m_extruders_sequence.interval_by_layers; + } + + wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); +} + // ---------------------------------------------------------------------------- // LockButton @@ -2988,6 +3745,8 @@ void LockButton::msw_rescale() m_bmp_lock_closed_f.msw_rescale(); m_bmp_lock_open.msw_rescale(); m_bmp_lock_open_f.msw_rescale(); + + update_button_bitmaps(); } void LockButton::update_button_bitmaps() diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index b7bb97e09..edb321287 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -18,6 +18,7 @@ #include #include #include +#include "libslic3r/Model.hpp" namespace Slic3r { enum class ModelVolumeType : int; @@ -51,6 +52,8 @@ wxMenuItem* append_menu_check_item(wxMenu* menu, int id, const wxString& string, std::function cb, wxEvtHandler* event_handler); 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); @@ -58,6 +61,14 @@ int em_unit(wxWindow* win); 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); +std::vector get_extruder_color_icons(bool thin_icon = false); +void apply_extruder_selector(wxBitmapComboBox** ctrl, + wxWindow* parent, + const std::string& first_item = "", + wxPoint pos = wxDefaultPosition, + wxSize size = wxDefaultSize, + bool use_thin_icon = false); + class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup { static const unsigned int DefaultWidth; @@ -212,6 +223,7 @@ class ObjectDataViewModelNode 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; @@ -227,7 +239,7 @@ public: m_type(itObject), m_extruder(extruder) { - set_action_icon(); + set_action_and_extruder_icons(); init_container(); } @@ -243,7 +255,7 @@ public: m_extruder (extruder) { m_bmp = bmp; - set_action_icon(); + set_action_and_extruder_icons(); init_container(); } @@ -359,7 +371,7 @@ public: } // Set action icons for node - void set_action_icon(); + void set_action_and_extruder_icons(); // Set printable icon for node void set_printable_icon(PrintIndicator printable); @@ -441,6 +453,8 @@ public: 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 @@ -457,6 +471,8 @@ public: 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, @@ -507,6 +523,9 @@ public: 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); @@ -566,6 +585,40 @@ private: }; +// ---------------------------------------------------------------------------- +// 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 // ---------------------------------------------------------------------------- @@ -708,6 +761,11 @@ enum TicksAction{ class DoubleSlider : public wxControl { + enum IconFocus { + ifNone, + ifRevert, + ifCog + }; public: DoubleSlider( wxWindow *parent, @@ -753,8 +811,8 @@ public: m_values = values; } void ChangeOneLayerLock(); - std::vector GetTicksValues() const; - void SetTicksValues(const std::vector& heights); + std::vector GetTicksValues() const; + void SetTicksValues(const std::vector &heights); void EnableTickManipulation(bool enable = true) { m_is_enabled_tick_manipulation = enable; } @@ -762,6 +820,18 @@ public: EnableTickManipulation(false); } + enum ManipulationState { + msSingleExtruder, // single extruder printer preset is selected + msMultiExtruder // multiple extruder printer preset is selected, and "Whole print" is selected + }; + void SetManipulationState(ManipulationState state) { + m_state = state; + } + void SetManipulationState(int extruders_cnt) { + m_state = extruders_cnt ==1 ? msSingleExtruder : msMultiExtruder; + } + ManipulationState GetManipulationState() const { return m_state; } + 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; } @@ -779,7 +849,12 @@ public: void OnKeyUp(wxKeyEvent &event); void OnChar(wxKeyEvent &event); void OnRightDown(wxMouseEvent& event); + int get_extruder_for_tick(int tick); void OnRightUp(wxMouseEvent& event); + void add_code(std::string code, int selected_extruder = -1); + void edit_tick(); + void change_extruder(int extruder); + void edit_extruder_sequence(); protected: @@ -793,6 +868,7 @@ protected: 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; @@ -801,6 +877,7 @@ protected: void detect_selected_slider(const wxPoint& pt); void correct_lower_value(); void correct_higher_value(); + wxString get_tooltip(IconFocus icon_focus); void move_current_thumb(const bool condition); void action_tick(const TicksAction action); void enter_window(wxMouseEvent& event, const bool enter); @@ -835,6 +912,7 @@ private: 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; @@ -843,16 +921,25 @@ private: 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_edit_extruder_sequence = false; + bool m_suppress_add_code = false; + ManipulationState m_state = msSingleExtruder; + std::string m_custom_gcode = ""; + std::string m_pause_print_msg; 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; @@ -871,6 +958,88 @@ private: std::vector m_segm_pens; std::set m_ticks; std::vector m_values; + + struct TICK_CODE + { + TICK_CODE(int tick):tick(tick), gcode(Slic3r::ColorChangeCode), extruder(0), color("") {} + TICK_CODE(int tick, const std::string& code) : + tick(tick), gcode(code), extruder(0) {} + TICK_CODE(int tick, int extruder) : + tick(tick), gcode(Slic3r::ColorChangeCode), extruder(extruder) {} + TICK_CODE(int tick, const std::string& code, int extruder, const std::string& color) : + tick(tick), gcode(code), extruder(extruder), color(color) {} + + bool operator<(const TICK_CODE& other) const { return other.tick > this->tick; } + bool operator>(const TICK_CODE& other) const { return other.tick < this->tick; } + TICK_CODE operator=(const TICK_CODE& other) const { + TICK_CODE ret_val(other.tick, other.gcode, other.extruder, other.color); + return ret_val; + } + + int tick; + std::string gcode; + int extruder; + std::string color; + }; + + std::set m_ticks_; + +public: + struct ExtrudersSequence + { + bool is_mm_intervals; + double interval_by_mm; + int interval_by_layers; + std::vector extruders; + + ExtrudersSequence() : + is_mm_intervals(true), + interval_by_mm(3.0), + interval_by_layers(10), + extruders({ 0 }) {} + + ExtrudersSequence(const ExtrudersSequence& other) : + is_mm_intervals(other.is_mm_intervals), + interval_by_mm(other.interval_by_mm), + interval_by_layers(other.interval_by_layers), + extruders(other.extruders) {} + + ExtrudersSequence& operator=(const ExtrudersSequence& other) { + this->is_mm_intervals = other.is_mm_intervals; + this->interval_by_mm = other.interval_by_mm; + this->interval_by_layers= other.interval_by_layers; + this->extruders = other.extruders; + + return *this; + } + 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; }; diff --git a/src/slic3r/Utils/FixModelByWin10.cpp b/src/slic3r/Utils/FixModelByWin10.cpp index d955d6a7e..b7eb12e02 100644 --- a/src/slic3r/Utils/FixModelByWin10.cpp +++ b/src/slic3r/Utils/FixModelByWin10.cpp @@ -423,7 +423,7 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx) wxMessageDialog dlg(nullptr, _(L("Model repaired successfully")), _(L("Model Repair by the Netfabb service")), wxICON_INFORMATION | wxOK_DEFAULT); dlg.ShowModal(); } else { - wxMessageDialog dlg(nullptr, _(L("Model repair failed: \n")) + _(progress.message), _(L("Model Repair by the Netfabb service")), wxICON_ERROR | wxOK_DEFAULT); + wxMessageDialog dlg(nullptr, _(L("Model repair failed:")) + " \n" + _(progress.message), _(L("Model Repair by the Netfabb service")), wxICON_ERROR | wxOK_DEFAULT); dlg.ShowModal(); } worker_thread.join(); diff --git a/src/slic3r/Utils/FlashAir.cpp b/src/slic3r/Utils/FlashAir.cpp new file mode 100644 index 000000000..3fc913c99 --- /dev/null +++ b/src/slic3r/Utils/FlashAir.cpp @@ -0,0 +1,219 @@ +#include "FlashAir.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "libslic3r/PrintConfig.hpp" +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/I18N.hpp" +#include "slic3r/GUI/MsgDialog.hpp" +#include "Http.hpp" + +namespace fs = boost::filesystem; +namespace pt = boost::property_tree; + +namespace Slic3r { + +FlashAir::FlashAir(DynamicPrintConfig *config) : + host(config->opt_string("print_host")) +{} + +FlashAir::~FlashAir() {} + +const char* FlashAir::get_name() const { return "FlashAir"; } + +bool FlashAir::test(wxString &msg) const +{ + // Since the request is performed synchronously here, + // it is ok to refer to `msg` from within the closure + + const char *name = get_name(); + + bool res = false; + auto url = make_url("command.cgi", "op", "118"); + + BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get upload enabled at: %2%") % name % url; + + auto http = Http::get(std::move(url)); + http.on_error([&](std::string body, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting upload enabled: %2%, HTTP %3%, body: `%4%`") % name % error % status % body; + res = false; + msg = format_error(body, error, status); + }) + .on_complete([&, this](std::string body, unsigned) { + BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got upload enabled: %2%") % name % body; + + res = boost::starts_with(body, "1"); + if (! res) { + msg = _(L("Upload not enabled on FlashAir card.")); + } + }) + .perform_sync(); + + return res; +} + +wxString FlashAir::get_test_ok_msg () const +{ + return _(L("Connection to FlashAir works correctly and upload is enabled.")); +} + +wxString FlashAir::get_test_failed_msg (wxString &msg) const +{ + return wxString::Format("%s: %s", _(L("Could not connect to FlashAir")), msg, _(L("Note: FlashAir with firmware 2.00.02 or newer and activated upload function is required."))); +} + +bool FlashAir::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const +{ + const char *name = get_name(); + + const auto upload_filename = upload_data.upload_path.filename(); + const auto upload_parent_path = upload_data.upload_path.parent_path(); + + wxString test_msg; + if (! test(test_msg)) { + error_fn(std::move(test_msg)); + return false; + } + + bool res = false; + + auto urlPrepare = make_url("upload.cgi", "WRITEPROTECT=ON&FTIME", timestamp_str()); + auto urlUpload = make_url("upload.cgi"); + + BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3% / %4%, filename: %5%") + % name + % upload_data.source_path + % urlPrepare + % urlUpload + % upload_filename.string(); + + // set filetime for upload and make card writeprotect to prevent filesystem damage + auto httpPrepare = Http::get(std::move(urlPrepare)); + httpPrepare.on_error([&](std::string body, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error prepareing upload: %2%, HTTP %3%, body: `%4%`") % name % error % status % body; + error_fn(format_error(body, error, status)); + res = false; + }) + .on_complete([&, this](std::string body, unsigned) { + BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got prepare result: %2%") % name % body; + res = boost::icontains(body, "SUCCESS"); + if (! res) { + BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Request completed but no SUCCESS message was received.") % name; + error_fn(format_error(body, L("Unknown error occured"), 0)); + } + }) + .perform_sync(); + + if(! res ) { + return res; + } + + // start file upload + auto http = Http::post(std::move(urlUpload)); + http.form_add_file("file", upload_data.source_path.string(), upload_filename.string()) + .on_complete([&](std::string body, unsigned status) { + BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body; + res = boost::icontains(body, "SUCCESS"); + if (! res) { + BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Request completed but no SUCCESS message was received.") % name; + error_fn(format_error(body, L("Unknown error occured"), 0)); + } + }) + .on_error([&](std::string body, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status % body; + error_fn(format_error(body, error, status)); + res = false; + }) + .on_progress([&](Http::Progress progress, bool &cancel) { + prorgess_fn(std::move(progress), cancel); + if (cancel) { + // Upload was canceled + BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Upload canceled") % name; + res = false; + } + }) + .perform_sync(); + + return res; +} + +bool FlashAir::has_auto_discovery() const +{ + return false; +} + +bool FlashAir::can_test() const +{ + return true; +} + +bool FlashAir::can_start_print() const +{ + return false; +} + +std::string FlashAir::timestamp_str() const +{ + auto t = std::time(nullptr); + auto tm = *std::localtime(&t); + + const char *name = get_name(); + + unsigned long fattime = ((tm.tm_year - 80) << 25) | + ((tm.tm_mon + 1) << 21) | + (tm.tm_mday << 16) | + (tm.tm_hour << 11) | + (tm.tm_min << 5) | + (tm.tm_sec >> 1); + + return (boost::format("%1$#x") % fattime).str(); +} + +std::string FlashAir::make_url(const std::string &path) const +{ + if (host.find("http://") == 0 || host.find("https://") == 0) { + if (host.back() == '/') { + return (boost::format("%1%%2%") % host % path).str(); + } else { + return (boost::format("%1%/%2%") % host % path).str(); + } + } else { + if (host.back() == '/') { + return (boost::format("http://%1%%2%") % host % path).str(); + } else { + return (boost::format("http://%1%/%2%") % host % path).str(); + } + } +} + +std::string FlashAir::make_url(const std::string &path, const std::string &arg, const std::string &val) const +{ + if (host.find("http://") == 0 || host.find("https://") == 0) { + if (host.back() == '/') { + return (boost::format("%1%%2%?%3%=%4%") % host % path % arg % val).str(); + } else { + return (boost::format("%1%/%2%?%3%=%4%") % host % path % arg % val).str(); + } + } else { + if (host.back() == '/') { + return (boost::format("http://%1%%2%?%3%=%4%") % host % path % arg % val).str(); + } else { + return (boost::format("http://%1%/%2%?%3%=%4%") % host % path % arg % val).str(); + } + } +} + +} diff --git a/src/slic3r/Utils/FlashAir.hpp b/src/slic3r/Utils/FlashAir.hpp new file mode 100644 index 000000000..1499eee5d --- /dev/null +++ b/src/slic3r/Utils/FlashAir.hpp @@ -0,0 +1,44 @@ +#ifndef slic3r_FlashAir_hpp_ +#define slic3r_FlashAir_hpp_ + +#include +#include + +#include "PrintHost.hpp" + + +namespace Slic3r { + + +class DynamicPrintConfig; +class Http; + +class FlashAir : public PrintHost +{ +public: + FlashAir(DynamicPrintConfig *config); + virtual ~FlashAir(); + + virtual const char* get_name() const; + + virtual bool test(wxString &curl_msg) const; + virtual wxString get_test_ok_msg () const; + virtual wxString get_test_failed_msg (wxString &msg) const; + virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const; + virtual bool has_auto_discovery() const; + virtual bool can_test() const; + virtual bool can_start_print() const; + virtual std::string get_host() const { return host; } + +private: + std::string host; + + std::string timestamp_str() const; + std::string make_url(const std::string &path) const; + std::string make_url(const std::string &path, const std::string &arg, const std::string &val) const; +}; + + +} + +#endif diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index 5723afca2..d333e96ed 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -153,10 +153,10 @@ struct PresetUpdater::priv bool get_file(const std::string &url, const fs::path &target_path) const; void prune_tmps() const; void sync_version() const; - void sync_config(const std::set vendors); + void sync_config(const VendorMap vendors); void check_install_indices() const; - Updates get_config_updates() const; + Updates get_config_updates(const Semver& old_slic3r_version) const; void perform_updates(Updates &&updates, bool snapshot = true) const; }; @@ -167,7 +167,9 @@ PresetUpdater::priv::priv() , cancel(false) { set_download_prefs(GUI::wxGetApp().app_config); + // Install indicies from resources. Only installs those that are either missing or older than in resources. check_install_indices(); + // Load indices from the cache directory. index_db = Index::load_db(); } @@ -266,23 +268,24 @@ void PresetUpdater::priv::sync_version() const // Download vendor indices. Also download new bundles if an index indicates there's a new one available. // Both are saved in cache. -void PresetUpdater::priv::sync_config(const std::set vendors) +void PresetUpdater::priv::sync_config(const VendorMap vendors) { BOOST_LOG_TRIVIAL(info) << "Syncing configuration cache"; if (!enabled_config_update) { return; } // Donwload vendor preset bundles + // Over all indices from the cache directory: for (auto &index : index_db) { if (cancel) { return; } - const auto vendor_it = vendors.find(VendorProfile(index.vendor())); + const auto vendor_it = vendors.find(index.vendor()); if (vendor_it == vendors.end()) { BOOST_LOG_TRIVIAL(warning) << "No such vendor: " << index.vendor(); continue; } - const VendorProfile &vendor = *vendor_it; + const VendorProfile &vendor = vendor_it->second; if (vendor.config_update_url.empty()) { BOOST_LOG_TRIVIAL(info) << "Vendor has no config_update_url: " << vendor.name; continue; @@ -366,13 +369,16 @@ void PresetUpdater::priv::check_install_indices() const } } -// Generates a list of bundle updates that are to be performed -Updates PresetUpdater::priv::get_config_updates() const +// Generates a list of bundle updates that are to be performed. +// Version of slic3r that was running the last time and which was read out from PrusaSlicer.ini is provided +// as a parameter. +Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version) const { Updates updates; BOOST_LOG_TRIVIAL(info) << "Checking for cached configuration updates..."; + // Over all indices from the cache directory: for (const auto idx : index_db) { auto bundle_path = vendor_path / (idx.vendor() + ".ini"); auto bundle_path_idx = vendor_path / idx.path().filename(); @@ -382,7 +388,7 @@ Updates PresetUpdater::priv::get_config_updates() const continue; } - // Perform a basic load and check the version + // Perform a basic load and check the version of the installed preset bundle. auto vp = VendorProfile::from_ini(bundle_path, false); // Getting a recommended version from the latest index, wich may have been downloaded @@ -414,7 +420,8 @@ Updates PresetUpdater::priv::get_config_updates() const 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); } else if (recommended->config_version > vp.config_version) { - // Config bundle update situation + // Config bundle update situation. The recommended config bundle version for this PrusaSlicer version from the index from the cache is newer + // than the version of the currently installed config bundle. // Load 'installed' idx, if any. // 'Installed' indices are kept alongside the bundle in the `vendor` subdir @@ -423,8 +430,9 @@ Updates PresetUpdater::priv::get_config_updates() const Index existing_idx; try { existing_idx.load(bundle_path_idx); - - const auto existing_recommended = existing_idx.recommended(); + // 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) { // 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(); @@ -574,7 +582,7 @@ void PresetUpdater::sync(PresetBundle *preset_bundle) // Copy the whole vendors data for use in the background thread // Unfortunatelly as of C++11, it needs to be copied again // into the closure (but perhaps the compiler can elide this). - std::set vendors = preset_bundle->vendors; + VendorMap vendors = preset_bundle->vendors; p->thread = std::move(std::thread([this, vendors]() { this->p->prune_tmps(); @@ -607,11 +615,11 @@ void PresetUpdater::slic3r_update_notify() } } -PresetUpdater::UpdateResult PresetUpdater::config_update() const +PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver &old_slic3r_version) const { if (! p->enabled_config_update) { return R_NOOP; } - auto updates = p->get_config_updates(); + auto updates = p->get_config_updates(old_slic3r_version); if (updates.incompats.size() > 0) { BOOST_LOG_TRIVIAL(info) << boost::format("%1% bundles incompatible. Asking for action...") % updates.incompats.size(); @@ -643,13 +651,10 @@ PresetUpdater::UpdateResult PresetUpdater::config_update() const // (snapshot is taken beforehand) p->perform_updates(std::move(updates)); - GUI::ConfigWizard wizard(nullptr, GUI::ConfigWizard::RR_DATA_INCOMPAT); - - if (! wizard.run(GUI::wxGetApp().preset_bundle, this)) { + if (! GUI::wxGetApp().run_wizard(GUI::ConfigWizard::RR_DATA_INCOMPAT)) { return R_INCOMPAT_EXIT; } - GUI::wxGetApp().load_current_presets(); return R_INCOMPAT_CONFIGURED; } else { BOOST_LOG_TRIVIAL(info) << "User wants to exit Slic3r, bye..."; @@ -694,8 +699,8 @@ void PresetUpdater::install_bundles_rsrc(std::vector bundles, bool BOOST_LOG_TRIVIAL(info) << boost::format("Installing %1% bundles from resources ...") % bundles.size(); for (const auto &bundle : bundles) { - auto path_in_rsrc = p->rsrc_path / bundle; - auto path_in_vendors = p->vendor_path / bundle; + auto path_in_rsrc = (p->rsrc_path / bundle).replace_extension(".ini"); + auto path_in_vendors = (p->vendor_path / bundle).replace_extension(".ini"); updates.updates.emplace_back(std::move(path_in_rsrc), std::move(path_in_vendors), Version(), "", ""); } diff --git a/src/slic3r/Utils/PresetUpdater.hpp b/src/slic3r/Utils/PresetUpdater.hpp index 7c2aab7cc..e18695828 100644 --- a/src/slic3r/Utils/PresetUpdater.hpp +++ b/src/slic3r/Utils/PresetUpdater.hpp @@ -11,6 +11,7 @@ namespace Slic3r { class AppConfig; class PresetBundle; +class Semver; class PresetUpdater { @@ -38,7 +39,9 @@ public: // If updating is enabled, check if updates are available in cache, if so, ask about installation. // A false return value implies Slic3r should exit due to incompatibility of configuration. - UpdateResult config_update() const; + // Providing old slic3r version upgrade profiles on upgrade of an application even in case + // that the config index installed from the Internet is equal to the index contained in the installation package. + UpdateResult config_update(const Semver &old_slic3r_version) const; // "Update" a list of bundles from resources (behaves like an online update). void install_bundles_rsrc(std::vector bundles, bool snapshot = true) const; diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index ab52b2344..59a929ecc 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -14,6 +14,7 @@ #include "libslic3r/Channel.hpp" #include "OctoPrint.hpp" #include "Duet.hpp" +#include "FlashAir.hpp" #include "../GUI/PrintHostDialogs.hpp" namespace fs = boost::filesystem; @@ -43,6 +44,7 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config) switch (host_type) { case htOctoPrint: return new OctoPrint(config); case htDuet: return new Duet(config); + case htFlashAir: return new FlashAir(config); default: return nullptr; } } else { diff --git a/src/slic3r/Utils/Thread.hpp b/src/slic3r/Utils/Thread.hpp new file mode 100644 index 000000000..e9c76d2ab --- /dev/null +++ b/src/slic3r/Utils/Thread.hpp @@ -0,0 +1,28 @@ +#ifndef THREAD_HPP +#define THREAD_HPP + +#include +#include + +namespace Slic3r { + +template +inline boost::thread create_thread(boost::thread::attributes &attrs, Fn &&fn) +{ + // Duplicating the stack allocation size of Thread Building Block worker + // threads of the thread pool: allocate 4MB on a 64bit system, allocate 2MB + // on a 32bit system by default. + + attrs.set_stack_size((sizeof(void*) == 4) ? (2048 * 1024) : (4096 * 1024)); + return boost::thread{attrs, std::forward(fn)}; +} + +template inline boost::thread create_thread(Fn &&fn) +{ + boost::thread::attributes attrs; + return create_thread(attrs, std::forward(fn)); +} + +} + +#endif // THREAD_HPP diff --git a/t/clipper.t b/t/clipper.t deleted file mode 100644 index 3c9838143..000000000 --- a/t/clipper.t +++ /dev/null @@ -1,89 +0,0 @@ -use Test::More; -use strict; -use warnings; - -plan tests => 6; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use List::Util qw(sum); -use Slic3r; -use Slic3r::Geometry::Clipper qw(intersection_ex union_ex diff_ex diff_pl); - -{ - my $square = [ # ccw - [10, 10], - [20, 10], - [20, 20], - [10, 20], - ]; - my $hole_in_square = [ # cw - [14, 14], - [14, 16], - [16, 16], - [16, 14], - ]; - my $square2 = [ # ccw - [5, 12], - [25, 12], - [25, 18], - [5, 18], - ]; - my $intersection = intersection_ex([ $square, $hole_in_square ], [ $square2 ]); - - is sum(map $_->area, @$intersection), Slic3r::ExPolygon->new( - [ - [20, 18], - [10, 18], - [10, 12], - [20, 12], - ], - [ - [14, 16], - [16, 16], - [16, 14], - [14, 14], - ], - )->area, 'hole is preserved after intersection'; -} - -#========================================================== - -{ - my $contour1 = [ [0,0], [40,0], [40,40], [0,40] ]; # ccw - my $contour2 = [ [10,10], [30,10], [30,30], [10,30] ]; # ccw - my $hole = [ [15,15], [15,25], [25,25], [25,15] ]; # cw - - my $union = union_ex([ $contour1, $contour2, $hole ]); - - is_deeply [ map $_->pp, @$union ], [[ [ [40,40], [0,40], [0,0], [40,0] ] ]], - 'union of two ccw and one cw is a contour with no holes'; - - my $diff = diff_ex([ $contour1, $contour2 ], [ $hole ]); - is sum(map $_->area, @$diff), - Slic3r::ExPolygon->new([ [40,40], [0,40], [0,0], [40,0] ], [ [15,25], [25,25], [25,15], [15,15] ])->area, - 'difference of a cw from two ccw is a contour with one hole'; -} - -#========================================================== - -{ - my $square = Slic3r::Polygon->new_scale( # ccw - [10, 10], - [20, 10], - [20, 20], - [10, 20], - ); - my $square_pl = $square->split_at_first_point; - - my $res = diff_pl([$square_pl], []); - is scalar(@$res), 1, 'no-op diff_pl returns the right number of polylines'; - isa_ok $res->[0], 'Slic3r::Polyline', 'no-op diff_pl result'; - is scalar(@{$res->[0]}), scalar(@$square_pl), 'no-op diff_pl returns the unmodified input polyline'; -} - -__END__ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 11bdc4b3d..f77b4bd25 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,3 +1,30 @@ # TODO Add individual tests as executables in separate directories +# add_subirectory() -# add_subirectory() \ No newline at end of file +set(TEST_DATA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data) +file(TO_NATIVE_PATH "${TEST_DATA_DIR}" TEST_DATA_DIR) + +add_library(Catch2 INTERFACE) +list (APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules/Catch2) +target_include_directories(Catch2 INTERFACE ${CMAKE_CURRENT_LIST_DIR}) +add_library(Catch2::Catch2 ALIAS Catch2) +include(Catch) + +set(CATCH_EXTRA_ARGS "" CACHE STRING "Extra arguments for catch2 test suites.") + +add_library(test_common INTERFACE) +target_compile_definitions(test_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)" CATCH_CONFIG_FAST_COMPILE) +target_link_libraries(test_common INTERFACE Catch2::Catch2) + +if (APPLE) + target_link_libraries(test_common INTERFACE "-liconv -framework IOKit" "-framework CoreFoundation" -lc++) +endif() + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +add_subdirectory(libnest2d) +add_subdirectory(libslic3r) +add_subdirectory(timeutils) +add_subdirectory(fff_print) +add_subdirectory(sla_print) +# add_subdirectory(example) diff --git a/tests/catch2/LICENSE.txt b/tests/catch2/LICENSE.txt new file mode 100644 index 000000000..36b7cd93c --- /dev/null +++ b/tests/catch2/LICENSE.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/tests/catch2/VERSION.txt b/tests/catch2/VERSION.txt new file mode 100644 index 000000000..587a8125d --- /dev/null +++ b/tests/catch2/VERSION.txt @@ -0,0 +1,2 @@ +2.9.2 g2c869e1 + diff --git a/tests/catch2/catch.hpp b/tests/catch2/catch.hpp new file mode 100644 index 000000000..5feb2a4be --- /dev/null +++ b/tests/catch2/catch.hpp @@ -0,0 +1,17075 @@ +/* + * Catch v2.9.2 + * Generated: 2019-08-08 13:35:12.279703 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2019 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp + + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 9 +#define CATCH_VERSION_PATCH 2 + +#ifdef __clang__ +# pragma clang system_header +#elif defined __GNUC__ +# pragma GCC system_header +#endif + +// start catch_suppress_warnings.h + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(push) +# pragma warning(disable: 161 1682) +# else // __ICC +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# endif +#elif defined __GNUC__ + // Because REQUIREs trigger GCC's -Wparentheses, and because still + // supported version of g++ have only buggy support for _Pragmas, + // Wparentheses have to be suppressed globally. +# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details + +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wpadded" +#endif +// end catch_suppress_warnings.h +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +# define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +#ifdef __APPLE__ +# include +# if TARGET_OS_OSX == 1 +# define CATCH_PLATFORM_MAC +# elif TARGET_OS_IPHONE == 1 +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h + +#ifdef CATCH_IMPL +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// start catch_user_interfaces.h + +namespace Catch { + unsigned int rngSeed(); +} + +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h + +// start catch_common.h + +// start catch_compiler_capabilities.h + +// Detect a number of compiler features - by compiler +// The following features are defined: +// +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +#ifdef __cplusplus + +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define CATCH_CPP14_OR_GREATER +# endif + +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER +# endif + +#endif + +#if defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#ifdef __clang__ + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING + +# endif +#endif // __CYGWIN__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#ifdef _MSC_VER + +# if _MSC_VER >= 1900 // Visual Studio 2015 or newer +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +# endif + +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif + +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif +#endif // _MSC_VER + +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +# define CATCH_INTERNAL_CONFIG_USE_ASYNC +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) + #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH + #define CATCH_INTERNAL_CONFIG_NO_ASYNC + #define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Check if string_view is available and usable +// The check is split apart to work around v140 (VS2015) preprocessor issue... +#if defined(__has_include) +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW +#endif +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Check if optional is available and usable +#if defined(__has_include) +# if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL +# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // __has_include + +//////////////////////////////////////////////////////////////////////////////// +// Check if byte is available and usable +#if defined(__has_include) +# if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_BYTE +# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // __has_include + +//////////////////////////////////////////////////////////////////////////////// +// Check if variant is available and usable +#if defined(__has_include) +# if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 +# include +# if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +# define CATCH_CONFIG_NO_CPP17_VARIANT +# else +# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +# else +# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +# endif // defined(__clang__) && (__clang_major__ < 8) +# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // __has_include + +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define CATCH_CONFIG_CPP17_OPTIONAL +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) +# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +# define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +# define CATCH_CONFIG_CPP17_BYTE +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +# define CATCH_CONFIG_USE_ASYNC +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif + +// end catch_compiler_capabilities.h +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#include +#include +#include + +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); + +namespace Catch { + + struct CaseSensitive { enum Choice { + Yes, + No + }; }; + + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; + + protected: + NonCopyable(); + virtual ~NonCopyable(); + }; + + struct SourceLineInfo { + + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo( SourceLineInfo&& ) noexcept = default; + SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; + + bool empty() const noexcept; + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // Bring in operator<< from global namespace into Catch namespace + // This is necessary because the overload of operator<< above makes + // lookup stop at namespace Catch + using ::operator<<; + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() const; + }; + template + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + +// end catch_common.h +namespace Catch { + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h + +// start catch_interfaces_testcase.h + +#include + +namespace Catch { + + class TestSpec; + + struct ITestInvoker { + virtual void invoke () const = 0; + virtual ~ITestInvoker(); + }; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool isThrowSafe( TestCase const& testCase, IConfig const& config ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include + +namespace Catch { + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. c_str() must return a null terminated + /// string, however, and so the StringRef will internally take ownership + /// (taking a copy), if necessary. In theory this ownership is not externally + /// visible - but it does mean (substring) StringRefs should not be shared between + /// threads. + class StringRef { + public: + using size_type = std::size_t; + + private: + friend struct StringRefTestAccess; + + char const* m_start; + size_type m_size; + + char* m_data = nullptr; + + void takeOwnership(); + + static constexpr char const* const s_empty = ""; + + public: // construction/ assignment + StringRef() noexcept + : StringRef( s_empty, 0 ) + {} + + StringRef( StringRef const& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ) + {} + + StringRef( StringRef&& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ), + m_data( other.m_data ) + { + other.m_data = nullptr; + } + + StringRef( char const* rawChars ) noexcept; + + StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + ~StringRef() noexcept { + delete[] m_data; + } + + auto operator = ( StringRef const &other ) noexcept -> StringRef& { + delete[] m_data; + m_data = nullptr; + m_start = other.m_start; + m_size = other.m_size; + return *this; + } + + operator std::string() const; + + void swap( StringRef& other ) noexcept; + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != ( StringRef const& other ) const noexcept -> bool; + + auto operator[] ( size_type index ) const noexcept -> char; + + public: // named queries + auto empty() const noexcept -> bool { + return m_size == 0; + } + auto size() const noexcept -> size_type { + return m_size; + } + + auto numberOfCharacters() const noexcept -> size_type; + auto c_str() const -> char const*; + + public: // substrings and searches + auto substr( size_type start, size_type size ) const noexcept -> StringRef; + + // Returns the current start pointer. + // Note that the pointer can change when if the StringRef is a substring + auto currentData() const noexcept -> char const*; + + private: // ownership queries - may not be consistent between calls + auto isOwned() const noexcept -> bool; + auto isSubstring() const noexcept -> bool; + }; + + auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; + auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; + auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; + + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + + inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } + +} // namespace Catch + +inline auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +// end catch_stringref.h +// start catch_type_traits.hpp + + +#include + +namespace Catch{ + +#ifdef CATCH_CPP17_OR_GREATER + template + inline constexpr auto is_unique = std::true_type{}; + + template + inline constexpr auto is_unique = std::bool_constant< + (!std::is_same_v && ...) && is_unique + >{}; +#else + +template +struct is_unique : std::true_type{}; + +template +struct is_unique : std::integral_constant +::value + && is_unique::value + && is_unique::value +>{}; + +#endif +} + +// end catch_type_traits.hpp +// start catch_preprocessor.hpp + + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif + +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) + +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) + +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define INTERNAL_CATCH_TYPE_GEN\ + template struct TypeList {};\ + template\ + constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ + \ + template class L1, typename...E1, template class L2, typename...E2> \ + constexpr auto append(L1, L2) noexcept -> L1 { return {}; }\ + template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ + constexpr auto append(L1, L2, Rest...) noexcept -> decltype(append(L1{}, Rest{}...)) { return {}; }\ + template< template class L1, typename...E1, typename...Rest>\ + constexpr auto append(L1, TypeList, Rest...) noexcept -> L1 { return {}; }\ + \ + template< template class Container, template class List, typename...elems>\ + constexpr auto rewrap(List) noexcept -> TypeList> { return {}; }\ + template< template class Container, template class List, class...Elems, typename...Elements>\ + constexpr auto rewrap(List,Elements...) noexcept -> decltype(append(TypeList>{}, rewrap(Elements{}...))) { return {}; }\ + \ + template