diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e1aef5e1..70b7ed567 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,6 @@ option(SLIC3R_STATIC "Compile PrusaSlicer with static libraries (Boost, TBB, option(SLIC3R_GUI "Compile PrusaSlicer with GUI components (OpenGL, wxWidgets)" 1) option(SLIC3R_FHS "Assume PrusaSlicer is to be installed in a FHS directory structure" 0) option(SLIC3R_WX_STABLE "Build against wxWidgets stable (3.0) as oppsed to dev (3.1) on Linux" 0) -option(SLIC3R_PROFILE "Compile PrusaSlicer with an invasive Shiny profiler" 0) option(SLIC3R_PCH "Use precompiled headers" 1) option(SLIC3R_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1) option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1) @@ -328,25 +327,6 @@ add_definitions(-DwxUSE_UNICODE -D_UNICODE -DUNICODE -DWXINTL_NO_GETTEXT_MACRO) # Disable unsafe implicit wxString to const char* / std::string and vice versa. This implicit conversion breaks the UTF-8 encoding quite often. add_definitions(-DwxNO_UNSAFE_WXSTRING_CONV) -if (SLIC3R_PROFILE) - message("PrusaSlicer will be built with a Shiny invasive profiler") - add_definitions(-DSLIC3R_PROFILE) -endif () - -# Disable optimization even with debugging on. -if (0) - message(STATUS "Perl compiled without optimization. Disabling optimization for the PrusaSlicer build.") - message("Old CMAKE_CXX_FLAGS_RELEASE: ${CMAKE_CXX_FLAGS_RELEASE}") - message("Old CMAKE_CXX_FLAGS_RELWITHDEBINFO: ${CMAKE_CXX_FLAGS_RELEASE}") - message("Old CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS_RELEASE}") - set(CMAKE_CXX_FLAGS_RELEASE "/MD /Od /Zi /EHsc /DWIN32 /DTBB_USE_ASSERT") - set(CMAKE_C_FLAGS_RELEASE "/MD /Od /Zi /DWIN32 /DTBB_USE_ASSERT") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /EHsc /DWIN32 /DTBB_USE_ASSERT") - set(CMAKE_C_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /DWIN32 /DTBB_USE_ASSERT") - set(CMAKE_CXX_FLAGS "/MD /Od /Zi /EHsc /DWIN32 /DTBB_USE_ASSERT") - set(CMAKE_C_FLAGS "/MD /Od /Zi /DWIN32 /DTBB_USE_ASSERT") -endif() - # Find and configure boost if(SLIC3R_STATIC) # Use static boost libraries. diff --git a/deps/OCCT/OCCT.cmake b/deps/OCCT/OCCT.cmake index 9981ac15e..efb529a5a 100644 --- a/deps/OCCT/OCCT.cmake +++ b/deps/OCCT/OCCT.cmake @@ -3,6 +3,7 @@ prusaslicer_add_cmake_project(OCCT URL https://github.com/Open-Cascade-SAS/OCCT/archive/refs/tags/V7_6_2.zip URL_HASH SHA256=c696b923593e8c18d059709717dbf155b3e72fdd283c8522047a790ec3a432c5 + PATCH_COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_LIST_DIR}/occt_toolkit.cmake ./adm/cmake/ CMAKE_ARGS -DINSTALL_DIR_LAYOUT=Unix # LMBBS -DBUILD_LIBRARY_TYPE=Static @@ -20,3 +21,7 @@ prusaslicer_add_cmake_project(OCCT -DBUILD_MODULE_ModelingData=OFF -DBUILD_MODULE_Visualization=OFF ) + +if (MSVC) + add_debug_dep(dep_OCCT) +endif () diff --git a/deps/OCCT/occt_toolkit.cmake b/deps/OCCT/occt_toolkit.cmake new file mode 100644 index 000000000..1b77eb1b2 --- /dev/null +++ b/deps/OCCT/occt_toolkit.cmake @@ -0,0 +1,453 @@ +# script for each OCCT toolkit + +# filling some variables by default values(src) or using custom(tools, samples) +set (RELATIVE_SOURCES_DIR "${RELATIVE_DIR}") +if ("${RELATIVE_SOURCES_DIR}" STREQUAL "") + #if it is not defined, use default directory + set (RELATIVE_SOURCES_DIR "src") +endif() + +set (OCC_MODULES_LIST "${MODULES_LIST}") +if ("${OCC_MODULES_LIST}" STREQUAL "") + set (OCC_MODULES_LIST ${OCCT_MODULES}) +endif() + +set (OCC_TARGET_FOLDER "${TARGET_FOLDER}") +if ("${OCC_TARGET_FOLDER}" STREQUAL "") + set (OCC_TARGET_FOLDER "Modules") +endif() + +set (OCCT_TOOLKITS_NAME_SUFFIX "${TOOLKITS_NAME_SUFFIX}") +if ("${OCCT_TOOLKITS_NAME_SUFFIX}" STREQUAL "") + set (OCCT_TOOLKITS_NAME_SUFFIX "TOOLKITS") +endif() + +# parse PACKAGES file +FILE_TO_LIST ("${RELATIVE_SOURCES_DIR}/${PROJECT_NAME}/PACKAGES" USED_PACKAGES) +if ("${USED_PACKAGES}" STREQUAL "") + set (USED_PACKAGES ${PROJECT_NAME}) +endif() + +if (USE_QT) + # Qt dependencies + OCCT_INCLUDE_CMAKE_FILE (adm/cmake/qt_macro) + FIND_QT_PACKAGE(PROJECT_LIBRARIES_DEBUG PROJECT_LIBRARIES_RELEASE PROJECT_INCLUDES) + include_directories("${PROJECT_INCLUDES}") +endif(USE_QT) + +set (PRECOMPILED_DEFS) + +if (NOT BUILD_SHARED_LIBS) + list (APPEND PRECOMPILED_DEFS "-DOCCT_NO_PLUGINS") + if (WIN32 AND NOT EXECUTABLE_PROJECT) + list (APPEND PRECOMPILED_DEFS "-DOCCT_STATIC_BUILD") + endif() +endif() + +# Get all used packages from toolkit +UNSET(RESOURCE_FILES) +foreach (OCCT_PACKAGE ${USED_PACKAGES}) + + #remove part after "/" in the OCCT_PACKAGE variable if exists + string (FIND "${OCCT_PACKAGE}" "/" _index) + if (_index GREATER -1) + math (EXPR _index "${_index}") + string (SUBSTRING "${OCCT_PACKAGE}" 0 ${_index} OCCT_PACKAGE_NAME) + else() + set (OCCT_PACKAGE_NAME "${OCCT_PACKAGE}") + endif() + + if (WIN32) + list (APPEND PRECOMPILED_DEFS "-D__${OCCT_PACKAGE_NAME}_DLL") + endif() + + set (SOURCE_FILES) + set (HEADER_FILES) + + # Generate Flex and Bison files + if (${BUILD_YACCLEX}) + # flex files + OCCT_ORIGIN_AND_PATCHED_FILES ("${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}" "*[.]lex" SOURCE_FILES_FLEX) + list (LENGTH SOURCE_FILES_FLEX SOURCE_FILES_FLEX_LEN) + + # bison files + OCCT_ORIGIN_AND_PATCHED_FILES ("${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}" "*[.]yacc" SOURCE_FILES_BISON) + list (LENGTH SOURCE_FILES_BISON SOURCE_FILES_BISON_LEN) + + if (${SOURCE_FILES_FLEX_LEN} EQUAL ${SOURCE_FILES_BISON_LEN} AND NOT ${SOURCE_FILES_FLEX_LEN} EQUAL 0) + + list (SORT SOURCE_FILES_FLEX) + list (SORT SOURCE_FILES_BISON) + + math (EXPR SOURCE_FILES_FLEX_LEN "${SOURCE_FILES_FLEX_LEN} - 1") + foreach (FLEX_FILE_INDEX RANGE ${SOURCE_FILES_FLEX_LEN}) + + list (GET SOURCE_FILES_FLEX ${FLEX_FILE_INDEX} CURRENT_FLEX_FILE) + get_filename_component (CURRENT_FLEX_FILE_NAME ${CURRENT_FLEX_FILE} NAME_WE) + + list (GET SOURCE_FILES_BISON ${FLEX_FILE_INDEX} CURRENT_BISON_FILE) + get_filename_component (CURRENT_BISON_FILE_NAME ${CURRENT_BISON_FILE} NAME_WE) + + string (COMPARE EQUAL ${CURRENT_FLEX_FILE_NAME} ${CURRENT_BISON_FILE_NAME} ARE_FILES_EQUAL) + + if (EXISTS "${CURRENT_FLEX_FILE}" AND EXISTS "${CURRENT_BISON_FILE}" AND ${ARE_FILES_EQUAL}) + + # Note: files are generated in original source directory (not in patch!) + set (FLEX_BISON_TARGET_DIR "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}") + + # choose appropriate extension for generated files: "cxx" if source file contains + # instruction to generate C++ code, "c" otherwise + set (BISON_OUTPUT_FILE_EXT "c") + set (FLEX_OUTPUT_FILE_EXT "c") + file (STRINGS "${CURRENT_BISON_FILE}" FILE_BISON_CONTENT) + foreach (FILE_BISON_CONTENT_LINE ${FILE_BISON_CONTENT}) + string (REGEX MATCH "%language \"C\\+\\+\"" CXX_BISON_LANGUAGE_FOUND ${FILE_BISON_CONTENT_LINE}) + if (CXX_BISON_LANGUAGE_FOUND) + set (BISON_OUTPUT_FILE_EXT "cxx") + endif() + endforeach() + file (STRINGS "${CURRENT_FLEX_FILE}" FILE_FLEX_CONTENT) + foreach (FILE_FLEX_CONTENT_LINE ${FILE_FLEX_CONTENT}) + string (REGEX MATCH "%option c\\+\\+" CXX_FLEX_LANGUAGE_FOUND ${FILE_FLEX_CONTENT_LINE}) + if (CXX_FLEX_LANGUAGE_FOUND) + set (FLEX_OUTPUT_FILE_EXT "cxx") + + # install copy of FlexLexer.h locally to allow further building without flex + if (FLEX_INCLUDE_DIR AND EXISTS "${FLEX_INCLUDE_DIR}/FlexLexer.h") + configure_file("${FLEX_INCLUDE_DIR}/FlexLexer.h" "${FLEX_BISON_TARGET_DIR}/FlexLexer.h" @ONLY NEWLINE_STYLE LF) + endif() + endif() + endforeach() + set (BISON_OUTPUT_FILE ${CURRENT_BISON_FILE_NAME}.tab.${BISON_OUTPUT_FILE_EXT}) + set (FLEX_OUTPUT_FILE lex.${CURRENT_FLEX_FILE_NAME}.${FLEX_OUTPUT_FILE_EXT}) + + BISON_TARGET (Parser_${CURRENT_BISON_FILE_NAME} ${CURRENT_BISON_FILE} "${FLEX_BISON_TARGET_DIR}/${BISON_OUTPUT_FILE}" + COMPILE_FLAGS "-p ${CURRENT_BISON_FILE_NAME} -l -M ${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/=") + FLEX_TARGET (Scanner_${CURRENT_FLEX_FILE_NAME} ${CURRENT_FLEX_FILE} "${FLEX_BISON_TARGET_DIR}/${FLEX_OUTPUT_FILE}" + COMPILE_FLAGS "-P${CURRENT_FLEX_FILE_NAME} -L") + ADD_FLEX_BISON_DEPENDENCY (Scanner_${CURRENT_FLEX_FILE_NAME} Parser_${CURRENT_BISON_FILE_NAME}) + + list (APPEND SOURCE_FILES ${BISON_OUTPUT_FILE} ${FLEX_OUTPUT_FILE}) + endif() + endforeach() + endif() + endif() + + # header files + if (BUILD_PATCH AND EXISTS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES") + file (STRINGS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES" HEADER_FILES_M REGEX ".+[.]h") + file (STRINGS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES" HEADER_FILES_LXX REGEX ".+[.]lxx") + file (STRINGS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES" HEADER_FILES_GXX REGEX ".+[.]gxx") + + file (STRINGS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES" SOURCE_FILES_C REGEX ".+[.]c") + if(APPLE) + file (STRINGS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES" SOURCE_FILES_M REGEX ".+[.]mm") + endif() + else() + file (STRINGS "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES" HEADER_FILES_M REGEX ".+[.]h") + file (STRINGS "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES" HEADER_FILES_LXX REGEX ".+[.]lxx") + file (STRINGS "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES" HEADER_FILES_GXX REGEX ".+[.]gxx") + + file (STRINGS "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES" SOURCE_FILES_C REGEX ".+[.]c") + if(APPLE) + file (STRINGS "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES" SOURCE_FILES_M REGEX ".+[.]mm") + endif() + endif() + + list (APPEND HEADER_FILES ${HEADER_FILES_M} ${HEADER_FILES_LXX} ${SOURCE_FILES_GXX}) + list (APPEND SOURCE_FILES ${SOURCE_FILES_C}) + if(APPLE) + list (APPEND SOURCE_FILES ${SOURCE_FILES_M}) + endif() + + foreach(HEADER_FILE ${HEADER_FILES}) + if (BUILD_PATCH AND EXISTS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${HEADER_FILE}") + message (STATUS "Info: consider patched file: ${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${HEADER_FILE}") + list (APPEND USED_INCFILES "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${HEADER_FILE}") + SOURCE_GROUP ("Header Files\\${OCCT_PACKAGE_NAME}" FILES "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${HEADER_FILE}") + else() + list (APPEND USED_INCFILES "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${HEADER_FILE}") + SOURCE_GROUP ("Header Files\\${OCCT_PACKAGE_NAME}" FILES "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${HEADER_FILE}") + endif() + endforeach() + + foreach(SOURCE_FILE ${SOURCE_FILES}) + if (BUILD_PATCH AND EXISTS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${SOURCE_FILE}") + message (STATUS "Info: consider patched file: ${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${SOURCE_FILE}") + list (APPEND USED_SRCFILES "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${SOURCE_FILE}") + SOURCE_GROUP ("Source Files\\${OCCT_PACKAGE_NAME}" FILES "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${SOURCE_FILE}") + else() + list (APPEND USED_SRCFILES "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${SOURCE_FILE}") + SOURCE_GROUP ("Source Files\\${OCCT_PACKAGE_NAME}" FILES "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${SOURCE_FILE}") + endif() + endforeach() + + if (USE_QT) + FIND_AND_INSTALL_QT_RESOURCES (${OCCT_PACKAGE} RESOURCE_FILES) + #message("Qt Resource files are: ${QT_RESOURCE_FILES} in ${OCCT_PACKAGE}") + endif(USE_QT) + + #message("Resource files are: ${RESOURCE_FILES} in ${OCCT_PACKAGE}") + foreach(RESOURCE_FILE ${RESOURCE_FILES}) + SOURCE_GROUP ("Resource Files\\${OCCT_PACKAGE_NAME}" FILES "${RESOURCE_FILE}") + endforeach() +endforeach() +string (REGEX REPLACE ";" " " PRECOMPILED_DEFS "${PRECOMPILED_DEFS}") + +set (USED_RCFILE "") +if (MSVC) + set (USED_RCFILE "${CMAKE_BINARY_DIR}/resources/${PROJECT_NAME}.rc") + + if (APPLY_OCCT_PATCH_DIR AND EXISTS "${APPLY_OCCT_PATCH_DIR}/adm/templates/occt_toolkit.rc.in") + configure_file("${APPLY_OCCT_PATCH_DIR}/adm/templates/occt_toolkit.rc.in" "${USED_RCFILE}" @ONLY) + else() + configure_file("${CMAKE_SOURCE_DIR}/adm/templates/occt_toolkit.rc.in" "${USED_RCFILE}" @ONLY) + endif() +endif() + +set (CURRENT_MODULE) +foreach (OCCT_MODULE ${OCC_MODULES_LIST}) + list (FIND ${OCCT_MODULE}_${OCCT_TOOLKITS_NAME_SUFFIX} ${PROJECT_NAME} CURRENT_PROJECT_IS_BUILT) + + if (NOT ${CURRENT_PROJECT_IS_BUILT} EQUAL -1) + set (CURRENT_MODULE ${OCCT_MODULE}) + endif() +endforeach() + +if (MSVC) + OCCT_INSERT_CODE_FOR_TARGET () +endif() + +if (USE_QT) + FIND_AND_WRAP_MOC_FILES("${USED_INCFILES}" "${PROJECT_NAME}_MOC_FILES") + #message("MOC files: ${${PROJECT_NAME}_MOC_FILES}") +endif (USE_QT) + +if (EXECUTABLE_PROJECT) + add_executable (${PROJECT_NAME} ${USED_SRCFILES} ${USED_INCFILES} ${USED_RCFILE} ${RESOURCE_FILES} ${${PROJECT_NAME}_MOC_FILES}) + + install (TARGETS ${PROJECT_NAME} + DESTINATION "${INSTALL_DIR_BIN}\${OCCT_INSTALL_BIN_LETTER}") + + if (EMSCRIPTEN) + install(FILES ${CMAKE_BINARY_DIR}/${OS_WITH_BIT}/${COMPILER}/bin\${OCCT_INSTALL_BIN_LETTER}/${PROJECT_NAME}.wasm DESTINATION "${INSTALL_DIR_BIN}/${OCCT_INSTALL_BIN_LETTER}") + endif() +else() + add_library (${PROJECT_NAME} ${USED_SRCFILES} ${USED_INCFILES} ${USED_RCFILE} ${RESOURCE_FILES} ${${PROJECT_NAME}_MOC_FILES}) + + if (MSVC) + if (BUILD_FORCE_RelWithDebInfo) + set (aReleasePdbConf "Release") + else() + set (aReleasePdbConf) + endif() + # install (FILES ${CMAKE_BINARY_DIR}/${OS_WITH_BIT}/${COMPILER}/bin\${OCCT_INSTALL_BIN_LETTER}/${PROJECT_NAME}.pdb + # CONFIGURATIONS Debug ${aReleasePdbConf} RelWithDebInfo + # DESTINATION "${INSTALL_DIR_BIN}\${OCCT_INSTALL_BIN_LETTER}") + endif() + + if (BUILD_SHARED_LIBS AND NOT "${BUILD_SHARED_LIBRARY_NAME_POSTFIX}" STREQUAL "") + set (CMAKE_SHARED_LIBRARY_SUFFIX_DEFAULT ${CMAKE_SHARED_LIBRARY_SUFFIX}) + set (CMAKE_SHARED_LIBRARY_SUFFIX "${BUILD_SHARED_LIBRARY_NAME_POSTFIX}${CMAKE_SHARED_LIBRARY_SUFFIX}") + endif() + + install (TARGETS ${PROJECT_NAME} + EXPORT OpenCASCADE${CURRENT_MODULE}Targets + RUNTIME DESTINATION "${INSTALL_DIR_BIN}\${OCCT_INSTALL_BIN_LETTER}" + ARCHIVE DESTINATION "${INSTALL_DIR_LIB}\${OCCT_INSTALL_BIN_LETTER}" + LIBRARY DESTINATION "${INSTALL_DIR_LIB}\${OCCT_INSTALL_BIN_LETTER}") + + if (NOT WIN32) + if (BUILD_SHARED_LIBS AND NOT "${BUILD_SHARED_LIBRARY_NAME_POSTFIX}" STREQUAL "") + set (LINK_NAME "${INSTALL_DIR}/${INSTALL_DIR_LIB}\${OCCT_INSTALL_BIN_LETTER}/lib${PROJECT_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX_DEFAULT}") + set (LIBRARY_NAME "${INSTALL_DIR}/${INSTALL_DIR_LIB}\${OCCT_INSTALL_BIN_LETTER}/lib${PROJECT_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX}") + OCCT_CREATE_SYMLINK_TO_FILE (${LIBRARY_NAME} ${LINK_NAME}) + endif() + endif() +endif() + +if (CURRENT_MODULE) + set_target_properties (${PROJECT_NAME} PROPERTIES FOLDER "${OCC_TARGET_FOLDER}/${CURRENT_MODULE}") + set_target_properties (${PROJECT_NAME} PROPERTIES MODULE "${CURRENT_MODULE}") + if (APPLE) + if (NOT "${INSTALL_NAME_DIR}" STREQUAL "") + set_target_properties (${PROJECT_NAME} PROPERTIES BUILD_WITH_INSTALL_RPATH 1 INSTALL_NAME_DIR "${INSTALL_NAME_DIR}") + endif() + endif() +endif() + +get_property (OCC_VERSION_MAJOR GLOBAL PROPERTY OCC_VERSION_MAJOR) +get_property (OCC_VERSION_MINOR GLOBAL PROPERTY OCC_VERSION_MINOR) +get_property (OCC_VERSION_MAINTENANCE GLOBAL PROPERTY OCC_VERSION_MAINTENANCE) + +if (ANDROID) + # do not append version to the filename + set_target_properties (${PROJECT_NAME} PROPERTIES COMPILE_FLAGS "${PRECOMPILED_DEFS}") +else() + set_target_properties (${PROJECT_NAME} PROPERTIES COMPILE_FLAGS "${PRECOMPILED_DEFS}" + SOVERSION "${OCC_VERSION_MAJOR}" + VERSION "${OCC_VERSION_MAJOR}.${OCC_VERSION_MINOR}.${OCC_VERSION_MAINTENANCE}") +endif() + +set (USED_TOOLKITS_BY_CURRENT_PROJECT) +set (USED_EXTERNAL_LIBS_BY_CURRENT_PROJECT) + +# parse EXTERNLIB file +if (CUSTOM_EXTERNLIB) + set (USED_EXTERNLIB_AND_TOOLKITS ${CUSTOM_EXTERNLIB}) +else() + FILE_TO_LIST ("${RELATIVE_SOURCES_DIR}/${PROJECT_NAME}/EXTERNLIB" USED_EXTERNLIB_AND_TOOLKITS) +endif() +foreach (USED_ITEM ${USED_EXTERNLIB_AND_TOOLKITS}) + string (REGEX MATCH "^ *#" COMMENT_FOUND ${USED_ITEM}) + if (NOT COMMENT_FOUND) + string (REGEX MATCH "^TK" TK_FOUND ${USED_ITEM}) + string (REGEX MATCH "^vtk" VTK_FOUND ${USED_ITEM}) + + if (NOT "${TK_FOUND}" STREQUAL "" OR NOT "${VTK_FOUND}" STREQUAL "") + list (APPEND USED_TOOLKITS_BY_CURRENT_PROJECT ${USED_ITEM}) + if (NOT "${VTK_FOUND}" STREQUAL "" AND BUILD_SHARED_LIBS AND INSTALL_VTK AND COMMAND OCCT_INSTALL_VTK) + OCCT_INSTALL_VTK(${USED_ITEM}) + endif() + else() + string (REGEX MATCH "^CSF_" CSF_FOUND ${USED_ITEM}) + if ("${CSF_FOUND}" STREQUAL "") + message (STATUS "Info: ${USED_ITEM} from ${PROJECT_NAME} skipped due to it is empty") + else() # get CSF_ value + set (CURRENT_CSF ${${USED_ITEM}}) + if (NOT "x${CURRENT_CSF}" STREQUAL "x") + if ("${CURRENT_CSF}" STREQUAL "CSF_OpenGlLibs") + add_definitions (-DHAVE_OPENGL) + endif() + if ("${CURRENT_CSF}" STREQUAL "CSF_OpenGlesLibs") + add_definitions (-DHAVE_GLES2) + endif() + + set (LIBRARY_FROM_CACHE 0) + separate_arguments (CURRENT_CSF) + foreach (CSF_LIBRARY ${CURRENT_CSF}) + string (TOLOWER "${CSF_LIBRARY}" CSF_LIBRARY) + string (REPLACE "+" "[+]" CSF_LIBRARY "${CSF_LIBRARY}") + string (REPLACE "." "" CSF_LIBRARY "${CSF_LIBRARY}") + get_cmake_property(ALL_CACHE_VARIABLES CACHE_VARIABLES) + string (REGEX MATCHALL "(^|;)3RDPARTY_[^;]+_LIBRARY[^;]*" ALL_CACHE_VARIABLES "${ALL_CACHE_VARIABLES}") + foreach (CACHE_VARIABLE ${ALL_CACHE_VARIABLES}) + set (CURRENT_CACHE_LIBRARY ${${CACHE_VARIABLE}}) + string (TOLOWER "${CACHE_VARIABLE}" CACHE_VARIABLE) + + if (EXISTS "${CURRENT_CACHE_LIBRARY}" AND NOT IS_DIRECTORY "${CURRENT_CACHE_LIBRARY}") + string (REGEX MATCH "_${CSF_LIBRARY}$" IS_ENDING "${CACHE_VARIABLE}") + string (REGEX MATCH "^([a-z]+)" CSF_WO_VERSION "${CSF_LIBRARY}") + string (REGEX MATCH "_${CSF_WO_VERSION}$" IS_ENDING_WO_VERSION "${CACHE_VARIABLE}") + if ("3rdparty_${CSF_LIBRARY}_library" STREQUAL "${CACHE_VARIABLE}" OR + "3rdparty_${CSF_WO_VERSION}_library" STREQUAL "${CACHE_VARIABLE}" OR + NOT "x${IS_ENDING}" STREQUAL "x" OR + NOT "x${IS_ENDING_WO_VERSION}" STREQUAL "x") + list (APPEND USED_EXTERNAL_LIBS_BY_CURRENT_PROJECT "${CURRENT_CACHE_LIBRARY}") + set (LIBRARY_FROM_CACHE 1) + endif() + endif() + endforeach() + endforeach() + + if (NOT ${LIBRARY_FROM_CACHE}) + # prepare a list from a string with whitespaces + separate_arguments (CURRENT_CSF) + list (APPEND USED_EXTERNAL_LIBS_BY_CURRENT_PROJECT ${CURRENT_CSF}) + endif() + endif() + endif() + endif() + endif() +endforeach() + +if (APPLE) + list (FIND USED_EXTERNAL_LIBS_BY_CURRENT_PROJECT X11 IS_X11_FOUND) + if (NOT ${IS_X11_FOUND} EQUAL -1) + find_package (X11 COMPONENTS X11) + if (NOT X11_FOUND) + message (STATUS "Warning: X11 is not found. It's required to install The XQuartz project: http://www.xquartz.org") + endif() + endif() +endif() + +# Update list of used VTK libraries if OpenGL2 Rendering BackEnd is used. +# Add VTK_OPENGL2_BACKEND definition. +if("${VTK_RENDERING_BACKEND}" STREQUAL "OpenGL2" OR IS_VTK_9XX) + add_definitions(-DVTK_OPENGL2_BACKEND) + foreach (VTK_EXCLUDE_LIBRARY vtkRenderingOpenGL vtkRenderingFreeTypeOpenGL) + list (FIND USED_TOOLKITS_BY_CURRENT_PROJECT "${VTK_EXCLUDE_LIBRARY}" IS_VTK_OPENGL_FOUND) + if (NOT ${IS_VTK_OPENGL_FOUND} EQUAL -1) + list (REMOVE_ITEM USED_TOOLKITS_BY_CURRENT_PROJECT ${VTK_EXCLUDE_LIBRARY}) + if (${VTK_EXCLUDE_LIBRARY} STREQUAL vtkRenderingOpenGL) + list (APPEND USED_TOOLKITS_BY_CURRENT_PROJECT vtkRenderingOpenGL2) + if(VTK_MAJOR_VERSION GREATER 6) + list (APPEND USED_TOOLKITS_BY_CURRENT_PROJECT vtkRenderingGL2PSOpenGL2) + endif() + endif() + endif() + endforeach() +else() + if(VTK_MAJOR_VERSION EQUAL 6 AND VTK_MINOR_VERSION GREATER 2 OR VTK_MAJOR_VERSION GREATER 6) + list (FIND USED_TOOLKITS_BY_CURRENT_PROJECT "vtkRenderingFreeTypeOpenGL" IS_VTK_RENDER_FREETYPE_FOUND) + if (NOT ${IS_VTK_RENDER_FREETYPE_FOUND} EQUAL -1) + list (REMOVE_ITEM USED_TOOLKITS_BY_CURRENT_PROJECT "vtkRenderingFreeTypeOpenGL") + endif() + endif() +endif() + +if (BUILD_SHARED_LIBS OR EXECUTABLE_PROJECT) + if(IS_VTK_9XX) + string (REGEX REPLACE "vtk" "VTK::" USED_TOOLKITS_BY_CURRENT_PROJECT "${USED_TOOLKITS_BY_CURRENT_PROJECT}") + endif() + target_link_libraries (${PROJECT_NAME} ${USED_TOOLKITS_BY_CURRENT_PROJECT} ${USED_EXTERNAL_LIBS_BY_CURRENT_PROJECT}) +endif() + +if (USE_QT) + foreach (PROJECT_LIBRARY_DEBUG ${PROJECT_LIBRARIES_DEBUG}) + target_link_libraries (${PROJECT_NAME} debug ${PROJECT_LIBRARY_DEBUG}) + endforeach() + foreach (PROJECT_LIBRARY_RELEASE ${PROJECT_LIBRARIES_RELEASE}) + target_link_libraries (${PROJECT_NAME} optimized ${PROJECT_LIBRARY_RELEASE}) + endforeach() +endif() + +# suppress deprecation warnings inside OCCT itself for old gcc versions with unavailable Standard_DISABLE_DEPRECATION_WARNINGS +if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6.0) + add_definitions("-DOCCT_NO_DEPRECATED") + message (STATUS "Warning: internal deprecation warnings by Standard_DEPRECATED have been disabled due to old gcc version being used") + endif() +endif() + +# use Cotire to accelerate build via usage of precompiled headers +if (BUILD_USE_PCH) + if (WIN32) + # prevent definition of min and max macros through inclusion of Windows.h + # (for cotire builds) + add_definitions("-DNOMINMAX") + # avoid warnings on deprecated names from standard C library (see strsafe.h) + add_definitions("-DSTRSAFE_NO_DEPRECATE") + # avoid "std::Equal1" warning in QANCollection_Stl.cxx in debug mode + # suggesting using msvc "Checked Iterators" + add_definitions("-D_SCL_SECURE_NO_WARNINGS") + endif() + + # Exclude system-provided glext.h. + # These macros are already defined within OpenGl_GlFunctions.hxx, + # however we have to duplicate them here for building TKOpenGl with PCH. + add_definitions("-DGL_GLEXT_LEGACY") + add_definitions("-DGLX_GLXEXT_LEGACY") + + # workaround for old gcc + if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + add_definitions("-D__STDC_CONSTANT_MACROS") + add_definitions("-D__STDC_FORMAT_MACROS") + endif() + + # unity builds are not used since they do not add speed but cause conflicts + # in TKV3d + set_target_properties(${PROJECT_NAME} PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE) + + cotire(${PROJECT_NAME}) +endif() diff --git a/doc/Dependencies.md b/doc/Dependencies.md index f37f45466..d8ced6318 100644 --- a/doc/Dependencies.md +++ b/doc/Dependencies.md @@ -24,7 +24,6 @@ * 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 * semver: One module C library, author expects to use clib for installation. No packages. -* Shiny: no packages ## Header only * igl diff --git a/resources/icons/edit.svg b/resources/icons/edit.svg new file mode 100644 index 000000000..3cc959b1e --- /dev/null +++ b/resources/icons/edit.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/localization/ja/PrusaSlicer.mo b/resources/localization/ja/PrusaSlicer.mo index d6b1a975d..bca1e1cd0 100644 Binary files a/resources/localization/ja/PrusaSlicer.mo and b/resources/localization/ja/PrusaSlicer.mo differ diff --git a/resources/localization/ja/PrusaSlicer_ja.po b/resources/localization/ja/PrusaSlicer_ja.po index 2f1b1257e..0427cb3b1 100644 --- a/resources/localization/ja/PrusaSlicer_ja.po +++ b/resources/localization/ja/PrusaSlicer_ja.po @@ -7,7 +7,7 @@ msgstr "" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Prusalator\n" -#: src/slic3r/GUI/Tab.cpp:4774 +#: src/slic3r/GUI/Tab.cpp:4772 #, boost-format msgid "" "\"%1%\" is disabled because \"%2%\" is on in \"%3%\" category.\n" @@ -36,9 +36,8 @@ msgstr "" #: src/libslic3r/PrintConfig.cpp:486 src/libslic3r/PrintConfig.cpp:1109 #: src/libslic3r/PrintConfig.cpp:1577 src/libslic3r/PrintConfig.cpp:1769 #: src/libslic3r/PrintConfig.cpp:1832 src/libslic3r/PrintConfig.cpp:2083 -#: src/libslic3r/PrintConfig.cpp:2142 src/libslic3r/PrintConfig.cpp:3134 -#: src/libslic3r/PrintConfig.cpp:3149 src/libslic3r/PrintConfig.cpp:3318 -#: src/libslic3r/PrintConfig.cpp:3607 src/libslic3r/PrintConfig.cpp:3733 +#: src/libslic3r/PrintConfig.cpp:2142 src/libslic3r/PrintConfig.cpp:3291 +#: src/libslic3r/PrintConfig.cpp:3580 src/libslic3r/PrintConfig.cpp:3706 msgid "%" msgstr "%" @@ -125,7 +124,7 @@ msgid_plural "%1% (%2$d shells)" msgstr[0] "%1% (%2$d シェル)" #. TRN Remove/Delete -#: src/slic3r/GUI/Tab.cpp:3752 +#: src/slic3r/GUI/Tab.cpp:3750 #, boost-format msgid "%1% Preset" msgstr "プリセット%1%" @@ -144,7 +143,7 @@ msgstr "" msgid "%1% was substituted with %2%" msgstr "%1%は%2%に置き換えられました" -#: src/slic3r/GUI/MainFrame.cpp:1710 +#: src/slic3r/GUI/MainFrame.cpp:1714 #, boost-format msgid "%1% was successfully sliced." msgstr "%1%は正常にスライスされました。" @@ -164,7 +163,7 @@ msgstr "フィラメント速度%3.2f mm/sで%3.2f mm³/ s。" msgid "%d lines: %.2f mm" msgstr "%dライン:%.2f mm" -#: src/slic3r/GUI/MainFrame.cpp:1875 +#: src/slic3r/GUI/MainFrame.cpp:1879 #, c-format, boost-format msgid "%d presets successfully imported." msgstr "%d プリセットを正常にインポートしました。" @@ -328,7 +327,7 @@ msgstr "" msgid "&About %s" msgstr "%sについて(&A)" -#: src/slic3r/GUI/MainFrame.cpp:1433 +#: src/slic3r/GUI/MainFrame.cpp:1437 msgid "&Collapse Sidebar" msgstr "サイドバーを折りたたむ&C" @@ -340,27 +339,27 @@ msgstr "構成(&C)" msgid "&Configuration Snapshots" msgstr "構成スナップショット(&C)" -#: src/slic3r/GUI/MainFrame.cpp:1345 +#: src/slic3r/GUI/MainFrame.cpp:1349 msgid "&Copy" msgstr "コピー(&C)" -#: src/slic3r/GUI/MainFrame.cpp:1329 +#: src/slic3r/GUI/MainFrame.cpp:1333 msgid "&Delete Selected" msgstr "選択を削除&D" -#: src/slic3r/GUI/MainFrame.cpp:1454 +#: src/slic3r/GUI/MainFrame.cpp:1458 msgid "&Edit" msgstr "編集(&E)" -#: src/slic3r/GUI/MainFrame.cpp:1266 +#: src/slic3r/GUI/MainFrame.cpp:1270 msgid "&Export" msgstr "エクスポート(&E)" -#: src/slic3r/GUI/MainFrame.cpp:1381 src/slic3r/GUI/MainFrame.cpp:1585 +#: src/slic3r/GUI/MainFrame.cpp:1385 src/slic3r/GUI/MainFrame.cpp:1589 msgid "&Filament Settings Tab" msgstr "フィラメント設定タブ(&F)" -#: src/slic3r/GUI/MainFrame.cpp:1453 src/slic3r/GUI/MainFrame.cpp:1556 +#: src/slic3r/GUI/MainFrame.cpp:1457 src/slic3r/GUI/MainFrame.cpp:1560 msgid "&File" msgstr "ファイル(&F)" @@ -368,19 +367,19 @@ msgstr "ファイル(&F)" msgid "&Finish" msgstr "終了(&F)" -#: src/slic3r/GUI/MainFrame.cpp:1438 +#: src/slic3r/GUI/MainFrame.cpp:1442 msgid "&Fullscreen" msgstr "フルスクリーン&F" -#: src/slic3r/GUI/MainFrame.cpp:1304 +#: src/slic3r/GUI/MainFrame.cpp:1308 msgid "&G-code Preview" msgstr "Gコードプレビュー&G" -#: src/slic3r/GUI/MainFrame.cpp:1459 src/slic3r/GUI/MainFrame.cpp:1560 +#: src/slic3r/GUI/MainFrame.cpp:1463 src/slic3r/GUI/MainFrame.cpp:1564 msgid "&Help" msgstr "ヘルプ(&H)" -#: src/slic3r/GUI/MainFrame.cpp:1227 +#: src/slic3r/GUI/MainFrame.cpp:1231 msgid "&Import" msgstr "インポート(&I)" @@ -388,7 +387,7 @@ msgstr "インポート(&I)" msgid "&Language" msgstr "言語(&L)" -#: src/slic3r/GUI/MainFrame.cpp:1145 +#: src/slic3r/GUI/MainFrame.cpp:1149 msgid "&New Project" msgstr "新しいプロジェクト(&N)" @@ -396,19 +395,19 @@ msgstr "新しいプロジェクト(&N)" msgid "&Next >" msgstr "次 >(&N)" -#: src/slic3r/GUI/MainFrame.cpp:1521 +#: src/slic3r/GUI/MainFrame.cpp:1525 msgid "&Open G-code" msgstr "Gコードのオープン(&O)" -#: src/slic3r/GUI/MainFrame.cpp:1148 +#: src/slic3r/GUI/MainFrame.cpp:1152 msgid "&Open Project" msgstr "プロジェクトのオープン(&O)" -#: src/slic3r/GUI/MainFrame.cpp:1348 +#: src/slic3r/GUI/MainFrame.cpp:1352 msgid "&Paste" msgstr "貼り付け(&P)" -#: src/slic3r/GUI/MainFrame.cpp:1373 +#: src/slic3r/GUI/MainFrame.cpp:1377 msgid "&Plater Tab" msgstr "プレートタブ(&P)" @@ -416,35 +415,35 @@ msgstr "プレートタブ(&P)" msgid "&Preferences" msgstr "環境設定(&P)" -#: src/slic3r/GUI/MainFrame.cpp:1307 src/slic3r/GUI/MainFrame.cpp:1541 +#: src/slic3r/GUI/MainFrame.cpp:1311 src/slic3r/GUI/MainFrame.cpp:1545 msgid "&Quit" msgstr "中止(&Q)" -#: src/slic3r/GUI/MainFrame.cpp:1340 +#: src/slic3r/GUI/MainFrame.cpp:1344 msgid "&Redo" msgstr "再実行(&R)" -#: src/slic3r/GUI/MainFrame.cpp:1300 +#: src/slic3r/GUI/MainFrame.cpp:1304 msgid "&Repair STL file" msgstr "STLファイルの修復(&R)" -#: src/slic3r/GUI/MainFrame.cpp:1190 +#: src/slic3r/GUI/MainFrame.cpp:1194 msgid "&Save Project" msgstr "プロジェクトを保存(&S)" -#: src/slic3r/GUI/MainFrame.cpp:1322 +#: src/slic3r/GUI/MainFrame.cpp:1326 msgid "&Select All" msgstr "全て選択&S" -#: src/slic3r/GUI/MainFrame.cpp:1337 +#: src/slic3r/GUI/MainFrame.cpp:1341 msgid "&Undo" msgstr "元に戻す(&U)" -#: src/slic3r/GUI/MainFrame.cpp:1456 src/slic3r/GUI/MainFrame.cpp:1557 +#: src/slic3r/GUI/MainFrame.cpp:1460 src/slic3r/GUI/MainFrame.cpp:1561 msgid "&View" msgstr "ビュー(&V)" -#: src/slic3r/GUI/MainFrame.cpp:1455 +#: src/slic3r/GUI/MainFrame.cpp:1459 msgid "&Window" msgstr "ウィンドウ(&W)" @@ -453,7 +452,7 @@ msgstr "ウィンドウ(&W)" msgid "(All)" msgstr "(全て)" -#: src/slic3r/GUI/MainFrame.cpp:1296 +#: src/slic3r/GUI/MainFrame.cpp:1300 msgid "(Re)Slice No&w" msgstr "(再)スライス実行(&w)" @@ -465,7 +464,7 @@ msgstr "スライス" msgid "(Some lines not shown)" msgstr "(一部のラインは表示されていません)" -#: src/libslic3r/PrintConfig.cpp:1088 src/libslic3r/PrintConfig.cpp:3532 +#: src/libslic3r/PrintConfig.cpp:1088 src/libslic3r/PrintConfig.cpp:3505 msgid "(Unknown)" msgstr "(不明)" @@ -477,7 +476,7 @@ msgstr "(スプールを含む)" msgid "(minimum)" msgstr "(最小)" -#: src/slic3r/GUI/MainFrame.cpp:1628 +#: src/slic3r/GUI/MainFrame.cpp:1632 msgid ") not found." msgstr ")見つかりません。" @@ -533,7 +532,7 @@ msgstr "2mm" msgid "3 (heavy)" msgstr "3(重い)" -#: src/slic3r/GUI/MainFrame.cpp:1391 +#: src/slic3r/GUI/MainFrame.cpp:1395 msgid "3&D" msgstr "3&D" @@ -620,7 +619,7 @@ msgstr "" "一般的には、PLAでは60℃、ABSでは110℃です。 ヒートベッドがないプリンタではゼロ" "を入力します。" -#: src/libslic3r/PrintConfig.cpp:3917 +#: src/libslic3r/PrintConfig.cpp:3890 msgid "" "A slower printing profile might be necessary when using materials with " "higher viscosity or with some hollowed parts. It slows down the tilt " @@ -646,7 +645,7 @@ msgstr "APIキー" msgid "Abort" msgstr "中止" -#: src/slic3r/GUI/AboutDialog.cpp:210 +#: src/slic3r/GUI/AboutDialog.cpp:212 #, c-format, boost-format msgid "About %s" msgstr "%sについて" @@ -663,7 +662,7 @@ msgstr "加速度コントロール (上級者向け)" msgid "Access violation" msgstr "アクセス違反" -#: src/libslic3r/PrintConfig.cpp:3891 +#: src/libslic3r/PrintConfig.cpp:3864 msgid "Accuracy" msgstr "精度" @@ -703,7 +702,7 @@ msgstr "" "て、プリント時間を短縮し、フィラメントの消費量を減らすことができることをご存" "知ですか? 詳細については、ドキュメントをご覧ください。" -#: src/slic3r/GUI/GalleryDialog.cpp:117 src/slic3r/GUI/Tab.cpp:4151 +#: src/slic3r/GUI/GalleryDialog.cpp:117 src/slic3r/GUI/Tab.cpp:4149 msgid "Add" msgstr "追加" @@ -766,7 +765,7 @@ msgstr "ギャラリーからシェイプを追加" msgid "Add Shapes from Gallery" msgstr "ギャラリーからシェイプを追加" -#: src/libslic3r/PrintConfig.cpp:3748 +#: src/libslic3r/PrintConfig.cpp:3721 msgid "Add a pad underneath the supported model" msgstr "サポートされているモデルの下にパッドを追加します" @@ -883,7 +882,7 @@ msgid "Add pause print" msgstr "プリントの一時停止を追加" #: src/slic3r/GUI/PresetComboBoxes.cpp:702 -#: src/slic3r/GUI/PresetComboBoxes.cpp:742 src/slic3r/GUI/Tab.cpp:3247 +#: src/slic3r/GUI/PresetComboBoxes.cpp:742 src/slic3r/GUI/Tab.cpp:3245 msgid "Add physical printer" msgstr "物理プリンターを追加する" @@ -983,17 +982,16 @@ msgstr "アドレス" #: src/slic3r/GUI/GUI_Factories.cpp:138 src/slic3r/GUI/Tab.cpp:1491 #: src/slic3r/GUI/Tab.cpp:1525 src/slic3r/GUI/Tab.cpp:1642 -#: src/slic3r/GUI/Tab.cpp:1646 src/slic3r/GUI/Tab.cpp:2030 -#: src/slic3r/GUI/Tab.cpp:2401 src/slic3r/GUI/Tab.cpp:4728 +#: src/slic3r/GUI/Tab.cpp:1646 src/slic3r/GUI/Tab.cpp:2028 +#: src/slic3r/GUI/Tab.cpp:2399 src/slic3r/GUI/Tab.cpp:4726 #: src/libslic3r/PrintConfig.cpp:259 src/libslic3r/PrintConfig.cpp:494 #: src/libslic3r/PrintConfig.cpp:1414 src/libslic3r/PrintConfig.cpp:1501 #: src/libslic3r/PrintConfig.cpp:1548 src/libslic3r/PrintConfig.cpp:2498 #: src/libslic3r/PrintConfig.cpp:2508 src/libslic3r/PrintConfig.cpp:3046 -#: src/libslic3r/PrintConfig.cpp:3080 src/libslic3r/PrintConfig.cpp:3090 -#: src/libslic3r/PrintConfig.cpp:3105 src/libslic3r/PrintConfig.cpp:3118 -#: src/libslic3r/PrintConfig.cpp:3127 src/libslic3r/PrintConfig.cpp:3142 -#: src/libslic3r/PrintConfig.cpp:3157 src/libslic3r/PrintConfig.cpp:3168 -#: src/libslic3r/PrintConfig.cpp:3365 +#: src/libslic3r/PrintConfig.cpp:3081 src/libslic3r/PrintConfig.cpp:3092 +#: src/libslic3r/PrintConfig.cpp:3107 src/libslic3r/PrintConfig.cpp:3120 +#: src/libslic3r/PrintConfig.cpp:3129 src/libslic3r/PrintConfig.cpp:3141 +#: src/libslic3r/PrintConfig.cpp:3338 msgid "Advanced" msgstr "上級者向け" @@ -1023,16 +1021,16 @@ msgstr "" "にこの量の材料をワイプタワーに試し出しすることで、インフィルまたは犠牲オブ" "ジェクトを確実に形成します。" -#: src/slic3r/GUI/Tab.cpp:2440 src/libslic3r/GCode.cpp:710 +#: src/slic3r/GUI/Tab.cpp:2438 src/libslic3r/GCode.cpp:710 #: src/libslic3r/PrintConfig.cpp:1602 msgid "After layer change G-code" msgstr "レイヤーチェンジ後のGコード" -#: src/libslic3r/PrintConfig.cpp:4466 +#: src/libslic3r/PrintConfig.cpp:4441 msgid "Align XY" msgstr "XYで整列" -#: src/libslic3r/PrintConfig.cpp:4467 +#: src/libslic3r/PrintConfig.cpp:4442 msgid "Align the model to the given point." msgstr "モデルを指定されたポイントに合わせます。" @@ -1045,7 +1043,7 @@ msgid "Aligned Rectilinear" msgstr "整列された直線" #: src/slic3r/GUI/ConfigWizard.cpp:331 src/slic3r/GUI/ConfigWizard.cpp:651 -#: src/slic3r/GUI/Preferences.cpp:434 src/slic3r/GUI/Tab.cpp:3832 +#: src/slic3r/GUI/Preferences.cpp:434 src/slic3r/GUI/Tab.cpp:3830 #: src/slic3r/GUI/UnsavedChangesDialog.cpp:1154 msgid "All" msgstr "全て" @@ -1243,7 +1241,7 @@ msgstr "カラーの変更を自動的に適用する" msgid "Apply to all the remaining small objects being loaded." msgstr "ロードされている残りのすべての小さなオブジェクトに適用します。" -#: src/libslic3r/PrintConfig.cpp:3074 +#: src/libslic3r/PrintConfig.cpp:3075 msgid "Arachne" msgstr "アラクネ" @@ -1255,7 +1253,7 @@ msgstr "アラクネ境界線生成" msgid "Archimedean Chords" msgstr "アルキメデスコード" -#: src/slic3r/GUI/Tab.cpp:3747 +#: src/slic3r/GUI/Tab.cpp:3745 #, boost-format msgid "Are you sure you want to %1% the selected preset?" msgstr "%1%のプリセットを選択してよろしいですか?" @@ -1272,7 +1270,7 @@ msgstr "" msgid "Are you sure you want to continue?" msgstr "続行しますか?" -#: src/slic3r/GUI/Tab.cpp:3715 +#: src/slic3r/GUI/Tab.cpp:3713 #, boost-format msgid "" "Are you sure you want to delete \"%1%\" preset from the physical printer " @@ -1284,7 +1282,7 @@ msgstr "物理プリンタ\"%2%\"から\"%1%\"プリセットを削除しても msgid "Are you sure you want to delete \"%1%\" printer?" msgstr "\"%1%\"プリンタを削除してもよろしいですか?" -#: src/slic3r/GUI/Tab.cpp:4159 +#: src/slic3r/GUI/Tab.cpp:4157 msgid "Are you sure you want to delete all substitutions?" msgstr "本当にすべての置換を削除しますか?" @@ -1292,7 +1290,7 @@ msgstr "本当にすべての置換を削除しますか?" msgid "Are you sure you want to do it?" msgstr "実行してもよろしいですか?" -#: src/libslic3r/PrintConfig.cpp:3316 +#: src/libslic3r/PrintConfig.cpp:3289 msgid "Area fill" msgstr "領域塗りつぶし" @@ -1323,7 +1321,7 @@ msgstr "" "アレンジアイコンを右クリックして、オブジェクト間のギャップのサイズを調" "整したり、自動回転を許可したりできることをご存知ですか?" -#: src/libslic3r/PrintConfig.cpp:4517 +#: src/libslic3r/PrintConfig.cpp:4492 msgid "" "Arrange the supplied models in a plate and merge them in a single model in " "order to perform actions once." @@ -1473,7 +1471,7 @@ msgstr "サポートポイントの自動生成" msgid "Autogeneration will erase all manually edited points." msgstr "自動生成は、マニュアルで編集されたすべてのポイントを消去します。" -#: src/slic3r/GUI/Tab.cpp:4699 +#: src/slic3r/GUI/Tab.cpp:4697 msgid "Automatic generation" msgstr "自動生成" @@ -1481,7 +1479,7 @@ msgstr "自動生成" msgid "Automatic updates" msgstr "自動アップデート" -#: src/slic3r/GUI/MainFrame.cpp:1300 +#: src/slic3r/GUI/MainFrame.cpp:1304 msgid "Automatically repair an STL file" msgstr "STLファイルの自動修復" @@ -1497,11 +1495,11 @@ msgstr "外周をまたがないようにする" msgid "Avoid crossing perimeters - Max detour length" msgstr "境界線をまたがないようにする-最大迂回長" -#: src/slic3r/GUI/Tab.cpp:4336 +#: src/slic3r/GUI/Tab.cpp:4334 msgid "BACK ARROW" msgstr "戻る矢印" -#: src/slic3r/GUI/Tab.cpp:4358 +#: src/slic3r/GUI/Tab.cpp:4356 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" @@ -1513,7 +1511,7 @@ msgstr "" "クリックすると、現在の設定グループのすべての設定が最後に保存されたプリセット" "に戻されます。" -#: src/slic3r/GUI/Tab.cpp:4372 +#: src/slic3r/GUI/Tab.cpp:4370 msgid "" "BACK ARROW icon indicates that the value was changed and is not equal to the " "last saved preset.\n" @@ -1527,7 +1525,7 @@ msgstr "" msgid "Background processing" msgstr "バックグラウンドで実行中" -#: src/libslic3r/PrintConfig.cpp:4571 +#: src/libslic3r/PrintConfig.cpp:4546 msgid "Bail out on unknown configuration values" msgstr "不明な構成値を回避する" @@ -1535,7 +1533,7 @@ msgstr "不明な構成値を回避する" msgid "Balanced" msgstr "バランスのとれた" -#: src/slic3r/GUI/Tab.cpp:1996 +#: src/slic3r/GUI/Tab.cpp:1994 msgid "Bed" msgstr "ベッド" @@ -1583,7 +1581,7 @@ msgstr "" "最初のレイヤー以降のレイヤーのベッド温度。 ベッド温度制御コマンドを無効にする" "には、これをゼロに設定します。" -#: src/slic3r/GUI/Tab.cpp:2430 src/libslic3r/GCode.cpp:709 +#: src/slic3r/GUI/Tab.cpp:2428 src/libslic3r/GCode.cpp:709 #: src/libslic3r/PrintConfig.cpp:424 msgid "Before layer change G-code" msgstr "レイヤー変更前のGコード" @@ -1608,7 +1606,7 @@ msgstr "ベストな表面仕上がり" msgid "Between objects G-code" msgstr "オブジェクト間のGコード" -#: src/slic3r/GUI/Tab.cpp:2460 src/libslic3r/GCode.cpp:712 +#: src/slic3r/GUI/Tab.cpp:2458 src/libslic3r/GCode.cpp:712 msgid "Between objects G-code (for sequential printing)" msgstr "オブジェクト間のGコード(シーケンシャルプリントの場合)" @@ -1631,11 +1629,11 @@ msgstr "ブロックサポート" msgid "Block supports by angle" msgstr "角度によるブロックサポート" -#: src/libslic3r/PrintConfig.cpp:3413 src/libslic3r/PrintConfig.cpp:3414 +#: src/libslic3r/PrintConfig.cpp:3386 src/libslic3r/PrintConfig.cpp:3387 msgid "Bottle volume" msgstr "ボトル容量" -#: src/libslic3r/PrintConfig.cpp:3420 src/libslic3r/PrintConfig.cpp:3421 +#: src/libslic3r/PrintConfig.cpp:3393 src/libslic3r/PrintConfig.cpp:3394 msgid "Bottle weight" msgstr "ボトル重量" @@ -1652,7 +1650,7 @@ msgstr "最下層" msgid "Bottom" msgstr "ボトム" -#: src/slic3r/GUI/MainFrame.cpp:1124 +#: src/slic3r/GUI/MainFrame.cpp:1128 msgid "Bottom View" msgstr "下面表示" @@ -1927,8 +1925,8 @@ msgstr "" "サポートポイントなしでは続行できません! サポートポイントを追加するか、サポー" "ト生成を無効にします。" -#: src/slic3r/GUI/Tab.cpp:2299 src/slic3r/GUI/UnsavedChangesDialog.cpp:1287 -#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1674 +#: src/slic3r/GUI/Tab.cpp:2297 src/slic3r/GUI/UnsavedChangesDialog.cpp:1287 +#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1677 msgid "Capabilities" msgstr "オプション" @@ -1936,7 +1934,7 @@ msgstr "オプション" msgid "Capture a configuration snapshot" msgstr "構成スナップショットをキャプチャーする" -#: src/slic3r/GUI/Tab.cpp:4048 +#: src/slic3r/GUI/Tab.cpp:4046 msgid "Case insensitive" msgstr "大文字・小文字の区別なし" @@ -1944,11 +1942,11 @@ msgstr "大文字・小文字の区別なし" msgid "Category" msgstr "カテゴリー" -#: src/libslic3r/PrintConfig.cpp:4493 +#: src/libslic3r/PrintConfig.cpp:4468 msgid "Center" msgstr "中心" -#: src/libslic3r/PrintConfig.cpp:4494 +#: src/libslic3r/PrintConfig.cpp:4469 msgid "Center the print around the given center." msgstr "指定されたポイントを中心にプリントを配置します。" @@ -2054,7 +2052,7 @@ msgstr "SLAアーカイブを選択します:" msgid "Choose a file to import bed texture from (PNG/SVG):" msgstr "ベッドのイメージファイルを選択(PNG/SVG):" -#: src/slic3r/GUI/MainFrame.cpp:1609 +#: src/slic3r/GUI/MainFrame.cpp:1613 msgid "Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):" msgstr "スライスするファイルを選択(STL/OBJ/AMF/3MF/PRUSA):" @@ -2100,7 +2098,7 @@ msgstr "円" msgid "Circular" msgstr "円形" -#: src/libslic3r/PrintConfig.cpp:3073 +#: src/libslic3r/PrintConfig.cpp:3074 msgid "Classic" msgstr "クラシック" @@ -2108,11 +2106,12 @@ msgstr "クラシック" msgid "" "Classic perimeter generator produces perimeters with constant extrusion " "width and for very thin areas is used gap-fill. Arachne engine produces " -"perimeters with variable extrusion width." +"perimeters with variable extrusion width. This setting also affects the " +"Concentric infill." msgstr "" "クラシックの境界線生成は、一定の押し出し幅の境界線を生成し、非常に薄い領域に" "はギャップフィルが使用されます。アラクネ・エンジンは、押し出し幅が変化する境" -"界線を生成します。" +"界線を生成します。この設定は、Concentric infillにも影響します。" #: src/slic3r/GUI/Preferences.cpp:295 msgid "Clear Undo / Redo stack on new project" @@ -2191,7 +2190,7 @@ msgid "Closing PrusaSlicer. Current project is modified." msgstr "PrusaSlicerを閉じます。 現在のプロジェクトが変更されました。" #: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:34 -#: src/libslic3r/PrintConfig.cpp:3900 +#: src/libslic3r/PrintConfig.cpp:3873 msgid "Closing distance" msgstr "閉鎖距離" @@ -2199,7 +2198,7 @@ msgstr "閉鎖距離" msgid "Closing radius" msgstr "閉半径" -#: src/slic3r/GUI/MainFrame.cpp:1433 src/slic3r/GUI/Plater.cpp:2320 +#: src/slic3r/GUI/MainFrame.cpp:1437 src/slic3r/GUI/Plater.cpp:2320 msgid "Collapse sidebar" msgstr "サイドバーを折りたたむ" @@ -2207,11 +2206,11 @@ msgstr "サイドバーを折りたたむ" msgid "Collapse/Expand the sidebar" msgstr "サイドバーを折りたたむ/展開する" -#: src/libslic3r/PrintConfig.cpp:889 src/libslic3r/PrintConfig.cpp:3388 +#: src/libslic3r/PrintConfig.cpp:889 src/libslic3r/PrintConfig.cpp:3361 msgid "Color" msgstr "色" -#: src/slic3r/GUI/Tab.cpp:2470 src/libslic3r/GCode.cpp:713 +#: src/slic3r/GUI/Tab.cpp:2468 src/libslic3r/GCode.cpp:713 msgid "Color Change G-code" msgstr "カラーチェンジGコード" @@ -2275,12 +2274,12 @@ msgstr "コマンド" msgid "Comment:" msgstr "コメント:" -#: src/slic3r/GUI/MainFrame.cpp:1420 -#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1563 +#: src/slic3r/GUI/MainFrame.cpp:1424 +#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1566 msgid "Compare Presets" msgstr "プリセットの比較" -#: src/slic3r/GUI/MainFrame.cpp:1420 +#: src/slic3r/GUI/MainFrame.cpp:1424 msgid "Compare presets" msgstr "プリセットを比較する" @@ -2288,7 +2287,7 @@ msgstr "プリセットを比較する" msgid "Compare this preset with some another" msgstr "このプリセットを別のプリセットと比較する" -#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1638 +#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1641 msgid "Compared presets has different printer technology" msgstr "比較されたプリセットには異なるプリンターテクノロジーがあります" @@ -2450,7 +2449,7 @@ msgstr "" "長くはなりません。 アンカーを無効にするには、このパラメーターをゼロに設定しま" "す。" -#: src/slic3r/GUI/Tab.cpp:4694 +#: src/slic3r/GUI/Tab.cpp:4692 msgid "Connection of the support sticks and junctions" msgstr "サポートスティックとジャンクションの接続" @@ -2504,7 +2503,7 @@ msgstr "続行して構成の更新をインストールしますか?" msgid "Continue to activate a configuration snapshot %1%?" msgstr "構成スナップショット%1%のアクティブ化を続行しますか?" -#: src/slic3r/GUI/AboutDialog.cpp:272 +#: src/slic3r/GUI/AboutDialog.cpp:274 msgid "" "Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, " "Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and " @@ -2522,7 +2521,7 @@ msgstr "" "Vojtech Bubnik, Enrico Turri, Oleksandra Iushchenko, Tamas Meszaros, Lukas " "Matena, Vojtech Kral, David Kocik 他、多くの方々による貢献。" -#: src/libslic3r/PrintConfig.cpp:3625 +#: src/libslic3r/PrintConfig.cpp:3598 msgid "" "Controls the bridge type between two neighboring pillars. Can be zig-zag, " "cross (double zig-zag) or dynamic which will automatically switch between " @@ -2540,7 +2539,7 @@ msgstr "インチ単位から変換する " msgid "Convert from meters" msgstr "メートルから換算" -#: src/slic3r/GUI/Tab.cpp:2001 +#: src/slic3r/GUI/Tab.cpp:1999 msgid "Cooling" msgstr "クーリング" @@ -2552,7 +2551,7 @@ msgstr "クーリング動作はこのスピードから徐々に加速します msgid "Cooling moves are gradually accelerating towards this speed." msgstr "冷却動作は、この速度に向かって徐々に加速しています。" -#: src/slic3r/GUI/Tab.cpp:2025 +#: src/slic3r/GUI/Tab.cpp:2023 msgid "Cooling thresholds" msgstr "クーリングしきい値" @@ -2581,7 +2580,7 @@ msgstr "コピー" msgid "Copy Version Info" msgstr "バージョン情報のコピー" -#: src/slic3r/GUI/MainFrame.cpp:1346 +#: src/slic3r/GUI/MainFrame.cpp:1350 msgid "Copy selection to clipboard" msgstr "選択をクリップボードにコピー" @@ -2642,32 +2641,32 @@ msgstr "" "ある可能性があります。もう一度エクスポートするか、別のデバイスを使用してみて" "ください。 破損した出力Gコードは%1%.tmpにあります。" -#: src/slic3r/GUI/AboutDialog.cpp:139 src/slic3r/GUI/AboutDialog.cpp:267 +#: src/slic3r/GUI/AboutDialog.cpp:141 src/slic3r/GUI/AboutDialog.cpp:269 msgid "Copyright" msgstr "コピーライト" -#: src/libslic3r/PrintConfig.cpp:3495 src/libslic3r/PrintConfig.cpp:3496 +#: src/libslic3r/PrintConfig.cpp:3468 src/libslic3r/PrintConfig.cpp:3469 msgid "Correction for expansion" msgstr "膨張補正" -#: src/libslic3r/PrintConfig.cpp:3502 src/libslic3r/PrintConfig.cpp:3503 +#: src/libslic3r/PrintConfig.cpp:3475 src/libslic3r/PrintConfig.cpp:3476 msgid "Correction for expansion in X axis" msgstr "X軸膨張の補正" -#: src/libslic3r/PrintConfig.cpp:3509 src/libslic3r/PrintConfig.cpp:3510 +#: src/libslic3r/PrintConfig.cpp:3482 src/libslic3r/PrintConfig.cpp:3483 msgid "Correction for expansion in Y axis" msgstr "Y軸膨張の補正" -#: src/libslic3r/PrintConfig.cpp:3516 src/libslic3r/PrintConfig.cpp:3517 +#: src/libslic3r/PrintConfig.cpp:3489 src/libslic3r/PrintConfig.cpp:3490 msgid "Correction for expansion in Z axis" msgstr "Z軸膨張の補正" -#: src/slic3r/GUI/Tab.cpp:2547 src/slic3r/GUI/Tab.cpp:4574 +#: src/slic3r/GUI/Tab.cpp:2545 src/slic3r/GUI/Tab.cpp:4572 msgid "Corrections" msgstr "補正" #: src/slic3r/GUI/Plater.cpp:1411 src/libslic3r/PrintConfig.cpp:1067 -#: src/libslic3r/PrintConfig.cpp:3434 src/libslic3r/PrintConfig.cpp:3435 +#: src/libslic3r/PrintConfig.cpp:3407 src/libslic3r/PrintConfig.cpp:3408 msgid "Cost" msgstr "費用" @@ -2740,7 +2739,7 @@ msgstr "" "埋められます。 ギャップを閉じることで、最終的なプリント解像度が低下する可能性" "があるため、この値は適度に小さくすることをお勧めします。" -#: src/libslic3r/PrintConfig.cpp:3823 +#: src/libslic3r/PrintConfig.cpp:3796 msgid "Create pad around object and ignore the support elevation" msgstr "" "オブジェクトの周りにパッドを作成し、サポートでオブジェクトを上げることを無視" @@ -2758,7 +2757,7 @@ msgstr "一部のプリセットを変更しながら、新しいプロジェク msgid "Creating a new project while the current project is modified." msgstr "現在のプロジェクトを変更しながら、新しいプロジェクトを作成します。" -#: src/libslic3r/PrintConfig.cpp:3690 +#: src/libslic3r/PrintConfig.cpp:3663 msgid "Critical angle" msgstr "限界角" @@ -2766,7 +2765,7 @@ msgstr "限界角" msgid "Critical error" msgstr "重大なエラー" -#: src/libslic3r/PrintConfig.cpp:3634 +#: src/libslic3r/PrintConfig.cpp:3607 msgid "Cross" msgstr "クロス" @@ -2811,8 +2810,8 @@ msgstr "" "HTTPS OctoPrint接続用にカスタムCA証明書ファイルをcrt/pem形式で指定できます。 " "空白のままにすると、デフォルトのOS CA証明書リポジトリが使用されます。" -#: src/slic3r/GUI/Tab.cpp:2088 src/slic3r/GUI/Tab.cpp:2409 -#: src/slic3r/GUI/Tab.cpp:4263 src/libslic3r/GCode.cpp:733 +#: src/slic3r/GUI/Tab.cpp:2086 src/slic3r/GUI/Tab.cpp:2407 +#: src/slic3r/GUI/Tab.cpp:4261 src/libslic3r/GCode.cpp:733 #: src/libslic3r/PrintConfig.cpp:2468 msgid "Custom G-code" msgstr "カスタムGコード" @@ -2856,7 +2855,7 @@ msgid "Custom template (\"%1%\")" msgstr "カスタムテンプレート(\"%1%\")" #: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:50 -#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:160 src/libslic3r/PrintConfig.cpp:4471 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:160 src/libslic3r/PrintConfig.cpp:4446 msgid "Cut" msgstr "カット" @@ -2864,7 +2863,7 @@ msgstr "カット" msgid "Cut by Plane" msgstr "面でカット" -#: src/libslic3r/PrintConfig.cpp:4472 +#: src/libslic3r/PrintConfig.cpp:4447 msgid "Cut model at the given Z." msgstr "指定されたZでモデルをカットします。" @@ -2872,7 +2871,7 @@ msgstr "指定されたZでモデルをカットします。" msgid "Cylinder" msgstr "シリンダー" -#: src/slic3r/GUI/MainFrame.cpp:1325 +#: src/slic3r/GUI/MainFrame.cpp:1329 msgid "D&eselect All" msgstr "選択を全てクリア&e" @@ -2880,7 +2879,7 @@ msgstr "選択を全てクリア&e" msgid "Dark mode (experimental)" msgstr "ダークモード(実験的)" -#: src/libslic3r/PrintConfig.cpp:4598 +#: src/libslic3r/PrintConfig.cpp:4573 msgid "Data directory" msgstr "データディレクトリー" @@ -2906,7 +2905,7 @@ msgstr "インスタンスを減らす" msgid "Default" msgstr "デフォルト" -#: src/libslic3r/PrintConfig.cpp:3536 src/libslic3r/PrintConfig.cpp:3547 +#: src/libslic3r/PrintConfig.cpp:3509 src/libslic3r/PrintConfig.cpp:3520 msgid "Default SLA material profile" msgstr "デフォルトのSLA材料プロファイル" @@ -2946,8 +2945,8 @@ msgstr "" msgid "Default print profile" msgstr "デフォルトプリントプロファイル" -#: src/libslic3r/PrintConfig.cpp:652 src/libslic3r/PrintConfig.cpp:3537 -#: src/libslic3r/PrintConfig.cpp:3548 +#: src/libslic3r/PrintConfig.cpp:652 src/libslic3r/PrintConfig.cpp:3510 +#: src/libslic3r/PrintConfig.cpp:3521 msgid "" "Default print profile associated with the current printer profile. On " "selection of the current printer profile, this print profile will be " @@ -2961,7 +2960,7 @@ msgstr "" msgid "Define a custom printer profile" msgstr "カスタムプリンタープロファイルを定義する" -#: src/libslic3r/PrintConfig.cpp:3764 +#: src/libslic3r/PrintConfig.cpp:3737 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 " @@ -2979,11 +2978,11 @@ msgstr "アンロードした後の待ち時間" #: src/slic3r/GUI/GalleryDialog.cpp:118 src/slic3r/GUI/GalleryDialog.cpp:508 #: src/slic3r/GUI/GLCanvas3D.cpp:4490 src/slic3r/GUI/GUI_Factories.cpp:444 -#: src/slic3r/GUI/Tab.cpp:3750 +#: src/slic3r/GUI/Tab.cpp:3748 msgid "Delete" msgstr "削除" -#: src/slic3r/GUI/MainFrame.cpp:1332 +#: src/slic3r/GUI/MainFrame.cpp:1336 msgid "Delete &All" msgstr "全て削除&A" @@ -3037,7 +3036,7 @@ msgid "Delete Subobject" msgstr "サブオブジェクト削除" #: src/slic3r/GUI/GLCanvas3D.cpp:4499 src/slic3r/GUI/KBShortcutsDialog.cpp:96 -#: src/slic3r/GUI/Plater.cpp:5496 src/slic3r/GUI/Tab.cpp:4157 +#: src/slic3r/GUI/Plater.cpp:5496 src/slic3r/GUI/Tab.cpp:4155 msgid "Delete all" msgstr "全て削除" @@ -3094,16 +3093,16 @@ msgstr "マーカーの削除-マウスの左ボタンまたは「-」キー" msgid "Delete tool change" msgstr "ツールチェンジを削除" -#: src/slic3r/GUI/MainFrame.cpp:1333 +#: src/slic3r/GUI/MainFrame.cpp:1337 msgid "Deletes all objects" msgstr "全てのオブジェクトを削除" -#: src/slic3r/GUI/MainFrame.cpp:1330 +#: src/slic3r/GUI/MainFrame.cpp:1334 msgid "Deletes the current selection" msgstr "現在の選択を削除します" -#: src/libslic3r/PrintConfig.cpp:1024 src/libslic3r/PrintConfig.cpp:3427 -#: src/libslic3r/PrintConfig.cpp:3428 +#: src/libslic3r/PrintConfig.cpp:1024 src/libslic3r/PrintConfig.cpp:3400 +#: src/libslic3r/PrintConfig.cpp:3401 msgid "Density" msgstr "密度" @@ -3115,9 +3114,9 @@ msgstr "0%-100%の範囲で表される内部インフィルの密度。" msgid "Density of the first raft or support layer." msgstr "1層目のラフトもしくはサポート層の密度" -#: src/slic3r/GUI/Tab.cpp:1730 src/slic3r/GUI/Tab.cpp:2117 -#: src/slic3r/GUI/Tab.cpp:2504 src/slic3r/GUI/Tab.cpp:2577 -#: src/slic3r/GUI/Tab.cpp:4592 src/slic3r/GUI/Tab.cpp:4739 +#: src/slic3r/GUI/Tab.cpp:1728 src/slic3r/GUI/Tab.cpp:2115 +#: src/slic3r/GUI/Tab.cpp:2502 src/slic3r/GUI/Tab.cpp:2575 +#: src/slic3r/GUI/Tab.cpp:4590 src/slic3r/GUI/Tab.cpp:4737 msgid "Dependencies" msgstr "依存関係" @@ -3141,7 +3140,7 @@ msgstr "全ての選択解除" msgid "Deselect by rectangle" msgstr "方形で選択解除" -#: src/slic3r/GUI/MainFrame.cpp:1326 +#: src/slic3r/GUI/MainFrame.cpp:1330 msgid "Deselects all objects" msgstr "全てのオブジェクトの選択解除" @@ -3176,7 +3175,7 @@ msgstr "システムプリセットから取り外す" msgid "Detach preset" msgstr "プリセットを切り離す" -#: src/slic3r/GUI/Tab.cpp:3624 +#: src/slic3r/GUI/Tab.cpp:3622 msgid "Detached" msgstr "取り外しました" @@ -3200,7 +3199,7 @@ msgstr "" msgid "Detect thin walls" msgstr "薄壁を検知" -#: src/libslic3r/PrintConfig.cpp:4546 +#: src/libslic3r/PrintConfig.cpp:4521 msgid "" "Detect unconnected parts in the given model(s) and split them into separate " "objects." @@ -3224,15 +3223,15 @@ msgstr "デバイス:" msgid "Diameter" msgstr "直径" -#: src/libslic3r/PrintConfig.cpp:3660 +#: src/libslic3r/PrintConfig.cpp:3633 msgid "Diameter in mm of the pillar base" msgstr "ピラーベースの直径(mm)" -#: src/libslic3r/PrintConfig.cpp:3595 +#: src/libslic3r/PrintConfig.cpp:3568 msgid "Diameter in mm of the support pillars" msgstr "サポートピラーの直径(mm)" -#: src/libslic3r/PrintConfig.cpp:3567 +#: src/libslic3r/PrintConfig.cpp:3540 msgid "Diameter of the pointing side of the head" msgstr "サポート先端の直径" @@ -3293,31 +3292,31 @@ msgstr "すべてのカスタム変更を破棄" msgid "Discard changes" msgstr "変更取りやめ" -#: src/slic3r/GUI/Tab.cpp:2524 +#: src/slic3r/GUI/Tab.cpp:2522 msgid "Display" msgstr "ディスプレイ" -#: src/libslic3r/PrintConfig.cpp:3243 +#: src/libslic3r/PrintConfig.cpp:3216 msgid "Display height" msgstr "ディスプレイの高さ" -#: src/libslic3r/PrintConfig.cpp:3262 +#: src/libslic3r/PrintConfig.cpp:3235 msgid "Display horizontal mirroring" msgstr "水平ディスプレイミラーリング" -#: src/libslic3r/PrintConfig.cpp:3276 +#: src/libslic3r/PrintConfig.cpp:3249 msgid "Display orientation" msgstr "ディスプレイの向き" -#: src/slic3r/GUI/MainFrame.cpp:1412 +#: src/slic3r/GUI/MainFrame.cpp:1416 msgid "Display the Print Host Upload Queue window" msgstr "プリントサーバーのアップロードキュー画面を表示する" -#: src/libslic3r/PrintConfig.cpp:3269 +#: src/libslic3r/PrintConfig.cpp:3242 msgid "Display vertical mirroring" msgstr "垂直ミラーリングを表示する" -#: src/libslic3r/PrintConfig.cpp:3237 +#: src/libslic3r/PrintConfig.cpp:3210 msgid "Display width" msgstr "画面の幅" @@ -3336,7 +3335,7 @@ msgstr "" "スカートとブリム(ドラフトシールドを使用しない場合)またはオブジェクトの間の距" "離。" -#: src/libslic3r/PrintConfig.cpp:3848 +#: src/libslic3r/PrintConfig.cpp:3821 msgid "" "Distance between two connector sticks which connect the object and the " "generated pad." @@ -3373,11 +3372,11 @@ msgstr "プレートの自動配置機能で使用される距離。" msgid "Divide by zero" msgstr "#DIV/0!" -#: src/libslic3r/PrintConfig.cpp:4560 +#: src/libslic3r/PrintConfig.cpp:4535 msgid "Do not fail if a file supplied to --load does not exist." msgstr "存在しない読込みが行われても提供されたファイルは失敗させない。" -#: src/libslic3r/PrintConfig.cpp:4499 +#: src/libslic3r/PrintConfig.cpp:4474 msgid "" "Do not rearrange the given models before merging and keep their original XY " "coordinates." @@ -3430,7 +3429,7 @@ msgstr "これらのプリンターモデルにデフォルトのSLAマテリア msgid "Do you want to select default filaments for these FFF printer models?" msgstr "これらのFFFプリンターモデルのデフォルトフィラメントを選択しますか?" -#: src/libslic3r/PrintConfig.cpp:4498 +#: src/libslic3r/PrintConfig.cpp:4473 msgid "Don't arrange" msgstr "整列させない" @@ -3494,11 +3493,11 @@ msgstr "" msgid "Drop to bed" msgstr "ベッドに落とす" -#: src/libslic3r/PrintConfig.cpp:4507 +#: src/libslic3r/PrintConfig.cpp:4482 msgid "Duplicate" msgstr "複製" -#: src/libslic3r/PrintConfig.cpp:4512 +#: src/libslic3r/PrintConfig.cpp:4487 msgid "Duplicate by grid" msgstr "グリッドで複製" @@ -3515,11 +3514,11 @@ msgstr "他のレイヤーの間、ファンは常に%1%%%で動作します " msgid "During the other layers, fan will be turned off." msgstr "他のレイヤーでは、ファンはオフになります。" -#: src/libslic3r/PrintConfig.cpp:3635 +#: src/libslic3r/PrintConfig.cpp:3608 msgid "Dynamic" msgstr "動的" -#: src/slic3r/GUI/MainFrame.cpp:1582 +#: src/slic3r/GUI/MainFrame.cpp:1586 msgid "E&xport" msgstr "エクスポート(&x)" @@ -3570,7 +3569,7 @@ msgstr "カスタムG-コードの編集" msgid "Edit pause print message" msgstr "一時停止メッセージを編集" -#: src/slic3r/GUI/PresetComboBoxes.cpp:730 src/slic3r/GUI/Tab.cpp:3247 +#: src/slic3r/GUI/PresetComboBoxes.cpp:730 src/slic3r/GUI/Tab.cpp:3245 msgid "Edit physical printer" msgstr "物理プリンターの編集" @@ -3594,7 +3593,7 @@ msgstr "編集中" msgid "Eigen vectorization supported:" msgstr "固有値ベクトル化に対応:" -#: src/slic3r/GUI/MainFrame.cpp:1268 +#: src/slic3r/GUI/MainFrame.cpp:1272 msgid "Ejec&t SD Card / Flash Drive" msgstr "SDカード/USBメモリーの取り出し&t" @@ -3602,7 +3601,7 @@ msgstr "SDカード/USBメモリーの取り出し&t" msgid "Eject SD card / Flash drive" msgstr "SDカード/USBメモリーを取り出す" -#: src/slic3r/GUI/MainFrame.cpp:1268 +#: src/slic3r/GUI/MainFrame.cpp:1272 msgid "Eject SD card / Flash drive after the G-code was exported to it." msgstr "Gコードをエクスポートした後に、SDカード/USBメモリーを取り出します。" @@ -3619,7 +3618,7 @@ msgstr "デバイス %s(%s) の取り出しに失敗しました。" msgid "Elephant foot compensation" msgstr "最初の層の広がり補正" -#: src/libslic3r/PrintConfig.cpp:3364 +#: src/libslic3r/PrintConfig.cpp:3337 msgid "Elephant foot minimum width" msgstr "エレファントフット(最下層がちょっと太る)の最小幅" @@ -3652,7 +3651,7 @@ msgstr "Gコードに送信 " msgid "Empty layer between %1% and %2%." msgstr "%1%と%2%.の間の空レイヤー。" -#: src/slic3r/GUI/Tab.cpp:2003 src/libslic3r/PrintConfig.cpp:1912 +#: src/slic3r/GUI/Tab.cpp:2001 src/libslic3r/PrintConfig.cpp:1912 #: src/libslic3r/PrintConfig.cpp:2962 msgid "Enable" msgstr "有効" @@ -3669,11 +3668,11 @@ msgstr "ダークモードを有効にする" msgid "Enable fan if layer print time is below" msgstr "レイヤーのプリント時間がこれ以下の場合にファンをオンにします" -#: src/libslic3r/PrintConfig.cpp:3874 +#: src/libslic3r/PrintConfig.cpp:3847 msgid "Enable hollowing" msgstr "くり抜きを有効にする" -#: src/libslic3r/PrintConfig.cpp:3264 +#: src/libslic3r/PrintConfig.cpp:3237 msgid "Enable horizontal mirroring of output images" msgstr "出力画像の水平ミラーリングを有効にします" @@ -3688,7 +3687,7 @@ msgstr "" "表面を滑らかにするために、プリントヘッドを使ったトップ表面のアイロンがけを有" "効にする " -#: src/libslic3r/PrintConfig.cpp:4573 +#: src/libslic3r/PrintConfig.cpp:4548 msgid "" "Enable reading unknown configuration values by silently substituting them " "with defaults." @@ -3696,7 +3695,7 @@ msgstr "" "不明な構成値をデフォルトでサイレントに置き換えることにより、それらの値の読み" "取りを有効にします。" -#: src/libslic3r/PrintConfig.cpp:4572 +#: src/libslic3r/PrintConfig.cpp:4547 msgid "" "Enable reading unknown configuration values by verbosely substituting them " "with defaults." @@ -3747,7 +3746,7 @@ msgstr "" msgid "Enable variable layer height feature" msgstr "可変レイヤー高さ機能を有効にする" -#: src/libslic3r/PrintConfig.cpp:3271 +#: src/libslic3r/PrintConfig.cpp:3244 msgid "Enable vertical mirroring of output images" msgstr "出力イメージの垂直ミラーリングをオンにします" @@ -3763,7 +3762,7 @@ msgstr "" "境界線と境界線の間、および最内境界線とインフィルの間の隙間を埋めることが可能" "です。" -#: src/slic3r/GUI/Tab.cpp:2099 src/slic3r/GUI/Tab.cpp:2420 +#: src/slic3r/GUI/Tab.cpp:2097 src/slic3r/GUI/Tab.cpp:2418 #: src/libslic3r/GCode.cpp:708 src/libslic3r/PrintConfig.cpp:684 #: src/libslic3r/PrintConfig.cpp:694 msgid "End G-code" @@ -3795,7 +3794,7 @@ msgstr "強制サポート" msgid "Enqueued" msgstr "キュー追加済み" -#: src/libslic3r/PrintConfig.cpp:4502 +#: src/libslic3r/PrintConfig.cpp:4477 msgid "Ensure on bed" msgstr "ベッド上で確認" @@ -3907,7 +3906,7 @@ msgstr "" "エラー: \"%2%\"" #: src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp:157 -#: src/slic3r/GUI/MainFrame.cpp:1165 src/slic3r/GUI/MainFrame.cpp:1622 +#: src/slic3r/GUI/MainFrame.cpp:1169 src/slic3r/GUI/MainFrame.cpp:1626 #: src/slic3r/GUI/PrintHostDialogs.cpp:373 msgid "Error" msgstr "エラー" @@ -4053,23 +4052,23 @@ msgstr "エキスパートモード" msgid "Export" msgstr "エクスポート" -#: src/slic3r/GUI/MainFrame.cpp:1257 +#: src/slic3r/GUI/MainFrame.cpp:1261 msgid "Export &Config" msgstr "設定のエクスポート(&C)" -#: src/slic3r/GUI/MainFrame.cpp:1230 src/slic3r/GUI/MainFrame.cpp:1582 +#: src/slic3r/GUI/MainFrame.cpp:1234 src/slic3r/GUI/MainFrame.cpp:1586 msgid "Export &G-code" msgstr "Gコードのエクスポート(&G)" -#: src/slic3r/GUI/MainFrame.cpp:1253 src/slic3r/GUI/MainFrame.cpp:1534 +#: src/slic3r/GUI/MainFrame.cpp:1257 src/slic3r/GUI/MainFrame.cpp:1538 msgid "Export &Toolpaths as OBJ" msgstr "ツールパスをOBJとしてエクスポート&T" -#: src/libslic3r/PrintConfig.cpp:4401 +#: src/libslic3r/PrintConfig.cpp:4376 msgid "Export 3MF" msgstr "3MFのエクスポート" -#: src/libslic3r/PrintConfig.cpp:4406 +#: src/libslic3r/PrintConfig.cpp:4381 msgid "Export AMF" msgstr "AMFのエクスポート" @@ -4077,11 +4076,11 @@ msgstr "AMFのエクスポート" msgid "Export AMF file:" msgstr "AMFファイルのエクスポート:" -#: src/slic3r/GUI/MainFrame.cpp:1260 +#: src/slic3r/GUI/MainFrame.cpp:1264 msgid "Export Config &Bundle" msgstr "設定とバンドルのエクスポート(&B)" -#: src/slic3r/GUI/MainFrame.cpp:1263 +#: src/slic3r/GUI/MainFrame.cpp:1267 msgid "Export Config Bundle With Physical Printers" msgstr "物理プリンター情報を含んだ構成セットのエクスポート " @@ -4090,15 +4089,15 @@ msgid "Export G-Code." msgstr "Gコードのエクスポート。" #: src/slic3r/GUI/KBShortcutsDialog.cpp:87 src/slic3r/GUI/Plater.cpp:913 -#: src/slic3r/GUI/Plater.cpp:6530 src/libslic3r/PrintConfig.cpp:4416 +#: src/slic3r/GUI/Plater.cpp:6530 src/libslic3r/PrintConfig.cpp:4391 msgid "Export G-code" msgstr "Gコードのエクスポート" -#: src/slic3r/GUI/MainFrame.cpp:1238 +#: src/slic3r/GUI/MainFrame.cpp:1242 msgid "Export G-code to SD Card / Flash Drive" msgstr "GコードをSDカード/USBメモリーにエクスポートする" -#: src/libslic3r/PrintConfig.cpp:4383 +#: src/libslic3r/PrintConfig.cpp:4358 msgid "Export OBJ" msgstr "OBJのエクスポート" @@ -4106,19 +4105,19 @@ msgstr "OBJのエクスポート" msgid "Export OBJ file:" msgstr "OBJファイルのエクスポート :" -#: src/slic3r/GUI/MainFrame.cpp:1242 +#: src/slic3r/GUI/MainFrame.cpp:1246 msgid "Export Plate as &STL" msgstr "プレートを&STLとしてエクスポート" -#: src/slic3r/GUI/MainFrame.cpp:1245 +#: src/slic3r/GUI/MainFrame.cpp:1249 msgid "Export Plate as STL &Including Supports" msgstr "サポートを含むSTLとしてプレートをエクスポート&I" -#: src/libslic3r/PrintConfig.cpp:4395 +#: src/libslic3r/PrintConfig.cpp:4370 msgid "Export SLA" msgstr "SLAのエクスポート" -#: src/libslic3r/PrintConfig.cpp:4411 +#: src/libslic3r/PrintConfig.cpp:4386 msgid "Export STL" msgstr "STLのエクスポート" @@ -4126,11 +4125,11 @@ msgstr "STLのエクスポート" msgid "Export STL file:" msgstr "STLファイルのエクスポート :" -#: src/slic3r/GUI/MainFrame.cpp:1263 +#: src/slic3r/GUI/MainFrame.cpp:1267 msgid "Export all presets including physical printers to file" msgstr "物理プリンターを含むすべてのプリセットをファイルにエクスポートする" -#: src/slic3r/GUI/MainFrame.cpp:1260 +#: src/slic3r/GUI/MainFrame.cpp:1264 msgid "Export all presets to file" msgstr "すべてのプリセットをファイルにエクスポートします" @@ -4142,23 +4141,23 @@ msgstr "STLとしてエクスポート" msgid "Export config" msgstr "構成のエクスポート" -#: src/slic3r/GUI/MainFrame.cpp:1257 +#: src/slic3r/GUI/MainFrame.cpp:1261 msgid "Export current configuration to file" msgstr "現在の構成をファイルにエクスポート" -#: src/slic3r/GUI/MainFrame.cpp:1230 +#: src/slic3r/GUI/MainFrame.cpp:1234 msgid "Export current plate as G-code" msgstr "現在のプレートをGコードとしてエクスポート" -#: src/slic3r/GUI/MainFrame.cpp:1238 +#: src/slic3r/GUI/MainFrame.cpp:1242 msgid "Export current plate as G-code to SD card / Flash drive" msgstr "現在のプレートをGコードとしてSDカード/USBメモリーにエクスポート" -#: src/slic3r/GUI/MainFrame.cpp:1242 +#: src/slic3r/GUI/MainFrame.cpp:1246 msgid "Export current plate as STL" msgstr "現在のプレートをSTLとしてエクスポート" -#: src/slic3r/GUI/MainFrame.cpp:1245 +#: src/slic3r/GUI/MainFrame.cpp:1249 msgid "Export current plate as STL including supports" msgstr "サポートを含むSTLとして現在のプレートをエクスポート" @@ -4176,19 +4175,19 @@ msgstr "3MFの一時ファイルのエクスポートに失敗しました" msgid "Export sources full pathnames to 3mf and amf" msgstr "ソースのフルパス名を3mfおよびamfにエクスポートする" -#: src/libslic3r/PrintConfig.cpp:4402 +#: src/libslic3r/PrintConfig.cpp:4377 msgid "Export the model(s) as 3MF." msgstr "モデルを3MFとしてエクスポートします。" -#: src/libslic3r/PrintConfig.cpp:4407 +#: src/libslic3r/PrintConfig.cpp:4382 msgid "Export the model(s) as AMF." msgstr "モデルをAMF形式でエクスポート。" -#: src/libslic3r/PrintConfig.cpp:4384 +#: src/libslic3r/PrintConfig.cpp:4359 msgid "Export the model(s) as OBJ." msgstr "モデルをOBJとしてエクスポート。" -#: src/libslic3r/PrintConfig.cpp:4412 +#: src/libslic3r/PrintConfig.cpp:4387 msgid "Export the model(s) as STL." msgstr "STLとしてモデルをエクスポート。" @@ -4196,7 +4195,7 @@ msgstr "STLとしてモデルをエクスポート。" msgid "Export to SD card / Flash drive" msgstr "SDカード/Flashドライブにエクスポート" -#: src/slic3r/GUI/MainFrame.cpp:1253 src/slic3r/GUI/MainFrame.cpp:1534 +#: src/slic3r/GUI/MainFrame.cpp:1257 src/slic3r/GUI/MainFrame.cpp:1538 msgid "Export toolpaths as OBJ" msgstr "ツールパスをOBJとしてエクスポート" @@ -4208,7 +4207,7 @@ msgstr "エクスポート。" msgid "Exporting G-code" msgstr "Gコードのエクスポート" -#: src/slic3r/GUI/MainFrame.cpp:1812 +#: src/slic3r/GUI/MainFrame.cpp:1816 msgid "Exporting configuration bundle" msgstr "構成バンドルのエクスポート" @@ -4229,11 +4228,11 @@ msgstr "エクスポート中。" msgid "Exposition time is out of printer profile bounds." msgstr "露光時間がプリンタープロファイルの範囲外です。" -#: src/slic3r/GUI/Tab.cpp:2560 src/slic3r/GUI/Tab.cpp:4570 +#: src/slic3r/GUI/Tab.cpp:2558 src/slic3r/GUI/Tab.cpp:4568 msgid "Exposure" msgstr "露光" -#: src/libslic3r/PrintConfig.cpp:3465 src/libslic3r/PrintConfig.cpp:3466 +#: src/libslic3r/PrintConfig.cpp:3438 src/libslic3r/PrintConfig.cpp:3439 msgid "Exposure time" msgstr "露光時間" @@ -4295,7 +4294,7 @@ msgstr "エクストルーダーカラー" msgid "Extruder changed to" msgstr "エクストルーダーを変更" -#: src/slic3r/GUI/Tab.cpp:1687 +#: src/slic3r/GUI/Tab.cpp:1685 msgid "Extruder clearance" msgstr "エクストルーダーのクリアランス" @@ -4304,7 +4303,7 @@ msgid "Extruder offset" msgstr "エクストルーダーのオフセット" #: src/slic3r/GUI/GUI_Factories.cpp:133 src/slic3r/GUI/Tab.cpp:1620 -#: src/slic3r/GUI/Tab.cpp:2303 src/libslic3r/PrintConfig.cpp:792 +#: src/slic3r/GUI/Tab.cpp:2301 src/libslic3r/PrintConfig.cpp:792 #: src/libslic3r/PrintConfig.cpp:1465 src/libslic3r/PrintConfig.cpp:1965 #: src/libslic3r/PrintConfig.cpp:2341 src/libslic3r/PrintConfig.cpp:2615 #: src/libslic3r/PrintConfig.cpp:2643 @@ -4312,7 +4311,7 @@ msgid "Extruders" msgstr "エクストルーダー" #: src/slic3r/GUI/UnsavedChangesDialog.cpp:1283 -#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1670 +#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1673 msgid "Extruders count" msgstr "エクストルーダーの数" @@ -4352,7 +4351,7 @@ msgstr "FFF型プリンター" msgid "Facets" msgstr "面" -#: src/libslic3r/PrintConfig.cpp:3441 +#: src/libslic3r/PrintConfig.cpp:3414 msgid "Faded layers" msgstr "初期露出レイヤー" @@ -4381,11 +4380,11 @@ msgstr "モデルへの穴あけに失敗しました" msgid "Fan Speed (%)" msgstr "ファンスピード(%)" -#: src/slic3r/GUI/Tab.cpp:2014 +#: src/slic3r/GUI/Tab.cpp:2012 msgid "Fan settings" msgstr "ファン設定" -#: src/slic3r/GUI/GUI_Preview.cpp:221 src/slic3r/GUI/Tab.cpp:2015 +#: src/slic3r/GUI/GUI_Preview.cpp:221 src/slic3r/GUI/Tab.cpp:2013 msgid "Fan speed" msgstr "ファンスピード" @@ -4418,11 +4417,11 @@ msgstr "ファンは常に%1%%%で動作します " msgid "Fan will be turned off." msgstr "ファンがオフになります。" -#: src/libslic3r/PrintConfig.cpp:3289 src/libslic3r/PrintConfig.cpp:3924 +#: src/libslic3r/PrintConfig.cpp:3262 src/libslic3r/PrintConfig.cpp:3897 msgid "Fast" msgstr "早い" -#: src/libslic3r/PrintConfig.cpp:3290 +#: src/libslic3r/PrintConfig.cpp:3263 msgid "Fast tilt" msgstr "早いチルト" @@ -4446,7 +4445,7 @@ msgstr "射出の種類" #: src/slic3r/GUI/GCodeViewer.cpp:3700 src/slic3r/GUI/GCodeViewer.cpp:3726 #: src/slic3r/GUI/GUI.cpp:339 src/slic3r/GUI/Plater.cpp:819 -#: src/slic3r/GUI/Tab.cpp:1969 src/slic3r/GUI/Tab.cpp:1970 +#: src/slic3r/GUI/Tab.cpp:1967 src/slic3r/GUI/Tab.cpp:1968 msgid "Filament" msgstr "フィラメント" @@ -4458,7 +4457,7 @@ msgstr "フィラメント径:" msgid "Filament End G-code" msgstr "フィラメントエンドG-Code" -#: src/slic3r/GUI/Tab.cpp:1868 +#: src/slic3r/GUI/Tab.cpp:1866 msgid "Filament Overrides" msgstr "フィラメント上書き" @@ -4467,7 +4466,7 @@ msgid "Filament Profiles Selection" msgstr "フィラメントプロファイルの選択" #: src/slic3r/GUI/MainFrame.cpp:286 src/slic3r/GUI/MainFrame.cpp:337 -#: src/slic3r/GUI/MainFrame.cpp:2099 src/slic3r/GUI/MainFrame.cpp:2100 +#: src/slic3r/GUI/MainFrame.cpp:2103 src/slic3r/GUI/MainFrame.cpp:2104 #: src/slic3r/GUI/Tab.hpp:461 msgid "Filament Settings" msgstr "フィラメント設定" @@ -4509,7 +4508,7 @@ msgstr "フィラメントメモ" msgid "Filament parking position" msgstr "フィラメント待避ポジション" -#: src/slic3r/GUI/Tab.cpp:2031 +#: src/slic3r/GUI/Tab.cpp:2029 msgid "Filament properties" msgstr "フィラメント特性" @@ -4526,7 +4525,7 @@ msgstr "フィラメントアンロード時間" msgid "Filaments" msgstr "フィラメント" -#: src/slic3r/GUI/MainFrame.cpp:1629 +#: src/slic3r/GUI/MainFrame.cpp:1633 msgid "File Not Found" msgstr "ファイルが見つかりません" @@ -4594,7 +4593,7 @@ msgstr "ベッドの残りの領域を、選択したオブジェクトのイン msgid "Filling bed" msgstr "ベッドフィリング" -#: src/slic3r/GUI/Tab.cpp:3954 +#: src/slic3r/GUI/Tab.cpp:3952 msgid "Find" msgstr "探す" @@ -4606,11 +4605,11 @@ msgstr "Gコード行のパターンを検索して置換します。" msgid "Finished" msgstr "完了" -#: src/slic3r/GUI/ConfigWizard.cpp:1357 src/slic3r/GUI/Tab.cpp:2365 +#: src/slic3r/GUI/ConfigWizard.cpp:1357 src/slic3r/GUI/Tab.cpp:2363 msgid "Firmware" msgstr "ファームウェア" -#: src/slic3r/GUI/Tab.cpp:3013 +#: src/slic3r/GUI/Tab.cpp:3011 msgid "Firmware Retraction" msgstr "ファームウェア引き込み" @@ -4814,7 +4813,7 @@ msgstr "" "ワイプタワーを可溶性のサポートと連携させるには、サポートレイヤーをオブジェク" "トレイヤーと同期させる必要があります。" -#: src/libslic3r/PrintConfig.cpp:3830 +#: src/libslic3r/PrintConfig.cpp:3803 msgid "Force pad around object everywhere" msgstr "オブジェクト全体にパッドを強制" @@ -4848,7 +4847,7 @@ msgstr "" "Gコードサムネイルのフォーマット。PNGは最高の品質、JPGは最小のサイズ、QOIは低" "メモリのファームウェアのため" -#: src/libslic3r/PrintConfig.cpp:4563 +#: src/libslic3r/PrintConfig.cpp:4538 msgid "" "Forward-compatibility rule when loading configurations from config files and " "project files (3MF, AMF)." @@ -4856,7 +4855,7 @@ msgstr "" "構成ファイルおよびプロジェクトファイル (3MF, AMF)から構成をロードする場合の上" "位互換性ルール。" -#: src/slic3r/GUI/Tab.cpp:1854 +#: src/slic3r/GUI/Tab.cpp:1852 msgid "Found reserved keywords in" msgstr "予約キーワードが見つかりました" @@ -4869,11 +4868,11 @@ msgid "From Object List You can't delete the last solid part from object." msgstr "" "オブジェクトリストからオブジェクトの最後のパートを削除することはできません。" -#: src/slic3r/GUI/MainFrame.cpp:1126 +#: src/slic3r/GUI/MainFrame.cpp:1130 msgid "Front" msgstr "正面" -#: src/slic3r/GUI/MainFrame.cpp:1126 +#: src/slic3r/GUI/MainFrame.cpp:1130 msgid "Front View" msgstr "正面" @@ -4881,7 +4880,7 @@ msgstr "正面" msgid "Full fan speed at layer" msgstr "レイヤーでのフルファン速度" -#: src/slic3r/GUI/MainFrame.cpp:1438 +#: src/slic3r/GUI/MainFrame.cpp:1442 msgid "Fullscreen" msgstr "フルスクリーン" @@ -4929,7 +4928,7 @@ msgstr "ファジースキン厚さ" msgid "Fuzzy skin type." msgstr "ファジースキンタイプ。" -#: src/slic3r/GUI/MainFrame.cpp:1664 +#: src/slic3r/GUI/MainFrame.cpp:1668 msgid "G-code" msgstr "Gコード" @@ -4967,11 +4966,11 @@ msgstr "Gコード置換" msgid "G-code thumbnails" msgstr "Gコードのサムネイル" -#: src/libslic3r/PrintConfig.cpp:4422 +#: src/libslic3r/PrintConfig.cpp:4397 msgid "G-code viewer" msgstr "Gコードビュワー" -#: src/slic3r/GUI/AboutDialog.cpp:270 src/slic3r/GUI/GUI_App.cpp:268 +#: src/slic3r/GUI/AboutDialog.cpp:272 src/slic3r/GUI/GUI_App.cpp:268 msgid "GNU Affero General Public License, version 3" msgstr "GNU Affero General Public License, version 3 (AGPL v3)" @@ -4988,10 +4987,10 @@ msgstr "ギャラリー" msgid "Gap fill" msgstr "ギャップフィル" -#: src/slic3r/GUI/Preferences.cpp:106 src/slic3r/GUI/Tab.cpp:2289 -#: src/slic3r/GUI/Tab.cpp:2516 src/slic3r/GUI/Tab.cpp:2623 +#: src/slic3r/GUI/Preferences.cpp:106 src/slic3r/GUI/Tab.cpp:2287 +#: src/slic3r/GUI/Tab.cpp:2514 src/slic3r/GUI/Tab.cpp:2621 #: src/slic3r/GUI/UnsavedChangesDialog.cpp:1287 -#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1674 +#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1677 msgid "General" msgstr "全般" @@ -5021,11 +5020,11 @@ msgstr "" "ト(ベッド)上のプリント領域(フットプリント)が非常に薄い/不十分なオブジェクトの" "密着力を高めるのに役立ちます。" -#: src/libslic3r/PrintConfig.cpp:3558 +#: src/libslic3r/PrintConfig.cpp:3531 msgid "Generate supports" msgstr "サポート生成" -#: src/libslic3r/PrintConfig.cpp:3560 +#: src/libslic3r/PrintConfig.cpp:3533 msgid "Generate supports for the models" msgstr "モデルのサポートを生成する" @@ -5214,11 +5213,11 @@ msgstr "" msgid "Head diameter" msgstr "先端径" -#: src/libslic3r/PrintConfig.cpp:3574 +#: src/libslic3r/PrintConfig.cpp:3547 msgid "Head penetration" msgstr "サポートチップの貫通" -#: src/slic3r/GUI/ConfigManipulation.cpp:338 +#: src/slic3r/GUI/ConfigManipulation.cpp:336 msgid "Head penetration should not be greater than the head width." msgstr "" "サポートチップの貫通は、サポートチップの厚さを超えないようにしてください。" @@ -5243,7 +5242,7 @@ msgstr "高さ(mm)" msgid "Height of skirt expressed in layers." msgstr "スカートの高さをレイヤーで表現。" -#: src/libslic3r/PrintConfig.cpp:3244 +#: src/libslic3r/PrintConfig.cpp:3217 msgid "Height of the display" msgstr "ディスプレイの高さ" @@ -5268,15 +5267,15 @@ msgstr "" "こんにちは、%sへようこそ! この%sは初期設定に役立ちます。 いくつかの設定を行" "うだけで、プリントの準備ができます。" -#: src/libslic3r/PrintConfig.cpp:4434 +#: src/libslic3r/PrintConfig.cpp:4409 msgid "Help" msgstr "ヘルプ" -#: src/libslic3r/PrintConfig.cpp:4440 +#: src/libslic3r/PrintConfig.cpp:4415 msgid "Help (FFF options)" msgstr "ヘルプ(FFFオプション)" -#: src/libslic3r/PrintConfig.cpp:4445 +#: src/libslic3r/PrintConfig.cpp:4420 msgid "Help (SLA options)" msgstr "ヘルプ(SLAオプション)" @@ -5308,7 +5307,7 @@ msgstr "高い" msgid "High extruder current on filament swap" msgstr "フィラメント交換時の高いエクストルーダー電流" -#: src/libslic3r/PrintConfig.cpp:3307 src/libslic3r/PrintConfig.cpp:3925 +#: src/libslic3r/PrintConfig.cpp:3280 src/libslic3r/PrintConfig.cpp:3898 msgid "High viscosity" msgstr "高粘度" @@ -5340,7 +5339,7 @@ msgstr "穴径" msgid "Hollow and drill" msgstr "くり抜きと穴開け" -#: src/libslic3r/PrintConfig.cpp:3876 +#: src/libslic3r/PrintConfig.cpp:3849 msgid "Hollow out a model to have an empty interior" msgstr "内部を空にするためにモデルをくり抜く" @@ -5348,16 +5347,16 @@ msgstr "内部を空にするためにモデルをくり抜く" msgid "Hollow this object" msgstr "このオブジェクトのくり抜き" -#: src/slic3r/GUI/GUI_Factories.cpp:142 src/slic3r/GUI/Tab.cpp:4721 -#: src/slic3r/GUI/Tab.cpp:4722 src/libslic3r/SLA/Hollowing.cpp:73 +#: src/slic3r/GUI/GUI_Factories.cpp:142 src/slic3r/GUI/Tab.cpp:4719 +#: src/slic3r/GUI/Tab.cpp:4720 src/libslic3r/SLA/Hollowing.cpp:73 #: src/libslic3r/SLA/Hollowing.cpp:85 src/libslic3r/SLA/Hollowing.cpp:105 -#: src/libslic3r/SLA/Hollowing.cpp:114 src/libslic3r/PrintConfig.cpp:3875 -#: src/libslic3r/PrintConfig.cpp:3882 src/libslic3r/PrintConfig.cpp:3892 -#: src/libslic3r/PrintConfig.cpp:3901 +#: src/libslic3r/SLA/Hollowing.cpp:114 src/libslic3r/PrintConfig.cpp:3848 +#: src/libslic3r/PrintConfig.cpp:3855 src/libslic3r/PrintConfig.cpp:3865 +#: src/libslic3r/PrintConfig.cpp:3874 msgid "Hollowing" msgstr "くり抜き" -#: src/libslic3r/PrintConfig.cpp:3903 +#: src/libslic3r/PrintConfig.cpp:3876 msgid "" "Hollowing is done in two steps: first, an imaginary interior is calculated " "deeper (offset plus the closing distance) in the object and then it's " @@ -5424,19 +5423,19 @@ msgstr "" "カーソルをボタンの上に置くと、詳細情報が表示されます。またはこのボタンをク" "リックします。" -#: src/libslic3r/PrintConfig.cpp:3778 +#: src/libslic3r/PrintConfig.cpp:3751 msgid "How far should the pad extend around the contained geometry" msgstr "パッドの形状の周りの幅" -#: src/libslic3r/PrintConfig.cpp:3867 +#: src/libslic3r/PrintConfig.cpp:3840 msgid "How much should the tiny connectors penetrate into the model body." msgstr "小さなコネクターをモデルにどの程度深く入れるか。" -#: src/libslic3r/PrintConfig.cpp:3576 +#: src/libslic3r/PrintConfig.cpp:3549 msgid "How much the pinhead has to penetrate the model surface" msgstr "サポートの先端がモデルの表面をどの程度貫通しているか" -#: src/libslic3r/PrintConfig.cpp:3721 +#: src/libslic3r/PrintConfig.cpp:3694 msgid "" "How much the supports should lift up the supported object. If \"Pad around " "object\" is enabled, this value is ignored." @@ -5649,7 +5648,7 @@ msgid "" msgstr "" "有効にすると、折りたたみサイドバーのボタンが3Dシーンの右上隅に表示されます" -#: src/libslic3r/PrintConfig.cpp:4587 +#: src/libslic3r/PrintConfig.cpp:4562 msgid "" "If enabled, the command line arguments are sent to an existing instance of " "GUI PrusaSlicer, or an existing PrusaSlicer window is activated. Overrides " @@ -5909,7 +5908,7 @@ msgstr "" "接続に失敗した場合は、自己署名証明書に対してこのオプションを有効にすることを" "お勧めします。" -#: src/libslic3r/PrintConfig.cpp:4559 +#: src/libslic3r/PrintConfig.cpp:4534 msgid "Ignore non-existent config files" msgstr "存在しない設定ファイルを無視する" @@ -5927,15 +5926,15 @@ msgstr "違法な指示" msgid "Import" msgstr "インポート" -#: src/slic3r/GUI/MainFrame.cpp:1217 +#: src/slic3r/GUI/MainFrame.cpp:1221 msgid "Import &Config" msgstr "設定のインポート(&C)" -#: src/slic3r/GUI/MainFrame.cpp:1224 +#: src/slic3r/GUI/MainFrame.cpp:1228 msgid "Import Config &Bundle" msgstr "構成バンドルのインポート(&B)" -#: src/slic3r/GUI/MainFrame.cpp:1220 +#: src/slic3r/GUI/MainFrame.cpp:1224 msgid "Import Config from &Project" msgstr "プロジェクトから構成をインポート&P" @@ -5951,7 +5950,7 @@ msgstr "オブジェクトをインポート" msgid "Import Objects" msgstr "オブジェクトのインポート" -#: src/slic3r/GUI/MainFrame.cpp:1212 +#: src/slic3r/GUI/MainFrame.cpp:1216 msgid "Import SL1 / SL1S Archive" msgstr "SL1/SL1Sアーカイブのインポート" @@ -5959,11 +5958,11 @@ msgstr "SL1/SL1Sアーカイブのインポート" msgid "Import SLA archive" msgstr "SLAアーカイブをインポート" -#: src/slic3r/GUI/MainFrame.cpp:1208 +#: src/slic3r/GUI/MainFrame.cpp:1212 msgid "Import STL (Imperial Units)" msgstr "STLのインポート(インチ)" -#: src/slic3r/GUI/MainFrame.cpp:1204 +#: src/slic3r/GUI/MainFrame.cpp:1208 msgid "Import STL/OBJ/AM&F/3MF" msgstr "STL/OBJ/AMF/3MFのインポート(&F)" @@ -6091,11 +6090,11 @@ msgstr "プロファイルを継承" msgid "Initial exposition time is out of printer profile bounds." msgstr "初期露出時間は、プリンタプロファイルの範囲外です。" -#: src/libslic3r/PrintConfig.cpp:3488 src/libslic3r/PrintConfig.cpp:3489 +#: src/libslic3r/PrintConfig.cpp:3461 src/libslic3r/PrintConfig.cpp:3462 msgid "Initial exposure time" msgstr "初期露出時間" -#: src/libslic3r/PrintConfig.cpp:3406 src/libslic3r/PrintConfig.cpp:3407 +#: src/libslic3r/PrintConfig.cpp:3379 src/libslic3r/PrintConfig.cpp:3380 msgid "Initial layer height" msgstr "初期レイヤー高さ" @@ -6198,7 +6197,7 @@ msgstr "内部エラー:%1%" msgid "Internal infill" msgstr "内部のインフィル" -#: src/slic3r/GUI/ConfigManipulation.cpp:340 +#: src/slic3r/GUI/ConfigManipulation.cpp:338 msgid "Invalid Head penetration" msgstr "モデルへの無効なサポートの貫通" @@ -6224,7 +6223,7 @@ msgstr "入力形式が無効です。 次の形式の次元の予想される msgid "Invalid numeric input." msgstr "無効な数値入力。" -#: src/slic3r/GUI/ConfigManipulation.cpp:353 +#: src/slic3r/GUI/ConfigManipulation.cpp:351 msgid "Invalid pinhead diameter" msgstr "無効なピンヘッド径" @@ -6259,11 +6258,11 @@ msgstr "アイロンタイプ" msgid "Is it safe?" msgstr "安全ですか?" -#: src/slic3r/GUI/MainFrame.cpp:1117 +#: src/slic3r/GUI/MainFrame.cpp:1121 msgid "Iso" msgstr "アイソメ" -#: src/slic3r/GUI/MainFrame.cpp:1117 +#: src/slic3r/GUI/MainFrame.cpp:1121 msgid "Iso View" msgstr "アイソメ表示" @@ -6290,7 +6289,7 @@ msgstr "" "メント押出し力を高められます。フィラメントの先端シェイプによりロード時の負荷" "抵抗が増加してしまう場合に有効な機能です。" -#: src/slic3r/GUI/Tab.cpp:3710 +#: src/slic3r/GUI/Tab.cpp:3708 msgid "It's a last preset for this physical printer." msgstr "これは、この物理プリンターの最後のプリセットです。" @@ -6302,7 +6301,7 @@ msgstr "SLAではマルチパートオブジェクトのプリントはできま msgid "It's not possible to delete the last related preset for the printer." msgstr "プリンタの最後の関連プリセットを削除することはできません。" -#: src/slic3r/GUI/Tab.cpp:2673 +#: src/slic3r/GUI/Tab.cpp:2671 msgid "Jerk limits" msgstr "ジャーク(加加速度)限界" @@ -6368,7 +6367,7 @@ msgstr "選択した設定を保持します。" msgid "Keep upper part" msgstr "上側パーツを保持" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:18 src/slic3r/GUI/MainFrame.cpp:1103 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:18 src/slic3r/GUI/MainFrame.cpp:1107 msgid "Keyboard Shortcuts" msgstr "キーボードショートカット" @@ -6376,11 +6375,11 @@ msgstr "キーボードショートカット" msgid "Keyboard shortcuts" msgstr "キーボードショートカット" -#: src/slic3r/GUI/Tab.cpp:4320 +#: src/slic3r/GUI/Tab.cpp:4318 msgid "LOCKED LOCK" msgstr "ロックしたカギ" -#: src/slic3r/GUI/Tab.cpp:4348 +#: src/slic3r/GUI/Tab.cpp:4346 msgid "" "LOCKED LOCK icon indicates that the settings are the same as the system (or " "default) values for the current option group" @@ -6388,7 +6387,7 @@ msgstr "" "ロックされたカギアイコンは、設定が現在のオプショングループのシステム(または" "デフォルト)値と同じであることを示します" -#: src/slic3r/GUI/Tab.cpp:4364 +#: src/slic3r/GUI/Tab.cpp:4362 msgid "" "LOCKED LOCK icon indicates that the value is the same as the system (or " "default) value." @@ -6400,7 +6399,7 @@ msgstr "" msgid "Label objects" msgstr "オブジェクトにラベルを付ける" -#: src/libslic3r/PrintConfig.cpp:3283 +#: src/libslic3r/PrintConfig.cpp:3256 msgid "Landscape" msgstr "横方向" @@ -6440,7 +6439,7 @@ msgstr "" "\n" "レイヤーの高さは0.01にリセットされます。" -#: src/slic3r/GUI/Tab.cpp:2803 +#: src/slic3r/GUI/Tab.cpp:2801 msgid "Layer height limits" msgstr "レイヤー高さ限度" @@ -6448,8 +6447,8 @@ msgstr "レイヤー高さ限度" msgid "Layer range Settings to modify" msgstr "修正するレイヤー範囲の設定" -#: src/slic3r/GUI/ObjectDataViewModel.cpp:105 src/slic3r/GUI/Tab.cpp:4567 -#: src/slic3r/GUI/Tab.cpp:4658 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:105 src/slic3r/GUI/Tab.cpp:4565 +#: src/slic3r/GUI/Tab.cpp:4656 msgid "Layers" msgstr "レイヤー" @@ -6466,7 +6465,7 @@ msgstr "レイヤー" msgid "Layers and Perimeters" msgstr "積層ピッチと外壁の設定" -#: src/slic3r/GUI/Tab.cpp:1447 src/slic3r/GUI/Tab.cpp:4656 +#: src/slic3r/GUI/Tab.cpp:1447 src/slic3r/GUI/Tab.cpp:4654 msgid "Layers and perimeters" msgstr "レイヤーと外周" @@ -6479,15 +6478,15 @@ msgstr "レイアウトオプション" msgid "Leave \"%1%\" enabled" msgstr "\"%1%\" を有効のままにする" -#: src/slic3r/GUI/MainFrame.cpp:1130 +#: src/slic3r/GUI/MainFrame.cpp:1134 msgid "Left" msgstr "左" -#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1517 +#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1520 msgid "Left Preset Value" msgstr "左プリセット値" -#: src/slic3r/GUI/MainFrame.cpp:1130 +#: src/slic3r/GUI/MainFrame.cpp:1134 msgid "Left View" msgstr "左面" @@ -6526,7 +6525,7 @@ msgid "Length of the infill anchor" msgstr "インフィルアンカー長" #. TRN "Slic3r _is licensed under the_ License" -#: src/slic3r/GUI/AboutDialog.cpp:141 +#: src/slic3r/GUI/AboutDialog.cpp:143 msgid "" "License agreements of all following programs (libraries) are part of " "application license agreement" @@ -6538,7 +6537,7 @@ msgstr "" msgid "Lift Z" msgstr "リフトZ" -#: src/libslic3r/PrintConfig.cpp:4503 +#: src/libslic3r/PrintConfig.cpp:4478 msgid "" "Lift the object above the bed when it is partially below. Enabled by " "default, use --no-ensure-on-bed to disable." @@ -6598,19 +6597,19 @@ msgstr "パーツの読込み" msgid "Load Project" msgstr "プロジェクト読込み" -#: src/slic3r/GUI/MainFrame.cpp:1204 +#: src/slic3r/GUI/MainFrame.cpp:1208 msgid "Load a model" msgstr "モデルを読込む" -#: src/slic3r/GUI/MainFrame.cpp:1212 +#: src/slic3r/GUI/MainFrame.cpp:1216 msgid "Load an SL1 / Sl1S archive" msgstr "SL1 / SL1Sアーカイブをロードする" -#: src/slic3r/GUI/MainFrame.cpp:1208 +#: src/slic3r/GUI/MainFrame.cpp:1212 msgid "Load an model saved with imperial units" msgstr "インチで保存されたモデルをロードします " -#: src/libslic3r/PrintConfig.cpp:4599 +#: src/libslic3r/PrintConfig.cpp:4574 msgid "" "Load and store settings at the given directory. This is useful for " "maintaining different profiles or including configurations from a network " @@ -6619,7 +6618,7 @@ msgstr "" "指定されたディレクトリで設定を読込み/保存します。 これは、異なるプロファイル" "を維持したり、ネットワークストレージからの構成を含めたりするのに役立ちます。" -#: src/libslic3r/PrintConfig.cpp:4577 +#: src/libslic3r/PrintConfig.cpp:4552 msgid "Load config file" msgstr "設定ファイルの読込み" @@ -6638,11 +6637,11 @@ msgstr "" "ブ]を使用できます。これにより、ボクセルデータから3Dモデルを再構築することもで" "きます。" -#: src/slic3r/GUI/MainFrame.cpp:1220 +#: src/slic3r/GUI/MainFrame.cpp:1224 msgid "Load configuration from project file" msgstr "プロジェクトファイルから設定を読み込む" -#: src/libslic3r/PrintConfig.cpp:4578 +#: src/libslic3r/PrintConfig.cpp:4553 msgid "" "Load configuration from the specified file. It can be used more than once to " "load options from multiple files." @@ -6650,11 +6649,11 @@ msgstr "" "指定されたファイルから構成をロードします。 複数のファイルからオプションをロー" "ドするために複数回使用できます。" -#: src/slic3r/GUI/MainFrame.cpp:1217 +#: src/slic3r/GUI/MainFrame.cpp:1221 msgid "Load exported configuration file" msgstr "エクスポートされた構成ファイルを読込む" -#: src/slic3r/GUI/MainFrame.cpp:1224 +#: src/slic3r/GUI/MainFrame.cpp:1228 msgid "Load presets from a bundle" msgstr "プリセットをバンドルから読込む" @@ -6686,11 +6685,11 @@ msgstr "構成のロード" msgid "Loading file" msgstr "ファイル読込み中" -#: src/slic3r/GUI/MainFrame.cpp:1845 +#: src/slic3r/GUI/MainFrame.cpp:1849 msgid "Loading of a configuration bundle" msgstr "構成バンドルのロード" -#: src/slic3r/GUI/MainFrame.cpp:1781 +#: src/slic3r/GUI/MainFrame.cpp:1785 msgid "Loading of a configuration file" msgstr "構成ファイルのロード" @@ -6729,7 +6728,7 @@ msgstr "ローカル座標" msgid "Lock supports under new islands" msgstr "新しい台座でのサポートロック" -#: src/libslic3r/PrintConfig.cpp:4602 +#: src/libslic3r/PrintConfig.cpp:4577 msgid "Logging level" msgstr "ログレベル" @@ -6745,7 +6744,7 @@ msgstr "低い" msgid "Lowest Z height" msgstr "最小Z高さ" -#: src/slic3r/GUI/Tab.cpp:2621 src/slic3r/GUI/Tab.cpp:2708 +#: src/slic3r/GUI/Tab.cpp:2619 src/slic3r/GUI/Tab.cpp:2706 #: src/libslic3r/PrintConfig.cpp:1631 src/libslic3r/PrintConfig.cpp:1666 #: src/libslic3r/PrintConfig.cpp:1683 src/libslic3r/PrintConfig.cpp:1700 #: src/libslic3r/PrintConfig.cpp:1716 src/libslic3r/PrintConfig.cpp:1726 @@ -6754,7 +6753,7 @@ msgstr "最小Z高さ" msgid "Machine limits" msgstr "機体の限界" -#: src/slic3r/GUI/Tab.cpp:4298 +#: src/slic3r/GUI/Tab.cpp:4296 msgid "" "Machine limits are not set, therefore the print time estimate may not be " "accurate." @@ -6762,7 +6761,7 @@ msgstr "" "機械の制限が設定されていないため、プリント時間の見積もりが不正確な場合があり" "ます。 " -#: src/slic3r/GUI/Tab.cpp:4294 +#: src/slic3r/GUI/Tab.cpp:4292 msgid "" "Machine limits will NOT be emitted to G-code, however they will be used to " "estimate print time, which may therefore not be accurate as the printer may " @@ -6772,7 +6771,7 @@ msgstr "" "す。したがって、プリンターが異なるマシン制限のセットを使った場合、不正確な予" "測時間になります。" -#: src/slic3r/GUI/Tab.cpp:4291 +#: src/slic3r/GUI/Tab.cpp:4289 msgid "" "Machine limits will be emitted to G-code and used to estimate print time." msgstr "" @@ -6797,20 +6796,20 @@ msgstr "マニュアル編集" msgid "Masked SLA file exported to %1%" msgstr "マスクされたSLAファイルが%1%にエクスポートされました" -#: src/slic3r/GUI/Tab.cpp:4056 +#: src/slic3r/GUI/Tab.cpp:4054 msgid "Match single line" msgstr "1行にマッチする" -#: src/slic3r/GUI/MainFrame.cpp:1585 +#: src/slic3r/GUI/MainFrame.cpp:1589 msgid "Mate&rial Settings Tab" msgstr "材料設定タブ(&r)" -#: src/slic3r/GUI/Tab.cpp:4526 src/slic3r/GUI/Tab.cpp:4528 +#: src/slic3r/GUI/Tab.cpp:4524 src/slic3r/GUI/Tab.cpp:4526 msgid "Material" msgstr "材料" #: src/slic3r/GUI/MainFrame.cpp:286 src/slic3r/GUI/MainFrame.cpp:339 -#: src/slic3r/GUI/MainFrame.cpp:2099 src/slic3r/GUI/MainFrame.cpp:2100 +#: src/slic3r/GUI/MainFrame.cpp:2103 src/slic3r/GUI/MainFrame.cpp:2104 #: src/slic3r/GUI/Tab.hpp:535 msgid "Material Settings" msgstr "材料設定" @@ -6819,7 +6818,7 @@ msgstr "材料設定" msgid "Material Settings Tab" msgstr "材料設定タブ" -#: src/slic3r/GUI/Tab.cpp:4613 src/slic3r/GUI/Tab.cpp:4614 +#: src/slic3r/GUI/Tab.cpp:4611 src/slic3r/GUI/Tab.cpp:4612 msgid "Material printing profile" msgstr "材料プリントプロファイル" @@ -6827,19 +6826,19 @@ msgstr "材料プリントプロファイル" msgid "Max" msgstr "最大" -#: src/libslic3r/PrintConfig.cpp:3700 +#: src/libslic3r/PrintConfig.cpp:3673 msgid "Max bridge length" msgstr "最長ブリッジ長さ" -#: src/libslic3r/PrintConfig.cpp:3614 +#: src/libslic3r/PrintConfig.cpp:3587 msgid "Max bridges on a pillar" msgstr "ピラー上の最大ブリッジ数" -#: src/libslic3r/PrintConfig.cpp:3788 +#: src/libslic3r/PrintConfig.cpp:3761 msgid "Max merge distance" msgstr "最大結合距離" -#: src/libslic3r/PrintConfig.cpp:3709 +#: src/libslic3r/PrintConfig.cpp:3682 msgid "Max pillar linking distance" msgstr "ピラーがリンクする最大距離" @@ -6935,7 +6934,7 @@ msgstr "吸込み中の最大加速度" msgid "Maximum acceleration when retracting (M204 R)" msgstr "リトラクト時の最大加速度(M204 R)" -#: src/slic3r/GUI/Tab.cpp:2664 +#: src/slic3r/GUI/Tab.cpp:2662 msgid "Maximum accelerations" msgstr "最大加速度" @@ -6956,7 +6955,7 @@ msgstr "" "ヤーで個別に実行されるため、目に見えるアーティファクトが生成される可能性があ" "ります。" -#: src/libslic3r/PrintConfig.cpp:3457 src/libslic3r/PrintConfig.cpp:3458 +#: src/libslic3r/PrintConfig.cpp:3430 src/libslic3r/PrintConfig.cpp:3431 msgid "Maximum exposure time" msgstr "最長露光時間" @@ -6992,11 +6991,11 @@ msgstr "Y軸の最大送り速度" msgid "Maximum feedrate of the Z axis" msgstr "Z軸最大送り量" -#: src/slic3r/GUI/Tab.cpp:2659 +#: src/slic3r/GUI/Tab.cpp:2657 msgid "Maximum feedrates" msgstr "最大送り速度" -#: src/libslic3r/PrintConfig.cpp:3480 src/libslic3r/PrintConfig.cpp:3481 +#: src/libslic3r/PrintConfig.cpp:3453 src/libslic3r/PrintConfig.cpp:3454 msgid "Maximum initial exposure time" msgstr "最大初期露光時間" @@ -7036,7 +7035,7 @@ msgstr "Z軸最大ジャーク" msgid "Maximum length of the infill anchor" msgstr "最長インフィルアンカー" -#: src/libslic3r/PrintConfig.cpp:3616 +#: src/libslic3r/PrintConfig.cpp:3589 msgid "" "Maximum number of bridges that can be placed on a pillar. Bridges hold " "support point pinheads and connect to pillars as small branches." @@ -7067,7 +7066,7 @@ msgid "Medium" msgstr "中間" #: src/slic3r/GUI/GUI_Factories.cpp:859 src/slic3r/GUI/GUI_ObjectList.cpp:2133 -#: src/libslic3r/PrintConfig.cpp:4516 +#: src/libslic3r/PrintConfig.cpp:4491 msgid "Merge" msgstr "マージ" @@ -7083,7 +7082,7 @@ msgstr "オブジェクトを1つのマルチパートオブジェクトにマ msgid "Merged" msgstr "マージ" -#: src/libslic3r/PrintConfig.cpp:3649 +#: src/libslic3r/PrintConfig.cpp:3622 msgid "" "Merging bridges or pillars into another pillars can increase the radius. " "Zero means no increase, one means full increase." @@ -7121,7 +7120,7 @@ msgstr "最小" msgid "Min print speed" msgstr "最低プリント速度" -#: src/libslic3r/PrintConfig.cpp:3738 +#: src/libslic3r/PrintConfig.cpp:3711 msgid "Minimal distance of the support points" msgstr "サポートポイントの最小距離" @@ -7162,11 +7161,11 @@ msgstr "" "力以上の情報があります。 単純化しないでファイルの完全な解像度で処理するには、" "ゼロに設定します。" -#: src/libslic3r/PrintConfig.cpp:3449 src/libslic3r/PrintConfig.cpp:3450 +#: src/libslic3r/PrintConfig.cpp:3422 src/libslic3r/PrintConfig.cpp:3423 msgid "Minimum exposure time" msgstr "最短露光時間" -#: src/libslic3r/PrintConfig.cpp:3156 +#: src/libslic3r/PrintConfig.cpp:3128 msgid "Minimum feature size" msgstr "最小フィーチャーサイズ" @@ -7178,15 +7177,15 @@ msgstr "射出中の最小速度" msgid "Minimum feedrate when extruding (M205 S)" msgstr "射出時の最小送り速度(M205 S)" -#: src/slic3r/GUI/Tab.cpp:2679 +#: src/slic3r/GUI/Tab.cpp:2677 msgid "Minimum feedrates" msgstr "最小送り速度" -#: src/libslic3r/PrintConfig.cpp:3472 src/libslic3r/PrintConfig.cpp:3473 +#: src/libslic3r/PrintConfig.cpp:3445 src/libslic3r/PrintConfig.cpp:3446 msgid "Minimum initial exposure time" msgstr "最小初期露光時間" -#: src/libslic3r/PrintConfig.cpp:3167 +#: src/libslic3r/PrintConfig.cpp:3140 msgid "Minimum perimeter width" msgstr "最小境界線幅" @@ -7210,14 +7209,17 @@ msgstr "" msgid "Minimum thickness of a top / bottom shell" msgstr "上部/下部シェルの最小厚" -#: src/libslic3r/PrintConfig.cpp:3158 +#: src/libslic3r/PrintConfig.cpp:3130 msgid "" "Minimum thickness of thin features. Model features that are thinner than " "this value will not be printed, while features thicker than the Minimum " -"feature size will be widened to the Minimum perimeter width." +"feature size will be widened to the Minimum perimeter width. If expressed as " +"a percentage (for example 25%), it will be computed based on the nozzle " +"diameter." msgstr "" "薄いフィーチャーの最小厚み。この値より薄いモデルフィーチャーはプリントされ" -"ず、最小フィーチャーサイズより厚いフィーチャーは最小境界線幅に広げられます。" +"ず、最小フィーチャーサイズより厚いフィーチャーは最小外周幅に広げられます。" +"パーセントで表現される場合(例えば25%)、ノズル径を基に計算されます。" #: src/libslic3r/PrintConfig.cpp:2899 msgid "Minimum top shell thickness" @@ -7240,11 +7242,11 @@ msgstr "最小移動速度" msgid "Minimum travel feedrate (M205 T)" msgstr "最小移動速度 (M205 T)" -#: src/libslic3r/PrintConfig.cpp:3883 +#: src/libslic3r/PrintConfig.cpp:3856 msgid "Minimum wall thickness of a hollowed model." msgstr "くり抜きモデルの最小壁厚" -#: src/libslic3r/PrintConfig.cpp:3366 +#: src/libslic3r/PrintConfig.cpp:3339 msgid "" "Minimum width of features to maintain when doing elephant foot compensation." msgstr "エレファントフットの補正を行うときに維持する外観の最小幅。" @@ -7267,7 +7269,7 @@ msgstr "" msgid "Mirror Object" msgstr "オブジェクトのミラーリング" -#: src/libslic3r/PrintConfig.cpp:3263 +#: src/libslic3r/PrintConfig.cpp:3236 msgid "Mirror horizontally" msgstr "水平にミラーリング" @@ -7287,7 +7289,7 @@ msgstr "選択オブジェクトをY軸に沿ってミラーリング" msgid "Mirror the selected object along the Z axis" msgstr "選択したオブジェクトをZ軸に沿ってミラーリングします" -#: src/libslic3r/PrintConfig.cpp:3270 +#: src/libslic3r/PrintConfig.cpp:3243 msgid "Mirror vertically" msgstr "垂直にミラーリング" @@ -7468,11 +7470,11 @@ msgstr "" "これらは複数のオブジェクトではなく、\n" "複数のパーツからなる単一のオブジェクトとしますか?" -#: src/libslic3r/PrintConfig.cpp:4513 +#: src/libslic3r/PrintConfig.cpp:4488 msgid "Multiply copies by creating a grid." msgstr "グリッドを作成して複数コピーします。" -#: src/libslic3r/PrintConfig.cpp:4508 +#: src/libslic3r/PrintConfig.cpp:4483 msgid "Multiply copies by this factor." msgstr "この係数で複数コピーします。" @@ -7562,7 +7564,7 @@ msgstr "新しいレイアウト、トップメニューの設定ボタンから msgid "New prerelease version %1% is available." msgstr "新しいプレリリースバージョン%1%が利用可能です。" -#: src/slic3r/GUI/Tab.cpp:3318 +#: src/slic3r/GUI/Tab.cpp:3316 msgid "New printer preset selected" msgstr "新しいプリンタプリセットが選択されました" @@ -7622,7 +7624,7 @@ msgstr "オブジェクトのツールパスが生成されませんでした。 msgid "No pad can be generated for this model with the current configuration" msgstr "現在の構成では、このモデルのパッドを生成できません" -#: src/slic3r/GUI/MainFrame.cpp:1621 +#: src/slic3r/GUI/MainFrame.cpp:1625 msgid "No previously sliced file." msgstr "以前にスライスされたファイルはありません。" @@ -7630,7 +7632,7 @@ msgstr "以前にスライスされたファイルはありません。" msgid "No sparse layers (EXPERIMENTAL)" msgstr "スパースレイヤーなし(試用的)" -#: src/libslic3r/PrintConfig.cpp:3740 +#: src/libslic3r/PrintConfig.cpp:3713 msgid "No support points will be placed closer than this threshold." msgstr "このしきい値よりも近くにサポートポイントは配置されません。" @@ -7646,7 +7648,7 @@ msgid "None" msgstr "なし" #: src/slic3r/GUI/Search.cpp:90 src/slic3r/GUI/Search.cpp:336 -#: src/slic3r/GUI/Tab.cpp:2645 +#: src/slic3r/GUI/Tab.cpp:2643 msgid "Normal" msgstr "ノーマル" @@ -7662,7 +7664,7 @@ msgstr "見つかりません:" msgid "Note" msgstr "注意" -#: src/slic3r/GUI/Tab.cpp:3732 +#: src/slic3r/GUI/Tab.cpp:3730 msgid "Note, that the selected preset will be deleted from this printer too." msgid_plural "" "Note, that the selected preset will be deleted from these printers too." @@ -7670,7 +7672,7 @@ msgstr[0] "" "選択したプリセットはこれらのプリンターからも削除されることに注意してくださ" "い。" -#: src/slic3r/GUI/Tab.cpp:3742 +#: src/slic3r/GUI/Tab.cpp:3740 msgid "" "Note, that this printer will be deleted after deleting the selected preset." msgid_plural "" @@ -7679,7 +7681,7 @@ msgstr[0] "" "選択したプリセットを削除すると、これらのプリンターは削除されることに注意して" "ください。" -#: src/slic3r/GUI/Tab.cpp:2256 +#: src/slic3r/GUI/Tab.cpp:2254 msgid "" "Note: All parameters from this group are moved to the Physical Printer " "settings (see changelog).\n" @@ -7728,12 +7730,12 @@ msgstr "注:このプリセットは保存後に置き換えられます" msgid "Note: some shortcuts work in (non)editing mode only." msgstr "注:一部のショートカットは編集モードでは使えません。" -#: src/slic3r/GUI/Tab.cpp:1723 src/slic3r/GUI/Tab.cpp:1724 -#: src/slic3r/GUI/Tab.cpp:2109 src/slic3r/GUI/Tab.cpp:2110 -#: src/slic3r/GUI/Tab.cpp:2497 src/slic3r/GUI/Tab.cpp:2498 -#: src/slic3r/GUI/Tab.cpp:2570 src/slic3r/GUI/Tab.cpp:2571 -#: src/slic3r/GUI/Tab.cpp:3956 src/slic3r/GUI/Tab.cpp:4584 -#: src/slic3r/GUI/Tab.cpp:4585 +#: src/slic3r/GUI/Tab.cpp:1721 src/slic3r/GUI/Tab.cpp:1722 +#: src/slic3r/GUI/Tab.cpp:2107 src/slic3r/GUI/Tab.cpp:2108 +#: src/slic3r/GUI/Tab.cpp:2495 src/slic3r/GUI/Tab.cpp:2496 +#: src/slic3r/GUI/Tab.cpp:2568 src/slic3r/GUI/Tab.cpp:2569 +#: src/slic3r/GUI/Tab.cpp:3954 src/slic3r/GUI/Tab.cpp:4582 +#: src/slic3r/GUI/Tab.cpp:4583 msgid "Notes" msgstr "メモ" @@ -7747,7 +7749,7 @@ msgstr "通知" msgid "Notify about new releases" msgstr "新しいリリースについて通知する" -#: src/slic3r/GUI/Tab.cpp:1991 +#: src/slic3r/GUI/Tab.cpp:1989 msgid "Nozzle" msgstr "ノズル" @@ -7759,7 +7761,7 @@ msgstr "ノズル径:" msgid "Nozzle and Bed Temperatures" msgstr "ノズルとベッド温度" -#: src/slic3r/GUI/Tab.cpp:2337 src/slic3r/GUI/Tab.cpp:2781 +#: src/slic3r/GUI/Tab.cpp:2335 src/slic3r/GUI/Tab.cpp:2779 #: src/libslic3r/PrintConfig.cpp:1876 msgid "Nozzle diameter" msgstr "ノズル径" @@ -7789,7 +7791,7 @@ msgstr "" msgid "Number of cooling moves" msgstr "冷却移動回数" -#: src/slic3r/GUI/Tab.cpp:2304 +#: src/slic3r/GUI/Tab.cpp:2302 msgid "Number of extruders of the printer." msgstr "プリンターのエクストルーダー数。" @@ -7817,15 +7819,15 @@ msgstr "" "されている場合、ループ数はここで設定された値よりも大きくなる場合があります。 " "スカートを完全に無効にするには、これをゼロに設定します。" -#: src/libslic3r/PrintConfig.cpp:3249 +#: src/libslic3r/PrintConfig.cpp:3222 msgid "Number of pixels in" msgstr "ピクセル数" -#: src/libslic3r/PrintConfig.cpp:3251 +#: src/libslic3r/PrintConfig.cpp:3224 msgid "Number of pixels in X" msgstr "Xのピクセル数" -#: src/libslic3r/PrintConfig.cpp:3257 +#: src/libslic3r/PrintConfig.cpp:3230 msgid "Number of pixels in Y" msgstr "Yのピクセル数" @@ -7841,7 +7843,7 @@ msgstr "上部と底部のソリッドレイヤー(塗りつぶし)数。" msgid "Number of solid layers to generate on top surfaces." msgstr "上部に生成するソリッドレイヤー(塗りつぶし)数。" -#: src/libslic3r/PrintConfig.cpp:3442 +#: src/libslic3r/PrintConfig.cpp:3415 msgid "" "Number of the layers needed for the exposure time fade from initial exposure " "time to the exposure time" @@ -7855,7 +7857,7 @@ msgstr "ツールチェンジ回数" msgid "Object Settings to modify" msgstr "オブジェクト設定を変更" -#: src/slic3r/GUI/Tab.cpp:4776 src/libslic3r/PrintConfig.cpp:3719 +#: src/slic3r/GUI/Tab.cpp:4774 src/libslic3r/PrintConfig.cpp:3692 msgid "Object elevation" msgstr "オブジェクトの持ち上げ高" @@ -7969,7 +7971,7 @@ msgstr "垂直スライダーの1レイヤーモードOn/Off" msgid "One layer mode" msgstr "1レイヤーモード" -#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1627 +#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1630 msgid "One of the presets doesn't found" msgstr "プリセットの1つが見つかりません" @@ -7994,7 +7996,7 @@ msgstr "" "す。 最後のオブジェクトを除くすべてを削除するか、「complete_objects」でシーケ" "ンシャルモードを有効にします。 " -#: src/libslic3r/PrintConfig.cpp:2560 src/libslic3r/PrintConfig.cpp:3642 +#: src/libslic3r/PrintConfig.cpp:2560 src/libslic3r/PrintConfig.cpp:3615 msgid "" "Only create support if it lies on a build plate. Don't create support on a " "print." @@ -8006,7 +8008,7 @@ msgstr "" msgid "Only infill where needed" msgstr "必要な場合のみインフィルを付ける" -#: src/slic3r/GUI/Tab.cpp:2814 +#: src/slic3r/GUI/Tab.cpp:2812 msgid "Only lift Z" msgstr "Zをリフト" @@ -8047,7 +8049,7 @@ msgid "Ooze prevention is currently not supported with the wipe tower enabled." msgstr "" "垂れ防止機能は、現在のところ、ワイプタワーを有効にした状態では使えません。" -#: src/slic3r/GUI/MainFrame.cpp:1537 +#: src/slic3r/GUI/MainFrame.cpp:1541 msgid "Open &PrusaSlicer" msgstr "&Prusaスライサーのオープン" @@ -8067,11 +8069,11 @@ msgstr "フォルダーを開きます。" msgid "Open G-code file:" msgstr "開くGコードファイル:" -#: src/slic3r/GUI/MainFrame.cpp:78 src/slic3r/GUI/MainFrame.cpp:1304 +#: src/slic3r/GUI/MainFrame.cpp:78 src/slic3r/GUI/MainFrame.cpp:1308 msgid "Open G-code viewer" msgstr "Gコードビューワーを開く" -#: src/slic3r/GUI/MainFrame.cpp:1416 +#: src/slic3r/GUI/MainFrame.cpp:1420 msgid "Open New Instance" msgstr "新しいインスタンスを開く" @@ -8079,20 +8081,20 @@ msgstr "新しいインスタンスを開く" msgid "Open Preferences." msgstr "設定を開く。" -#: src/slic3r/GUI/MainFrame.cpp:89 src/slic3r/GUI/MainFrame.cpp:1537 +#: src/slic3r/GUI/MainFrame.cpp:89 src/slic3r/GUI/MainFrame.cpp:1541 msgid "Open PrusaSlicer" msgstr "PrusaSlicerを開く" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:205 src/slic3r/GUI/MainFrame.cpp:1521 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:205 src/slic3r/GUI/MainFrame.cpp:1525 msgid "Open a G-code file" msgstr "G-コードファイルを開く" #: src/slic3r/GUI/MainFrame.cpp:75 src/slic3r/GUI/MainFrame.cpp:89 -#: src/slic3r/GUI/MainFrame.cpp:1416 +#: src/slic3r/GUI/MainFrame.cpp:1420 msgid "Open a new PrusaSlicer instance" msgstr "新しいPrusaSlicerインスタンスを開く" -#: src/slic3r/GUI/MainFrame.cpp:1148 +#: src/slic3r/GUI/MainFrame.cpp:1152 msgid "Open a project file" msgstr "プロジェクトファイルを開く" @@ -8134,7 +8136,7 @@ msgstr "ブラウザで%sウェブサイトを開きます" msgid "Open the Prusa3D drivers download page in your browser" msgstr "ブラウザーでPrusa3Dドライバのダウンロードページを開きます" -#: src/slic3r/GUI/MainFrame.cpp:1400 +#: src/slic3r/GUI/MainFrame.cpp:1404 msgid "Open the dialog to modify shape gallery" msgstr "シェイプギャラリーの編集ダイヤログを開く" @@ -8150,7 +8152,7 @@ msgstr "構成ウィザードを開く" msgid "Opening new project while some presets are unsaved." msgstr "一部のプリセットが保存されていないときに新しいプロジェクトを開きます。" -#: src/slic3r/GUI/MainFrame.cpp:1100 +#: src/slic3r/GUI/MainFrame.cpp:1104 msgid "" "Opens Tip of the day notification in bottom right corner or shows another " "tip if already opened." @@ -8225,7 +8227,7 @@ msgstr "オリエンテーション検索がキャンセルされました。" msgid "Origin" msgstr "原点" -#: src/slic3r/GUI/Tab.cpp:1671 src/slic3r/GUI/Tab.cpp:1699 +#: src/slic3r/GUI/Tab.cpp:1671 src/slic3r/GUI/Tab.cpp:1697 msgid "Other" msgstr "その他" @@ -8245,15 +8247,15 @@ msgstr "アウターとインナーブリム" msgid "Outer brim only" msgstr "外側のブリムのみ" -#: src/libslic3r/PrintConfig.cpp:4581 +#: src/libslic3r/PrintConfig.cpp:4556 msgid "Output File" msgstr "ファイル出力" -#: src/libslic3r/PrintConfig.cpp:4450 +#: src/libslic3r/PrintConfig.cpp:4425 msgid "Output Model Info" msgstr "モデル情報のアウトプット" -#: src/slic3r/GUI/Tab.cpp:1692 src/slic3r/GUI/Tab.cpp:4734 +#: src/slic3r/GUI/Tab.cpp:1690 src/slic3r/GUI/Tab.cpp:4732 msgid "Output file" msgstr "出力ファイル" @@ -8261,7 +8263,7 @@ msgstr "出力ファイル" msgid "Output filename format" msgstr "出力ファイル名の形式" -#: src/slic3r/GUI/Tab.cpp:1684 src/slic3r/GUI/Tab.cpp:4733 +#: src/slic3r/GUI/Tab.cpp:1682 src/slic3r/GUI/Tab.cpp:4731 msgid "Output options" msgstr "出力オプション" @@ -8286,19 +8288,19 @@ msgstr "オーバハングのしきい値" msgid "Overlap" msgstr "オーバーラップ" -#: src/slic3r/GUI/MainFrame.cpp:1378 +#: src/slic3r/GUI/MainFrame.cpp:1382 msgid "P&rint Settings Tab" msgstr "プリント設定タブ(&r)" #: src/slic3r/GUI/GUI_Factories.cpp:141 src/slic3r/GUI/Plater.cpp:575 -#: src/slic3r/GUI/Tab.cpp:4703 src/slic3r/GUI/Tab.cpp:4704 -#: src/slic3r/GUI/Tab.cpp:4776 src/libslic3r/PrintConfig.cpp:3747 -#: src/libslic3r/PrintConfig.cpp:3754 src/libslic3r/PrintConfig.cpp:3768 -#: src/libslic3r/PrintConfig.cpp:3779 src/libslic3r/PrintConfig.cpp:3789 -#: src/libslic3r/PrintConfig.cpp:3811 src/libslic3r/PrintConfig.cpp:3822 -#: src/libslic3r/PrintConfig.cpp:3829 src/libslic3r/PrintConfig.cpp:3836 -#: src/libslic3r/PrintConfig.cpp:3847 src/libslic3r/PrintConfig.cpp:3856 -#: src/libslic3r/PrintConfig.cpp:3865 +#: src/slic3r/GUI/Tab.cpp:4701 src/slic3r/GUI/Tab.cpp:4702 +#: src/slic3r/GUI/Tab.cpp:4774 src/libslic3r/PrintConfig.cpp:3720 +#: src/libslic3r/PrintConfig.cpp:3727 src/libslic3r/PrintConfig.cpp:3741 +#: src/libslic3r/PrintConfig.cpp:3752 src/libslic3r/PrintConfig.cpp:3762 +#: src/libslic3r/PrintConfig.cpp:3784 src/libslic3r/PrintConfig.cpp:3795 +#: src/libslic3r/PrintConfig.cpp:3802 src/libslic3r/PrintConfig.cpp:3809 +#: src/libslic3r/PrintConfig.cpp:3820 src/libslic3r/PrintConfig.cpp:3829 +#: src/libslic3r/PrintConfig.cpp:3838 msgid "Pad" msgstr "パッド" @@ -8306,15 +8308,15 @@ msgstr "パッド" msgid "Pad and Support" msgstr "パッドとサポート" -#: src/slic3r/GUI/Tab.cpp:4776 src/libslic3r/PrintConfig.cpp:3821 +#: src/slic3r/GUI/Tab.cpp:4774 src/libslic3r/PrintConfig.cpp:3794 msgid "Pad around object" msgstr "オブジェクト周りにパッド" -#: src/libslic3r/PrintConfig.cpp:3828 +#: src/libslic3r/PrintConfig.cpp:3801 msgid "Pad around object everywhere" msgstr "オブジェクト周り全体にパッドを配置" -#: src/libslic3r/PrintConfig.cpp:3777 +#: src/libslic3r/PrintConfig.cpp:3750 msgid "Pad brim size" msgstr "パッドブリムサイズ" @@ -8322,31 +8324,31 @@ msgstr "パッドブリムサイズ" msgid "Pad brim size is too small for the current configuration." msgstr "パッドのブリム(縁)サイズは、現在の構成には小さすぎます。" -#: src/libslic3r/PrintConfig.cpp:3864 +#: src/libslic3r/PrintConfig.cpp:3837 msgid "Pad object connector penetration" msgstr "パッドオブジェクトコネクタの貫通" -#: src/libslic3r/PrintConfig.cpp:3846 +#: src/libslic3r/PrintConfig.cpp:3819 msgid "Pad object connector stride" msgstr "パッドオブジェクトコネクタのピッチ" -#: src/libslic3r/PrintConfig.cpp:3855 +#: src/libslic3r/PrintConfig.cpp:3828 msgid "Pad object connector width" msgstr "パッドオブジェクトの接続幅" -#: src/libslic3r/PrintConfig.cpp:3835 +#: src/libslic3r/PrintConfig.cpp:3808 msgid "Pad object gap" msgstr "パッドオブジェクトのギャップ" -#: src/libslic3r/PrintConfig.cpp:3763 +#: src/libslic3r/PrintConfig.cpp:3736 msgid "Pad wall height" msgstr "パッド壁の高さ" -#: src/libslic3r/PrintConfig.cpp:3810 +#: src/libslic3r/PrintConfig.cpp:3783 msgid "Pad wall slope" msgstr "側壁の傾斜" -#: src/libslic3r/PrintConfig.cpp:3753 +#: src/libslic3r/PrintConfig.cpp:3726 msgid "Pad wall thickness" msgstr "台座の壁の厚さ" @@ -8475,7 +8477,7 @@ msgstr "ペースト" msgid "Paste From Clipboard" msgstr "クリップボードからの貼り付け" -#: src/slic3r/GUI/MainFrame.cpp:1349 +#: src/slic3r/GUI/MainFrame.cpp:1353 msgid "Paste clipboard" msgstr "クリップボードからペースト" @@ -8513,7 +8515,7 @@ msgstr "サポートの生成用のパターン。" msgid "Pause" msgstr "停止" -#: src/slic3r/GUI/Tab.cpp:2479 src/libslic3r/GCode.cpp:714 +#: src/slic3r/GUI/Tab.cpp:2477 src/libslic3r/GCode.cpp:714 #: src/libslic3r/PrintConfig.cpp:2459 msgid "Pause Print G-code" msgstr "プリント停止Gコード" @@ -8548,7 +8550,7 @@ msgstr "" "デスクトップ統合を実行します(このバイナリをシステムで検索できるように設定し" "ます)。" -#: src/libslic3r/PrintConfig.cpp:3893 +#: src/libslic3r/PrintConfig.cpp:3866 msgid "" "Performance vs accuracy of calculation. Lower values may produce unwanted " "artifacts." @@ -8591,7 +8593,7 @@ msgstr "" msgid "Perimeter" msgstr "外周" -#: src/libslic3r/PrintConfig.cpp:3117 +#: src/libslic3r/PrintConfig.cpp:3119 msgid "Perimeter distribution count" msgstr "境界線分布カウント" @@ -8603,15 +8605,15 @@ msgstr "境界線エクストルーダー" msgid "Perimeter generator" msgstr "境界線の生成" -#: src/libslic3r/PrintConfig.cpp:3079 +#: src/libslic3r/PrintConfig.cpp:3080 msgid "Perimeter transition length" msgstr "境界線移行長さ" -#: src/libslic3r/PrintConfig.cpp:3089 +#: src/libslic3r/PrintConfig.cpp:3091 msgid "Perimeter transitioning filter margin" msgstr "境界線移行フィルタマージン" -#: src/libslic3r/PrintConfig.cpp:3104 +#: src/libslic3r/PrintConfig.cpp:3106 msgid "Perimeter transitioning threshold angle" msgstr "境界線移行しきい値角" @@ -8662,27 +8664,27 @@ msgstr "" "次の形式で.gcodeおよび.sl1 / .sl1sファイルに保存される画像サイズ:\"XxY, " "XxY, ...\"" -#: src/libslic3r/PrintConfig.cpp:3624 +#: src/libslic3r/PrintConfig.cpp:3597 msgid "Pillar connection mode" msgstr "ピラー接続モード" -#: src/libslic3r/PrintConfig.cpp:3593 +#: src/libslic3r/PrintConfig.cpp:3566 msgid "Pillar diameter" msgstr "ピラー径" -#: src/libslic3r/PrintConfig.cpp:3647 +#: src/libslic3r/PrintConfig.cpp:3620 msgid "Pillar widening factor" msgstr "ピラーの太さ係数" -#: src/slic3r/GUI/ConfigManipulation.cpp:351 +#: src/slic3r/GUI/ConfigManipulation.cpp:349 msgid "Pinhead diameter should be smaller than the pillar diameter." msgstr "サポートチップの直径は、ピラー径より小さくする必要があります。" -#: src/libslic3r/PrintConfig.cpp:3565 +#: src/libslic3r/PrintConfig.cpp:3538 msgid "Pinhead front diameter" msgstr "ピンヘッド前面径" -#: src/libslic3r/PrintConfig.cpp:3583 +#: src/libslic3r/PrintConfig.cpp:3556 msgid "Pinhead width" msgstr "ピンヘッド幅" @@ -8730,11 +8732,11 @@ msgstr "" msgid "Please select the file to reload" msgstr "リロードするファイルを選択してください" -#: src/slic3r/GUI/AboutDialog.cpp:45 src/slic3r/GUI/AboutDialog.cpp:303 +#: src/slic3r/GUI/AboutDialog.cpp:45 src/slic3r/GUI/AboutDialog.cpp:305 msgid "Portions copyright" msgstr "一部の著作権" -#: src/libslic3r/PrintConfig.cpp:3284 +#: src/libslic3r/PrintConfig.cpp:3257 msgid "Portrait" msgstr "ポートレート" @@ -8743,7 +8745,7 @@ msgstr "ポートレート" msgid "Position" msgstr "位置" -#: src/slic3r/GUI/Tab.cpp:2808 +#: src/slic3r/GUI/Tab.cpp:2806 msgid "Position (for multi-extruder printers)" msgstr "ポジション(マルチエクストルーダーの場合)" @@ -8759,7 +8761,7 @@ msgstr "Yポジション" msgid "Position of perimeters starting points." msgstr "境界線プリントの開始点。" -#: src/slic3r/GUI/Tab.cpp:1770 +#: src/slic3r/GUI/Tab.cpp:1768 msgid "Post processing scripts shall modify G-code file in place." msgstr "後処理スクリプトは、Gコードファイルを適切に変更する必要があります。" @@ -8781,11 +8783,11 @@ msgstr "" "後処理スクリプトを調整してGコードを変更し、オプションで後処理されたGコード" "ファイルの名前を変更する方法についてマニュアルを参照してください。\n" -#: src/slic3r/GUI/Tab.cpp:1711 src/libslic3r/PrintConfig.cpp:2010 +#: src/slic3r/GUI/Tab.cpp:1709 src/libslic3r/PrintConfig.cpp:2010 msgid "Post-processing scripts" msgstr "ポストプロセス・スクリプト" -#: src/slic3r/GUI/MainFrame.cpp:1394 +#: src/slic3r/GUI/MainFrame.cpp:1398 msgid "Pre&view" msgstr "プレビュー(&v)" @@ -8857,7 +8859,7 @@ msgstr "" msgid "Preset with name \"%1%\" already exists." msgstr "\"%1%\"というプリセット名は既に存在します。" -#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1660 +#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1663 msgid "" "Presets are different.\n" "Click this button to select the same preset for the right and left preset." @@ -8865,7 +8867,7 @@ msgstr "" "プリセットは異なります。\n" "このボタンをクリックして、左右のプリセットに同じプリセットを選択します。" -#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1652 +#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1655 msgid "Presets are the same" msgstr "プリセットは同じです" @@ -8905,7 +8907,7 @@ msgstr "" msgid "Pressure equalizer (experimental)" msgstr "圧力均一化(試行的)" -#: src/libslic3r/PrintConfig.cpp:3091 +#: src/libslic3r/PrintConfig.cpp:3093 msgid "" "Prevent transitioning back and forth between one extra perimeter and one " "less. This margin extends the range of extrusion widths which follow to " @@ -8924,7 +8926,7 @@ msgstr "" "す。パーセントで表すと(例えば25%)、ノズル径を基準に計算されます。" #: src/slic3r/GUI/KBShortcutsDialog.cpp:230 src/slic3r/GUI/Plater.cpp:4467 -#: src/slic3r/GUI/Tab.cpp:2831 +#: src/slic3r/GUI/Tab.cpp:2829 msgid "Preview" msgstr "プレビュー" @@ -8932,7 +8934,7 @@ msgstr "プレビュー" msgid "Preview hollowed and drilled model" msgstr "くり抜き穴開けモデルのプレビュー" -#: src/slic3r/GUI/MainFrame.cpp:1628 +#: src/slic3r/GUI/MainFrame.cpp:1632 msgid "Previously sliced file (" msgstr "以前のスライスファイル(" @@ -8944,7 +8946,7 @@ msgstr "全てのエクストルーダーでプライムを実施" msgid "Print" msgstr "プリント" -#: src/slic3r/GUI/MainFrame.cpp:1412 +#: src/slic3r/GUI/MainFrame.cpp:1416 msgid "Print &Host Upload Queue" msgstr "プリントサーバーアップロードキュー(&H)" @@ -8952,7 +8954,7 @@ msgstr "プリントサーバーアップロードキュー(&H)" msgid "Print Diameters" msgstr "各種直径" -#: src/slic3r/GUI/PhysicalPrinterDialog.cpp:210 src/slic3r/GUI/Tab.cpp:2254 +#: src/slic3r/GUI/PhysicalPrinterDialog.cpp:210 src/slic3r/GUI/Tab.cpp:2252 msgid "Print Host upload" msgstr "プリントサーバーアップロード" @@ -8989,15 +8991,15 @@ msgstr "プリント停止" msgid "Print settings" msgstr "プリント設定" -#: src/libslic3r/PrintConfig.cpp:3915 +#: src/libslic3r/PrintConfig.cpp:3888 msgid "Print speed" msgstr "プリントスピード" -#: src/slic3r/GUI/Tab.cpp:2038 +#: src/slic3r/GUI/Tab.cpp:2036 msgid "Print speed override" msgstr "プリント速度上書き" -#: src/slic3r/GUI/MainFrame.cpp:1385 +#: src/slic3r/GUI/MainFrame.cpp:1389 msgid "Print&er Settings Tab" msgstr "プリンター設定タブ(&e)" @@ -9032,11 +9034,11 @@ msgstr "プリンター設定" msgid "Printer Settings Tab" msgstr "プリンター設定タブ" -#: src/libslic3r/PrintConfig.cpp:3356 src/libslic3r/PrintConfig.cpp:3357 +#: src/libslic3r/PrintConfig.cpp:3329 src/libslic3r/PrintConfig.cpp:3330 msgid "Printer absolute correction" msgstr "絶対的なプリンター補正" -#: src/libslic3r/PrintConfig.cpp:3373 src/libslic3r/PrintConfig.cpp:3374 +#: src/libslic3r/PrintConfig.cpp:3346 src/libslic3r/PrintConfig.cpp:3347 msgid "Printer gamma correction" msgstr "プリンタガンマ補正" @@ -9048,32 +9050,32 @@ msgstr "プリンターメモ" msgid "Printer preset names" msgstr "プリンタのプリセット名" -#: src/libslic3r/PrintConfig.cpp:3333 +#: src/libslic3r/PrintConfig.cpp:3306 msgid "Printer scaling X axis correction" msgstr "プリンター寸法X軸補正" -#: src/libslic3r/PrintConfig.cpp:3341 +#: src/libslic3r/PrintConfig.cpp:3314 msgid "Printer scaling Y axis correction" msgstr "プリンター寸法Y軸補正" -#: src/libslic3r/PrintConfig.cpp:3349 +#: src/libslic3r/PrintConfig.cpp:3322 msgid "Printer scaling Z axis correction" msgstr "プリンター寸法Z軸補正" -#: src/libslic3r/PrintConfig.cpp:3324 src/libslic3r/PrintConfig.cpp:3325 -#: src/libslic3r/PrintConfig.cpp:3326 +#: src/libslic3r/PrintConfig.cpp:3297 src/libslic3r/PrintConfig.cpp:3298 +#: src/libslic3r/PrintConfig.cpp:3299 msgid "Printer scaling correction" msgstr "プリンター寸法補正" -#: src/libslic3r/PrintConfig.cpp:3332 src/libslic3r/PrintConfig.cpp:3334 +#: src/libslic3r/PrintConfig.cpp:3305 src/libslic3r/PrintConfig.cpp:3307 msgid "Printer scaling correction in X axis" msgstr "X軸のプリンター寸法補正" -#: src/libslic3r/PrintConfig.cpp:3340 src/libslic3r/PrintConfig.cpp:3342 +#: src/libslic3r/PrintConfig.cpp:3313 src/libslic3r/PrintConfig.cpp:3315 msgid "Printer scaling correction in Y axis" msgstr "Y軸のプリンター寸法補正" -#: src/libslic3r/PrintConfig.cpp:3348 src/libslic3r/PrintConfig.cpp:3350 +#: src/libslic3r/PrintConfig.cpp:3321 src/libslic3r/PrintConfig.cpp:3323 msgid "Printer scaling correction in Z axis" msgstr "Z軸のプリンター寸法補正" @@ -9120,7 +9122,7 @@ msgid "Process %1% / 100" msgstr "プロセス%1%/ 100" #. TRN "Processing input_file_basename" -#: src/slic3r/GUI/MainFrame.cpp:1687 +#: src/slic3r/GUI/MainFrame.cpp:1691 #, c-format, boost-format msgid "Processing %s" msgstr "%s実行中" @@ -9134,9 +9136,9 @@ msgstr "" "100万ポリゴンを超えるモデル'%1%'の処理は遅くなる可能性があります。 ポリゴン数" "を減らすことを強くお勧めします。" -#: src/slic3r/GUI/Tab.cpp:1731 src/slic3r/GUI/Tab.cpp:2118 -#: src/slic3r/GUI/Tab.cpp:2505 src/slic3r/GUI/Tab.cpp:2578 -#: src/slic3r/GUI/Tab.cpp:4593 src/slic3r/GUI/Tab.cpp:4740 +#: src/slic3r/GUI/Tab.cpp:1729 src/slic3r/GUI/Tab.cpp:2116 +#: src/slic3r/GUI/Tab.cpp:2503 src/slic3r/GUI/Tab.cpp:2576 +#: src/slic3r/GUI/Tab.cpp:4591 src/slic3r/GUI/Tab.cpp:4738 msgid "Profile dependencies" msgstr "プロファイルの依存関係" @@ -9226,7 +9228,7 @@ msgid "" "PrusaSlicer has encountered an error while taking a configuration snapshot." msgstr "PrusaSlicerは、構成スナップショットの作成中にエラーが発生しました。" -#: src/slic3r/GUI/AboutDialog.cpp:271 +#: src/slic3r/GUI/AboutDialog.cpp:273 msgid "" "PrusaSlicer is based on Slic3r by Alessandro Ranellucci and the RepRap " "community." @@ -9346,15 +9348,15 @@ msgstr "クイック" msgid "Quick Add Settings (%s)" msgstr "クイック追加設定(%s)" -#: src/slic3r/GUI/MainFrame.cpp:1276 +#: src/slic3r/GUI/MainFrame.cpp:1280 msgid "Quick Slice" msgstr "高速スライス" -#: src/slic3r/GUI/MainFrame.cpp:1282 +#: src/slic3r/GUI/MainFrame.cpp:1286 msgid "Quick Slice and Save As" msgstr "クイックスライスと名前を付けて保存" -#: src/slic3r/GUI/MainFrame.cpp:1307 src/slic3r/GUI/MainFrame.cpp:1541 +#: src/slic3r/GUI/MainFrame.cpp:1311 src/slic3r/GUI/MainFrame.cpp:1545 #, c-format, boost-format msgid "Quit %s" msgstr "%sを終了" @@ -9421,7 +9423,7 @@ msgstr "ラミング線幅" msgid "Ramming parameters" msgstr "ラミングパラメーター" -#: src/slic3r/GUI/Tab.cpp:2064 +#: src/slic3r/GUI/Tab.cpp:2062 msgid "Ramming settings" msgstr "ラミング設定" @@ -9441,8 +9443,8 @@ msgstr "範囲" msgid "Rasterizing layers" msgstr "レイヤーのラスタライズ" -#: src/slic3r/GUI/MainFrame.cpp:1354 src/slic3r/GUI/MainFrame.cpp:1358 -#: src/slic3r/GUI/MainFrame.cpp:1525 src/slic3r/GUI/MainFrame.cpp:1529 +#: src/slic3r/GUI/MainFrame.cpp:1358 src/slic3r/GUI/MainFrame.cpp:1362 +#: src/slic3r/GUI/MainFrame.cpp:1529 src/slic3r/GUI/MainFrame.cpp:1533 msgid "Re&load from Disk" msgstr "ディスクからの再読み込み&l" @@ -9454,15 +9456,15 @@ msgstr "再構成" msgid "Ready" msgstr "準備完了" -#: src/slic3r/GUI/MainFrame.cpp:1128 src/libslic3r/PrintConfig.cpp:2242 +#: src/slic3r/GUI/MainFrame.cpp:1132 src/libslic3r/PrintConfig.cpp:2242 msgid "Rear" msgstr "背面" -#: src/slic3r/GUI/MainFrame.cpp:1128 +#: src/slic3r/GUI/MainFrame.cpp:1132 msgid "Rear View" msgstr "背面" -#: src/slic3r/GUI/MainFrame.cpp:1153 +#: src/slic3r/GUI/MainFrame.cpp:1157 msgid "Recent projects" msgstr "最近のプロジェクト" @@ -9505,7 +9507,7 @@ msgid "Rectilinear grid" msgstr "直線グリッド" #: src/slic3r/GUI/GLCanvas3D.cpp:4738 src/slic3r/GUI/KBShortcutsDialog.cpp:98 -#: src/slic3r/GUI/MainFrame.cpp:1341 +#: src/slic3r/GUI/MainFrame.cpp:1345 msgid "Redo" msgstr "再実行" @@ -9536,7 +9538,7 @@ msgstr "プリンターの更新" msgid "Regular" msgstr "通常" -#: src/slic3r/GUI/Tab.cpp:4044 +#: src/slic3r/GUI/Tab.cpp:4042 msgid "Regular expression" msgstr "正規表現" @@ -9585,9 +9587,9 @@ msgstr "リロード元:" msgid "Reload plater from disk" msgstr "ディスクからプレートをリロードします" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:207 src/slic3r/GUI/MainFrame.cpp:1355 -#: src/slic3r/GUI/MainFrame.cpp:1359 src/slic3r/GUI/MainFrame.cpp:1526 -#: src/slic3r/GUI/MainFrame.cpp:1530 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:207 src/slic3r/GUI/MainFrame.cpp:1359 +#: src/slic3r/GUI/MainFrame.cpp:1363 src/slic3r/GUI/MainFrame.cpp:1530 +#: src/slic3r/GUI/MainFrame.cpp:1534 msgid "Reload the plater from disk" msgstr "プレートをリロードします" @@ -9613,7 +9615,7 @@ msgid "Remember output directory" msgstr "出力ディレクトリを記憶する" #: src/slic3r/GUI/BedShapeDialog.cpp:292 src/slic3r/GUI/BedShapeDialog.cpp:362 -#: src/slic3r/GUI/Tab.cpp:3750 +#: src/slic3r/GUI/Tab.cpp:3748 msgid "Remove" msgstr "除去" @@ -9741,11 +9743,11 @@ msgstr "" msgid "Render" msgstr "レンダー" -#: src/libslic3r/PrintConfig.cpp:4609 +#: src/libslic3r/PrintConfig.cpp:4584 msgid "Render with a software renderer" msgstr "ソフトウェアでレンダリングする" -#: src/libslic3r/PrintConfig.cpp:4610 +#: src/libslic3r/PrintConfig.cpp:4585 msgid "" "Render with a software renderer. The bundled MESA software renderer is " "loaded instead of the default OpenGL driver." @@ -9753,7 +9755,7 @@ msgstr "" "ソフトウェアレンダラーでレンダリングします。 デフォルトのOpenGLドライバーの代" "わりに、バンドルされたMESAソフトウェアレンダラーがロードされます。" -#: src/slic3r/GUI/MainFrame.cpp:1750 src/libslic3r/PrintConfig.cpp:4521 +#: src/slic3r/GUI/MainFrame.cpp:1754 src/libslic3r/PrintConfig.cpp:4496 msgid "Repair" msgstr "修復" @@ -9785,11 +9787,11 @@ msgstr "Netfabbでモデルを修復中" msgid "Repairing was canceled" msgstr "リペアはキャンセルされました" -#: src/slic3r/GUI/MainFrame.cpp:1288 +#: src/slic3r/GUI/MainFrame.cpp:1292 msgid "Repeat Last Quick Slice" msgstr "最後のクイックスライスを繰り返す" -#: src/slic3r/GUI/MainFrame.cpp:1288 +#: src/slic3r/GUI/MainFrame.cpp:1292 msgid "Repeat last quick slice" msgstr "最後のクイックスライスを繰り返す" @@ -9801,7 +9803,7 @@ msgstr "置換元:" msgid "Replace the selected volume with new STL" msgstr "選択したボリュームを新しいSTLに置き換えます" -#: src/slic3r/GUI/Tab.cpp:3955 +#: src/slic3r/GUI/Tab.cpp:3953 msgid "Replace with" msgstr "で置き換える" @@ -9869,7 +9871,7 @@ msgstr "縮尺をリセット" msgid "Reset selection" msgstr "選択のリセット" -#: src/slic3r/GUI/Tab.cpp:2834 +#: src/slic3r/GUI/Tab.cpp:2832 msgid "Reset to Filament Color" msgstr "フィラメントの色をリセット" @@ -9893,8 +9895,8 @@ msgstr "ワイプ前に引き込む" msgid "Retract on layer change" msgstr "レイヤーチェンジ時の待避" -#: src/slic3r/GUI/GCodeViewer.cpp:3614 src/slic3r/GUI/Tab.cpp:1869 -#: src/slic3r/GUI/Tab.cpp:2811 +#: src/slic3r/GUI/GCodeViewer.cpp:3614 src/slic3r/GUI/Tab.cpp:1867 +#: src/slic3r/GUI/Tab.cpp:2809 msgid "Retraction" msgstr "リトラクション" @@ -9915,7 +9917,7 @@ msgid "" "Retraction is not triggered when travel moves are shorter than this length." msgstr "移動がこの長さより短い場合、吸込み動作を行いません。" -#: src/slic3r/GUI/Tab.cpp:2827 +#: src/slic3r/GUI/Tab.cpp:2825 msgid "" "Retraction when tool is disabled (advanced settings for multi-extruder " "setups)" @@ -9947,15 +9949,15 @@ msgstr "メーターからの変換を元に戻す" msgid "Review the substitutions and adjust them if needed." msgstr "置換を確認し、必要に応じて調整します。" -#: src/slic3r/GUI/MainFrame.cpp:1132 +#: src/slic3r/GUI/MainFrame.cpp:1136 msgid "Right" msgstr "右" -#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1518 +#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1521 msgid "Right Preset Value" msgstr "正しいプリセット値" -#: src/slic3r/GUI/MainFrame.cpp:1132 +#: src/slic3r/GUI/MainFrame.cpp:1136 msgid "Right View" msgstr "右側" @@ -9990,15 +9992,15 @@ msgstr "右マウスボタン:" #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:543 #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:562 #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:578 -#: src/libslic3r/PrintConfig.cpp:4525 +#: src/libslic3r/PrintConfig.cpp:4500 msgid "Rotate" msgstr "回転" -#: src/libslic3r/PrintConfig.cpp:4530 +#: src/libslic3r/PrintConfig.cpp:4505 msgid "Rotate around X" msgstr "X軸周りで回転" -#: src/libslic3r/PrintConfig.cpp:4535 +#: src/libslic3r/PrintConfig.cpp:4510 msgid "Rotate around Y" msgstr "Y軸周りの回転" @@ -10026,15 +10028,15 @@ msgstr "" msgid "Rotation" msgstr "回転" -#: src/libslic3r/PrintConfig.cpp:4531 +#: src/libslic3r/PrintConfig.cpp:4506 msgid "Rotation angle around the X axis in degrees." msgstr "X軸の周りの回転角度(度)。" -#: src/libslic3r/PrintConfig.cpp:4536 +#: src/libslic3r/PrintConfig.cpp:4511 msgid "Rotation angle around the Y axis in degrees." msgstr "Y軸を中心とした回転角(度単位)。" -#: src/libslic3r/PrintConfig.cpp:4526 +#: src/libslic3r/PrintConfig.cpp:4501 msgid "Rotation angle around the Z axis in degrees." msgstr "Z軸周りの回転角度(度)。" @@ -10052,11 +10054,11 @@ msgstr "%s実行" msgid "Running post-processing scripts" msgstr "ポストプロセス スクリプト実行中" -#: src/slic3r/GUI/MainFrame.cpp:1234 src/slic3r/GUI/MainFrame.cpp:1583 +#: src/slic3r/GUI/MainFrame.cpp:1238 src/slic3r/GUI/MainFrame.cpp:1587 msgid "S&end G-code" msgstr "Gコードを送信(&e)" -#: src/slic3r/GUI/MainFrame.cpp:1583 +#: src/slic3r/GUI/MainFrame.cpp:1587 msgid "S&end to print" msgstr "プリントする(&e)" @@ -10086,7 +10088,7 @@ msgstr "SLAギズモのキーボードショートカット" msgid "SLA material" msgstr "SLA材料" -#: src/libslic3r/PrintConfig.cpp:3394 src/libslic3r/PrintConfig.cpp:3395 +#: src/libslic3r/PrintConfig.cpp:3367 src/libslic3r/PrintConfig.cpp:3368 msgid "SLA material type" msgstr "SLA材料のタイプ" @@ -10098,7 +10100,7 @@ msgstr "SLAマテリアル" msgid "SLA print" msgstr "SLAプリント" -#: src/libslic3r/PrintConfig.cpp:3523 +#: src/libslic3r/PrintConfig.cpp:3496 msgid "SLA print material notes" msgstr "SLAプリント材料メモ" @@ -10110,7 +10112,7 @@ msgstr "SLAプリント設定" msgid "SLA supports outside the print area were detected." msgstr "プリント領域外のSLAサポートが検出されました。" -#: src/slic3r/GUI/MainFrame.cpp:1664 +#: src/slic3r/GUI/MainFrame.cpp:1668 msgid "SVG" msgstr "SVG" @@ -10129,7 +10131,7 @@ msgstr "保存" msgid "Save %s as:" msgstr "形式を変更して%sを保存:" -#: src/slic3r/GUI/MainFrame.cpp:1664 +#: src/slic3r/GUI/MainFrame.cpp:1668 #, c-format, boost-format msgid "Save %s file as:" msgstr "%sファイルを別の名前で保存:" @@ -10138,11 +10140,11 @@ msgstr "%sファイルを別の名前で保存:" msgid "Save G-code file as:" msgstr "Gコードを別名で保存:" -#: src/slic3r/GUI/MainFrame.cpp:1739 +#: src/slic3r/GUI/MainFrame.cpp:1743 msgid "Save OBJ file (less prone to coordinate errors than STL) as:" msgstr "OBJファイルを保存します(STLよりも調整エラーが少ない):" -#: src/slic3r/GUI/MainFrame.cpp:1194 src/slic3r/GUI/MainFrame.cpp:1196 +#: src/slic3r/GUI/MainFrame.cpp:1198 src/slic3r/GUI/MainFrame.cpp:1200 msgid "Save Project &as" msgstr "名前を付けてプロジェクトを保存&a" @@ -10150,15 +10152,15 @@ msgstr "名前を付けてプロジェクトを保存&a" msgid "Save SL1 / SL1S file as:" msgstr "別名でSL1/SL1Sファイルを保存:" -#: src/libslic3r/PrintConfig.cpp:4455 +#: src/libslic3r/PrintConfig.cpp:4430 msgid "Save config file" msgstr "設定ファイルを保存" -#: src/slic3r/GUI/MainFrame.cpp:1764 +#: src/slic3r/GUI/MainFrame.cpp:1768 msgid "Save configuration as:" msgstr "構成ファイルを別名で保存:" -#: src/libslic3r/PrintConfig.cpp:4456 +#: src/libslic3r/PrintConfig.cpp:4431 msgid "Save configuration to the specified file." msgstr "指定したファイルに構成を保存します。" @@ -10168,11 +10170,11 @@ msgstr "指定したファイルに構成を保存します。" msgid "Save current %s" msgstr "現在の%sを保存" -#: src/slic3r/GUI/MainFrame.cpp:1190 +#: src/slic3r/GUI/MainFrame.cpp:1194 msgid "Save current project file" msgstr "現在のプロジェクトファイルの保存" -#: src/slic3r/GUI/MainFrame.cpp:1194 src/slic3r/GUI/MainFrame.cpp:1196 +#: src/slic3r/GUI/MainFrame.cpp:1198 src/slic3r/GUI/MainFrame.cpp:1200 msgid "Save current project file as" msgstr "現在のプロジェクトに名前を付けて保存" @@ -10185,7 +10187,7 @@ msgstr "別名で保存 :" msgid "Save preset" msgstr "プリセット保存" -#: src/slic3r/GUI/MainFrame.cpp:1822 +#: src/slic3r/GUI/MainFrame.cpp:1826 msgid "Save presets bundle as:" msgstr "プリセットパッケージを別の名前で保存:" @@ -10214,7 +10216,7 @@ msgstr "選択したオプションを保存して、\"%1%\"をプリセット msgid "Save the selected options." msgstr "選択したオプションを保存します。" -#: src/slic3r/GUI/MainFrame.cpp:1676 +#: src/slic3r/GUI/MainFrame.cpp:1680 msgid "Save zip file as:" msgstr "ZIPファイルを保存:" @@ -10228,7 +10230,7 @@ msgstr "3MFコンテナへのメッシュの保存に失敗しました。" #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:216 #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:563 #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:579 -#: src/libslic3r/PrintConfig.cpp:4540 +#: src/libslic3r/PrintConfig.cpp:4515 msgid "Scale" msgstr "スケール" @@ -10244,11 +10246,11 @@ msgstr "寸法係数" msgid "Scale the selected object to fit the print volume" msgstr "選択したオブジェクトをプリントボリュームに合わせて拡大縮小します" -#: src/libslic3r/PrintConfig.cpp:4549 +#: src/libslic3r/PrintConfig.cpp:4524 msgid "Scale to Fit" msgstr "フィットするように縮尺" -#: src/libslic3r/PrintConfig.cpp:4550 +#: src/libslic3r/PrintConfig.cpp:4525 msgid "Scale to fit the given volume." msgstr "指定したプリントスペースに合わせてサイズを変更します。" @@ -10256,7 +10258,7 @@ msgstr "指定したプリントスペースに合わせてサイズを変更し msgid "Scale to print volume" msgstr "プリントエリアに合わせて縮尺する" -#: src/libslic3r/PrintConfig.cpp:4541 +#: src/libslic3r/PrintConfig.cpp:4516 msgid "Scaling factor or percentage." msgstr "スケーリング係数または割合。" @@ -10287,7 +10289,7 @@ msgstr "シーム優先方向ジッター" msgid "Seams" msgstr "シーム" -#: src/slic3r/GUI/MainFrame.cpp:1364 +#: src/slic3r/GUI/MainFrame.cpp:1368 msgid "Searc&h" msgstr "検索(%h)" @@ -10310,7 +10312,7 @@ msgstr "" msgid "Search in English" msgstr "英語で検索" -#: src/slic3r/GUI/MainFrame.cpp:1365 +#: src/slic3r/GUI/MainFrame.cpp:1369 msgid "Search in settings" msgstr "設定で検索" @@ -10397,7 +10399,7 @@ msgstr "ファイルに適用するアクションを選択します" msgid "Select by rectangle" msgstr "四角形で選択" -#: src/slic3r/GUI/MainFrame.cpp:1783 src/slic3r/GUI/MainFrame.cpp:1848 +#: src/slic3r/GUI/MainFrame.cpp:1787 src/slic3r/GUI/MainFrame.cpp:1852 msgid "Select configuration to load:" msgstr "読み込む構成を選択します:" @@ -10405,7 +10407,7 @@ msgstr "読み込む構成を選択します:" msgid "Select coordinate space, in which the transformation will be performed." msgstr "変換する座標空間を選択します。" -#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1453 +#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1456 msgid "Select presets to compare" msgstr "比較するプリセットを選択" @@ -10417,7 +10419,7 @@ msgstr "ギャラリーからシェイプを選択" msgid "Select showing settings" msgstr "表示設定を選択" -#: src/slic3r/GUI/MainFrame.cpp:1729 +#: src/slic3r/GUI/MainFrame.cpp:1733 msgid "Select the STL file to repair:" msgstr "修復するSTLファイルを選択 :" @@ -10501,7 +10503,7 @@ msgstr "リストの選択-削除" msgid "Selection-Remove from rectangle" msgstr "選択-四角形から削除" -#: src/slic3r/GUI/MainFrame.cpp:1323 +#: src/slic3r/GUI/MainFrame.cpp:1327 msgid "Selects all objects" msgstr "全てのオブジェクトを選択" @@ -10518,7 +10520,7 @@ msgstr "Gコード送信" msgid "Send system info" msgstr "システム情報を送信する" -#: src/slic3r/GUI/MainFrame.cpp:1234 +#: src/slic3r/GUI/MainFrame.cpp:1238 msgid "Send to print current plate as G-code" msgstr "現在のプレートをプリントするためにGコードとして送信" @@ -10542,7 +10544,7 @@ msgstr "システム情報を送信しています..." msgid "Seq." msgstr "シーケンス" -#: src/slic3r/GUI/Tab.cpp:1685 +#: src/slic3r/GUI/Tab.cpp:1683 msgid "Sequential printing" msgstr "順次プリンティング" @@ -10558,7 +10560,7 @@ msgstr "シリアルポート:" msgid "Service name" msgstr "サービス名" -#: src/slic3r/GUI/Tab.cpp:3835 src/slic3r/GUI/Tab.cpp:4197 +#: src/slic3r/GUI/Tab.cpp:3833 src/slic3r/GUI/Tab.cpp:4195 msgid "Set" msgstr "設定" @@ -10685,7 +10687,7 @@ msgstr "選択したアイテムをプリント可/プリント不可に設定 msgid "Set settings tabs as menu items (experimental)" msgstr "設定タブをメニュー項目として設定(実験的)" -#: src/libslic3r/PrintConfig.cpp:3277 +#: src/libslic3r/PrintConfig.cpp:3250 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 " @@ -10835,7 +10837,7 @@ msgstr "" msgid "Set upper thumb as active" msgstr "上側の範囲をアクティブに設定" -#: src/libslic3r/PrintConfig.cpp:4603 +#: src/libslic3r/PrintConfig.cpp:4578 msgid "" "Sets logging sensitivity. 0:fatal, 1:error, 2:warning, 3:info, 4:debug, 5:" "trace\n" @@ -10847,7 +10849,7 @@ msgstr "" "します。" #: src/slic3r/GUI/BedShapeDialog.cpp:249 src/slic3r/GUI/GCodeViewer.cpp:3709 -#: src/slic3r/GUI/MainFrame.cpp:2143 +#: src/slic3r/GUI/MainFrame.cpp:2147 msgid "Settings" msgstr "設定" @@ -10898,7 +10900,7 @@ msgstr "" msgid "Shape" msgstr "シェイプ" -#: src/slic3r/GUI/GalleryDialog.cpp:69 src/slic3r/GUI/MainFrame.cpp:1400 +#: src/slic3r/GUI/GalleryDialog.cpp:69 src/slic3r/GUI/MainFrame.cpp:1404 msgid "Shape Gallery" msgstr "シェイプギャラリー" @@ -10949,7 +10951,7 @@ msgstr "開始後に「今日のティップス」を表示する" msgid "Show &Configuration Folder" msgstr "設定フォルダーの表示(&C)" -#: src/slic3r/GUI/MainFrame.cpp:1430 +#: src/slic3r/GUI/MainFrame.cpp:1434 msgid "Show &Labels" msgstr "ラベルを表示&L" @@ -10965,7 +10967,7 @@ msgstr "アバウトダイヤログを表示" msgid "Show advanced settings" msgstr "高度な設定を表示" -#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1502 +#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1505 msgid "Show all presets (including incompatible)" msgstr "すべてのプリセットを表示(互換性のないものを含む)" @@ -11005,7 +11007,7 @@ msgstr "オブジェクトの高さを表示" msgid "Show object height on the ruler" msgstr "ルーラーにオブジェクトの高さを表示する" -#: src/slic3r/GUI/MainFrame.cpp:1430 +#: src/slic3r/GUI/MainFrame.cpp:1434 msgid "Show object/instance labels in 3D scene" msgstr "3Dシーンにオブジェクト/インスタンスラベルを表示する" @@ -11033,43 +11035,43 @@ msgstr "サポート表示" msgid "Show system information" msgstr "システム情報を表示" -#: src/slic3r/GUI/MainFrame.cpp:1391 +#: src/slic3r/GUI/MainFrame.cpp:1395 msgid "Show the 3D editing view" msgstr "3D編集画面の表示" -#: src/slic3r/GUI/MainFrame.cpp:1394 +#: src/slic3r/GUI/MainFrame.cpp:1398 msgid "Show the 3D slices preview" msgstr "3Dスライスのプレビューを表示" -#: src/slic3r/GUI/MainFrame.cpp:1381 +#: src/slic3r/GUI/MainFrame.cpp:1385 msgid "Show the filament settings" msgstr "フィラメントの設定を表示" -#: src/libslic3r/PrintConfig.cpp:4446 +#: src/libslic3r/PrintConfig.cpp:4421 msgid "Show the full list of SLA print configuration options." msgstr "SLAプリント構成オプションの完全なリストを表示します。" -#: src/libslic3r/PrintConfig.cpp:4441 +#: src/libslic3r/PrintConfig.cpp:4416 msgid "Show the full list of print/G-code configuration options." msgstr "プリント/ Gコード構成オプションの完全なリストを表示します。" -#: src/slic3r/GUI/MainFrame.cpp:1103 +#: src/slic3r/GUI/MainFrame.cpp:1107 msgid "Show the list of the keyboard shortcuts" msgstr "キーボードショートカットのリストを表示する" -#: src/slic3r/GUI/MainFrame.cpp:1373 +#: src/slic3r/GUI/MainFrame.cpp:1377 msgid "Show the plater" msgstr "プレート表示" -#: src/slic3r/GUI/MainFrame.cpp:1378 +#: src/slic3r/GUI/MainFrame.cpp:1382 msgid "Show the print settings" msgstr "プリント設定を表示する" -#: src/slic3r/GUI/MainFrame.cpp:1385 +#: src/slic3r/GUI/MainFrame.cpp:1389 msgid "Show the printer settings" msgstr "プリンター設定を表示する" -#: src/libslic3r/PrintConfig.cpp:4435 +#: src/libslic3r/PrintConfig.cpp:4410 msgid "Show this help." msgstr "このヘルプを表示します。" @@ -11152,7 +11154,7 @@ msgstr "簡略化モデル" msgid "Single Extruder Multi Material" msgstr "シングルエクストルーダー・マルチマテリアル" -#: src/slic3r/GUI/Tab.cpp:2333 +#: src/slic3r/GUI/Tab.cpp:2331 msgid "" "Single Extruder Multi Material is selected, \n" "and all extruders must have the same diameter.\n" @@ -11163,15 +11165,15 @@ msgstr "" "のエクストルーダーの直径が同じでなければなりません。最初のエクストルーダーの" "直径で、すべてのエクストルーダーノズルの直径を設定しますか?" -#: src/slic3r/GUI/Tab.cpp:2733 src/slic3r/GUI/Tab.cpp:2742 +#: src/slic3r/GUI/Tab.cpp:2731 src/slic3r/GUI/Tab.cpp:2740 msgid "Single extruder MM setup" msgstr "シングルエクストルーダーのMM設定" -#: src/slic3r/GUI/Tab.cpp:2743 +#: src/slic3r/GUI/Tab.cpp:2741 msgid "Single extruder multimaterial parameters" msgstr "単一エクストルーダーのマルチマテリアルパラメーター" -#: src/libslic3r/PrintConfig.cpp:4586 +#: src/libslic3r/PrintConfig.cpp:4561 msgid "Single instance mode" msgstr "シングルインスタンスモード" @@ -11260,15 +11262,15 @@ msgstr "" msgid "Slic3r will not scale speed down below this speed." msgstr "Slic3rはこの速度以下にしません。" -#: src/libslic3r/PrintConfig.cpp:4428 +#: src/libslic3r/PrintConfig.cpp:4403 msgid "Slice" msgstr "スライス" -#: src/slic3r/GUI/MainFrame.cpp:1276 +#: src/slic3r/GUI/MainFrame.cpp:1280 msgid "Slice a file into a G-code" msgstr "ファイルをスライスしてGコードに入れる" -#: src/slic3r/GUI/MainFrame.cpp:1282 +#: src/slic3r/GUI/MainFrame.cpp:1286 msgid "Slice a file into a G-code, save as" msgstr "ファイルをスライスしGコードにして、名前を付けて保存" @@ -11285,15 +11287,15 @@ msgstr "スライス実行" msgid "Slice resolution" msgstr "スライス解像度" -#: src/libslic3r/PrintConfig.cpp:4396 +#: src/libslic3r/PrintConfig.cpp:4371 msgid "Slice the model and export SLA printing layers as PNG." msgstr "モデルをスライスし、SLAプリントレイヤーをPNGとしてエクスポートします。" -#: src/libslic3r/PrintConfig.cpp:4417 +#: src/libslic3r/PrintConfig.cpp:4392 msgid "Slice the model and export toolpaths as G-code." msgstr "モデルをスライスし、ツールパスをGコードでエクスポートします。" -#: src/libslic3r/PrintConfig.cpp:4429 +#: src/libslic3r/PrintConfig.cpp:4404 msgid "" "Slice the model as FFF or SLA based on the printer_technology configuration " "value." @@ -11310,13 +11312,13 @@ msgstr "スライス情報" msgid "Sliced object \"%1%\" looks like a logo or a sign" msgstr "スライスされたオブジェクト\"%1%\"はロゴまたはサインのように見えます" -#: src/slic3r/GUI/MainFrame.cpp:1685 src/slic3r/GUI/Plater.cpp:3316 +#: src/slic3r/GUI/MainFrame.cpp:1689 src/slic3r/GUI/Plater.cpp:3316 #: src/slic3r/GUI/Plater.cpp:6051 src/slic3r/GUI/Tab.cpp:1663 -#: src/slic3r/GUI/Tab.cpp:4729 +#: src/slic3r/GUI/Tab.cpp:4727 msgid "Slicing" msgstr "スライス中" -#: src/slic3r/GUI/MainFrame.cpp:1713 +#: src/slic3r/GUI/MainFrame.cpp:1717 msgid "Slicing Done!" msgstr "スライス完了!" @@ -11351,7 +11353,7 @@ msgstr "モデルをスライス" msgid "Slicing supports" msgstr "サポートのスライス" -#: src/libslic3r/PrintConfig.cpp:3298 src/libslic3r/PrintConfig.cpp:3923 +#: src/libslic3r/PrintConfig.cpp:3271 src/libslic3r/PrintConfig.cpp:3896 msgid "Slow" msgstr "スロー" @@ -11359,7 +11361,7 @@ msgstr "スロー" msgid "Slow down if layer print time is below" msgstr "スローダウンさせるレイヤーのプリント時間" -#: src/libslic3r/PrintConfig.cpp:3299 +#: src/libslic3r/PrintConfig.cpp:3272 msgid "Slow tilt" msgstr "スローチルト" @@ -11367,7 +11369,7 @@ msgstr "スローチルト" msgid "Small perimeters" msgstr "短い境界線" -#: src/libslic3r/PrintConfig.cpp:3603 +#: src/libslic3r/PrintConfig.cpp:3576 msgid "Small pillar diameter percent" msgstr "小さいピラー径パーセント" @@ -11464,7 +11466,7 @@ msgid "Some SLA materials were uninstalled." msgstr "一部のSLAマテリアルがアンインストールされました。" #: src/slic3r/GUI/UnsavedChangesDialog.cpp:936 -#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1696 +#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1699 msgid "" "Some fields are too long to fit. Right mouse click reveals the full text." msgstr "" @@ -11490,7 +11492,7 @@ msgstr "" "一部のオブジェクトが高すぎて、エクストルーダーの衝突なしでプリントできませ" "ん。" -#: src/libslic3r/PrintConfig.cpp:3790 +#: src/libslic3r/PrintConfig.cpp:3763 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 " @@ -11508,7 +11510,7 @@ msgstr "" "一部のプリセットが変更され、保存されていない変更が設定スナップショットに取り" "込まれません。" -#: src/slic3r/GUI/MainFrame.cpp:1813 +#: src/slic3r/GUI/MainFrame.cpp:1817 msgid "" "Some presets are modified and the unsaved changes will not be exported into " "configuration bundle." @@ -11690,7 +11692,7 @@ msgid "Spiral vase" msgstr "スパイラル花瓶" #: src/slic3r/GUI/GUI_Factories.cpp:944 src/slic3r/GUI/GUI_Factories.cpp:954 -#: src/slic3r/GUI/GUI_Factories.cpp:975 src/libslic3r/PrintConfig.cpp:4545 +#: src/slic3r/GUI/GUI_Factories.cpp:975 src/libslic3r/PrintConfig.cpp:4520 msgid "Split" msgstr "分割" @@ -11759,13 +11761,13 @@ msgstr "標準" msgid "Stars" msgstr "スター型" -#: src/slic3r/GUI/Tab.cpp:2089 src/slic3r/GUI/Tab.cpp:2410 +#: src/slic3r/GUI/Tab.cpp:2087 src/slic3r/GUI/Tab.cpp:2408 #: src/libslic3r/GCode.cpp:707 src/libslic3r/PrintConfig.cpp:2418 #: src/libslic3r/PrintConfig.cpp:2433 msgid "Start G-code" msgstr "Gコードの最初" -#: src/slic3r/GUI/MainFrame.cpp:1145 +#: src/slic3r/GUI/MainFrame.cpp:1149 msgid "Start a new project" msgstr "新しいプロジェクトを開始" @@ -11773,7 +11775,7 @@ msgstr "新しいプロジェクトを開始" msgid "Start at height" msgstr "開始高さ" -#: src/slic3r/GUI/MainFrame.cpp:1296 +#: src/slic3r/GUI/MainFrame.cpp:1300 msgid "Start new slicing process" msgstr "新しいスライスプロセスを開始する" @@ -11825,7 +11827,7 @@ msgid "Status:" msgstr "状況:" #: src/slic3r/GUI/Search.cpp:90 src/slic3r/GUI/Search.cpp:336 -#: src/slic3r/GUI/Tab.cpp:2651 +#: src/slic3r/GUI/Tab.cpp:2649 msgid "Stealth" msgstr "静音" @@ -11884,19 +11886,19 @@ msgstr "サポート強制" msgid "Support Generator" msgstr "サポートジェネレーター" -#: src/libslic3r/PrintConfig.cpp:3658 +#: src/libslic3r/PrintConfig.cpp:3631 msgid "Support base diameter" msgstr "サポートベースの直径" -#: src/libslic3r/PrintConfig.cpp:3668 +#: src/libslic3r/PrintConfig.cpp:3641 msgid "Support base height" msgstr "サポートベースの高さ" -#: src/libslic3r/PrintConfig.cpp:3677 +#: src/libslic3r/PrintConfig.cpp:3650 msgid "Support base safety distance" msgstr "サポートベースの安全距離" -#: src/slic3r/GUI/Tab.cpp:4666 +#: src/slic3r/GUI/Tab.cpp:4664 msgid "Support head" msgstr "サポート先端" @@ -11948,7 +11950,7 @@ msgid "Support material/raft/skirt extruder" msgstr "サポート材/ラフト/スカート用エクストルーダー" #: src/slic3r/GUI/Plater.cpp:433 src/libslic3r/PrintConfig.cpp:2558 -#: src/libslic3r/PrintConfig.cpp:3640 +#: src/libslic3r/PrintConfig.cpp:3613 msgid "Support on build plate only" msgstr "サポートをビルドプレート(ベッド)のみに限定する" @@ -11956,12 +11958,12 @@ msgstr "サポートをビルドプレート(ベッド)のみに限定する" msgid "Support parameter change" msgstr "サポートパラメータの変更" -#: src/slic3r/GUI/Tab.cpp:4671 +#: src/slic3r/GUI/Tab.cpp:4669 msgid "Support pillar" msgstr "サポートピラー" #: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:46 -#: src/libslic3r/PrintConfig.cpp:3730 +#: src/libslic3r/PrintConfig.cpp:3703 msgid "Support points density" msgstr "サポートポイント密度" @@ -11970,16 +11972,16 @@ msgid "Support points edit" msgstr "サポートポイントの編集" #: src/slic3r/GUI/GUI_Factories.cpp:140 src/slic3r/GUI/Plater.cpp:428 -#: src/slic3r/GUI/Tab.cpp:4662 src/slic3r/GUI/Tab.cpp:4663 -#: src/libslic3r/PrintConfig.cpp:3559 src/libslic3r/PrintConfig.cpp:3566 -#: src/libslic3r/PrintConfig.cpp:3575 src/libslic3r/PrintConfig.cpp:3584 -#: src/libslic3r/PrintConfig.cpp:3594 src/libslic3r/PrintConfig.cpp:3604 -#: src/libslic3r/PrintConfig.cpp:3641 src/libslic3r/PrintConfig.cpp:3648 -#: src/libslic3r/PrintConfig.cpp:3659 src/libslic3r/PrintConfig.cpp:3669 -#: src/libslic3r/PrintConfig.cpp:3678 src/libslic3r/PrintConfig.cpp:3691 -#: src/libslic3r/PrintConfig.cpp:3701 src/libslic3r/PrintConfig.cpp:3710 -#: src/libslic3r/PrintConfig.cpp:3720 src/libslic3r/PrintConfig.cpp:3731 -#: src/libslic3r/PrintConfig.cpp:3739 +#: src/slic3r/GUI/Tab.cpp:4660 src/slic3r/GUI/Tab.cpp:4661 +#: src/libslic3r/PrintConfig.cpp:3532 src/libslic3r/PrintConfig.cpp:3539 +#: src/libslic3r/PrintConfig.cpp:3548 src/libslic3r/PrintConfig.cpp:3557 +#: src/libslic3r/PrintConfig.cpp:3567 src/libslic3r/PrintConfig.cpp:3577 +#: src/libslic3r/PrintConfig.cpp:3614 src/libslic3r/PrintConfig.cpp:3621 +#: src/libslic3r/PrintConfig.cpp:3632 src/libslic3r/PrintConfig.cpp:3642 +#: src/libslic3r/PrintConfig.cpp:3651 src/libslic3r/PrintConfig.cpp:3664 +#: src/libslic3r/PrintConfig.cpp:3674 src/libslic3r/PrintConfig.cpp:3683 +#: src/libslic3r/PrintConfig.cpp:3693 src/libslic3r/PrintConfig.cpp:3704 +#: src/libslic3r/PrintConfig.cpp:3712 msgid "Supports" msgstr "サポート" @@ -12132,7 +12134,7 @@ msgstr "構成スナップショットの作成" msgid "Taking a configuration snapshot failed." msgstr "構成スナップショットの作成に失敗しました" -#: src/slic3r/GUI/GUI_Preview.cpp:222 src/slic3r/GUI/Tab.cpp:1990 +#: src/slic3r/GUI/GUI_Preview.cpp:222 src/slic3r/GUI/Tab.cpp:1988 msgid "Temperature" msgstr "温度" @@ -12157,7 +12159,7 @@ msgstr "温度変化" msgid "Temperatures" msgstr "温度" -#: src/slic3r/GUI/Tab.cpp:2488 src/libslic3r/GCode.cpp:715 +#: src/slic3r/GUI/Tab.cpp:2486 src/libslic3r/GCode.cpp:715 msgid "Template Custom G-code" msgstr "カスタムGコードのテンプレート" @@ -12315,7 +12317,7 @@ msgid "" msgstr "" "ワイプタワーは、複数のオブジェクトのレイヤーの高さが等しい場合に利用できます" -#: src/slic3r/GUI/Tab.cpp:3011 +#: src/slic3r/GUI/Tab.cpp:3009 msgid "" "The Wipe option is not available when using the Firmware Retraction mode.\n" "\n" @@ -12368,7 +12370,7 @@ msgstr "" "回転がオブジェクト座標に埋め込まれると、傾斜オブジェクトの不均一なスケーリン" "グはワールド座標系でのみ可能になります。" -#: src/libslic3r/PrintConfig.cpp:3692 +#: src/libslic3r/PrintConfig.cpp:3665 msgid "The default angle for connecting support sticks and junctions." msgstr "サポートスティックとジャンクションを接続するためのデフォルトの角度。" @@ -12463,7 +12465,7 @@ msgstr "カスタムGコードで使用するフィラメント材料タイプ msgid "The file does not exist." msgstr "ファイルがありません。" -#: src/libslic3r/PrintConfig.cpp:4582 +#: src/libslic3r/PrintConfig.cpp:4557 msgid "" "The file where the output will be written (if not specified, it will be " "based on the input file)." @@ -12495,7 +12497,7 @@ msgstr "次のSLAプリンターモデルには材料が選択されていませ msgid "The following characters are not allowed by a FAT file system:" msgstr "次の文字はFATファイルシステムでは許可されていません。" -#: src/slic3r/GUI/Tab.cpp:1849 +#: src/slic3r/GUI/Tab.cpp:1847 #, c-format, boost-format msgid "" "The following line %s contains reserved keywords.\n" @@ -12546,13 +12548,13 @@ msgstr "" msgid "The following values were substituted:" msgstr "次の値に置き換えられました。" -#: src/libslic3r/PrintConfig.cpp:3837 +#: src/libslic3r/PrintConfig.cpp:3810 msgid "" "The gap between the object bottom and the generated pad in zero elevation " "mode." msgstr "ゼロリフトモードでのオブジェクトの底面と生成されたパッド間のギャップ。" -#: src/libslic3r/PrintConfig.cpp:3670 +#: src/libslic3r/PrintConfig.cpp:3643 msgid "The height of the pillar base cone" msgstr "ピラーのベースコーンの高さ" @@ -12593,7 +12595,7 @@ msgstr "" "最後の色変更データは、シングルエクストルーダーでのプリント用に保存されまし" "た。" -#: src/libslic3r/PrintConfig.cpp:3711 +#: src/libslic3r/PrintConfig.cpp:3684 msgid "" "The max distance of two pillars to get linked with each other. A zero value " "will prohibit pillar cascading." @@ -12601,7 +12603,7 @@ msgstr "" "相互接続のための2つのピラー間の最大距離。 値がゼロの場合、ピラーのカスケード" "が無効になります。" -#: src/libslic3r/PrintConfig.cpp:3702 +#: src/libslic3r/PrintConfig.cpp:3675 msgid "The max length of a bridge" msgstr "最長ブリッジ長さ" @@ -12624,7 +12626,7 @@ msgstr "" "境界線の壁に垂直に測定された、各スキンポイントをオフセットできる最大距離(双" "方向)。" -#: src/libslic3r/PrintConfig.cpp:3680 +#: src/libslic3r/PrintConfig.cpp:3653 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 " @@ -12657,7 +12659,7 @@ msgstr "" "ボトムシェルの最小厚さを確保するために、必要に応じてボトムソリッドレイヤーの" "数をbottom_solid_layersよりも増やします。" -#: src/libslic3r/PrintConfig.cpp:3119 +#: src/libslic3r/PrintConfig.cpp:3121 msgid "" "The number of perimeters, counted from the center, over which the variation " "needs to be spread. Lower values mean that the outer perimeters don't change " @@ -12697,7 +12699,7 @@ msgstr "" "オブジェクトは、このレイヤー数だけ持ち上げられ、その下にサポート材が生成され" "ます。" -#: src/libslic3r/PrintConfig.cpp:3605 +#: src/libslic3r/PrintConfig.cpp:3578 msgid "" "The percentage of smaller pillars compared to the normal pillar diameter " "which are used in problematic areas where a normal pilla cannot fit." @@ -12705,7 +12707,7 @@ msgstr "" "通常のピラーが収まらない問題のある領域で使用される、通常のピラー径と比較した" "小さいピラーの割合。" -#: src/libslic3r/PrintConfig.cpp:3317 +#: src/libslic3r/PrintConfig.cpp:3290 msgid "" "The percentage of the bed area. \n" "If the print area exceeds the specified value, \n" @@ -12715,14 +12717,14 @@ msgstr "" "プリント領域が指定された値を超える場合、ティルト動作を遅くします。それ以外で" "は-速いティルトとなります" -#: src/slic3r/GUI/Tab.cpp:3727 +#: src/slic3r/GUI/Tab.cpp:3725 msgid "" "The physical printer below is based on the preset, you are going to delete." msgid_plural "" "The physical printers below are based on the preset, you are going to delete." msgstr[0] "以下の物理プリンタはプリセットに基づいているため、削除します。" -#: src/slic3r/GUI/Tab.cpp:3737 +#: src/slic3r/GUI/Tab.cpp:3735 msgid "" "The physical printer below is based only on the preset, you are going to " "delete." @@ -12774,7 +12776,7 @@ msgstr "指定されたファイル名が無効です。" msgid "The provided name is not valid;" msgstr "指定された名前は無効です;" -#: src/libslic3r/Format/3mf.cpp:1745 +#: src/libslic3r/Format/3mf.cpp:1746 msgid "" "The selected 3MF contains FDM supports painted object using a newer version " "of PrusaSlicer and is not compatible." @@ -12782,7 +12784,7 @@ msgstr "" "選択した3MFには、新しいバージョンのPrusaSlicerを使用してペイントされたオブ" "ジェクトをサポートするFDMが含まれており、互換性がありません。" -#: src/libslic3r/Format/3mf.cpp:1753 +#: src/libslic3r/Format/3mf.cpp:1754 msgid "" "The selected 3MF contains multi-material painted object using a newer " "version of PrusaSlicer and is not compatible." @@ -12790,7 +12792,7 @@ msgstr "" "選択した3MFには、新しいバージョンのPrusaSlicerを使用したマルチマテリアルペイ" "ントオブジェクトが含まれており、互換性がありません。" -#: src/libslic3r/Format/3mf.cpp:1749 +#: src/libslic3r/Format/3mf.cpp:1750 msgid "" "The selected 3MF contains seam painted object using a newer version of " "PrusaSlicer and is not compatible." @@ -12798,7 +12800,7 @@ msgstr "" "選択した3MFには、新しいバージョンのPrusaSlicerを使用したシームペイントされた" "オブジェクトが含まれており、互換性がありません。" -#: src/libslic3r/Format/3mf.cpp:1734 +#: src/libslic3r/Format/3mf.cpp:1735 #, boost-format msgid "" "The selected 3mf file has been saved with a newer version of %1% and is not " @@ -12845,7 +12847,7 @@ msgstr "" "選択したオブジェクトにはソリッドパーツが1つしかないため、分割できませんでし" "た。" -#: src/slic3r/GUI/MainFrame.cpp:1165 +#: src/slic3r/GUI/MainFrame.cpp:1169 msgid "" "The selected project is no longer available.\n" "Do you want to remove it from the recent projects list?" @@ -12882,7 +12884,7 @@ msgstr "オブジェクトのサイズはインチで指定できます" msgid "The size of the object is zero" msgstr "オブジェクトのサイズはゼロです" -#: src/libslic3r/PrintConfig.cpp:3812 +#: src/libslic3r/PrintConfig.cpp:3785 msgid "" "The slope of the pad wall relative to the bed plane. 90 degrees means " "straight walls." @@ -12954,7 +12956,7 @@ msgstr "指定された名前は無効です;" msgid "The supplied settings will cause an empty print." msgstr "指定された設定では、何もプリントされません。" -#: src/libslic3r/PrintConfig.cpp:3755 +#: src/libslic3r/PrintConfig.cpp:3728 msgid "The thickness of the pad and its optional cavity walls." msgstr "パッドとそのオプションのキャビティ壁の厚さ。" @@ -13334,11 +13336,11 @@ msgstr "" msgid "This is a default preset." msgstr "これはデフォルトのプリセットです。" -#: src/libslic3r/PrintConfig.cpp:3732 +#: src/libslic3r/PrintConfig.cpp:3705 msgid "This is a relative measure of support points density." msgstr "サポートポイント密度の相対値です。" -#: src/slic3r/GUI/Tab.cpp:2778 +#: src/slic3r/GUI/Tab.cpp:2776 msgid "" "This is a single extruder multimaterial printer, diameters of all extruders " "will be set to the new value. Do you want to proceed?" @@ -13351,7 +13353,7 @@ msgid "This is a system preset." msgstr "これはシステムプリセットです。" #: src/libslic3r/PrintConfig.cpp:827 src/libslic3r/PrintConfig.cpp:890 -#: src/libslic3r/PrintConfig.cpp:3389 +#: src/libslic3r/PrintConfig.cpp:3362 msgid "This is only used in the Slic3r interface as a visual help." msgstr "これはSlic3rのみで使用されるイラストです。" @@ -13650,7 +13652,7 @@ msgstr "" "してください。 このバージョン%sと互換性のある設定をインストールする前に、現在" "の構成のバックアップが作成されます。" -#: src/libslic3r/PrintConfig.cpp:4564 +#: src/libslic3r/PrintConfig.cpp:4539 msgid "" "This version of PrusaSlicer may not understand configurations produced by " "the newest PrusaSlicer versions. For example, newer PrusaSlicer may extend " @@ -13663,7 +13665,7 @@ msgstr "" "たは冗長的に、修正するか、未知の値をデフォルトに置き換えるかを決めることがで" "きます。" -#: src/libslic3r/PrintConfig.cpp:3375 +#: src/libslic3r/PrintConfig.cpp:3348 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 " @@ -13685,15 +13687,15 @@ msgstr "" "スレッドは、長時間実行されるタスクを並列化するために使用されます。スレッド数" "は、使用可能なコア/プロセッサーの数をわずかに超えたところが最適となります。" -#: src/slic3r/GUI/Tab.cpp:2539 +#: src/slic3r/GUI/Tab.cpp:2537 msgid "Tilt" msgstr "チルト" -#: src/libslic3r/PrintConfig.cpp:3308 +#: src/libslic3r/PrintConfig.cpp:3281 msgid "Tilt for high viscosity resin" msgstr "高粘度樹脂用ティルト" -#: src/slic3r/GUI/Tab.cpp:2540 +#: src/slic3r/GUI/Tab.cpp:2538 msgid "Tilt time" msgstr "チルト時間" @@ -13722,15 +13724,15 @@ msgstr "" "Material Unit 2.0)がフィラメントをアンロードする時間。 この時間は、Gコード時" "間予測プログラムによって合計プリント予測時間に追加されます。" -#: src/libslic3r/PrintConfig.cpp:3291 +#: src/libslic3r/PrintConfig.cpp:3264 msgid "Time of the fast tilt" msgstr "高速チルトの時間" -#: src/libslic3r/PrintConfig.cpp:3300 +#: src/libslic3r/PrintConfig.cpp:3273 msgid "Time of the slow tilt" msgstr "スローチルトの時間" -#: src/libslic3r/PrintConfig.cpp:3309 +#: src/libslic3r/PrintConfig.cpp:3282 msgid "Time of the super slow tilt" msgstr "超低速ティルト時間" @@ -13791,7 +13793,7 @@ msgstr "ツール" msgid "Tool #" msgstr "ツール#" -#: src/slic3r/GUI/Tab.cpp:2450 src/libslic3r/GCode.cpp:711 +#: src/slic3r/GUI/Tab.cpp:2448 src/libslic3r/GCode.cpp:711 #: src/libslic3r/PrintConfig.cpp:2845 msgid "Tool change G-code" msgstr "ツールチェンジ用のGコード" @@ -13813,7 +13815,7 @@ msgstr "ツール位置" msgid "Tool type" msgstr "ツールタイプ" -#: src/slic3r/GUI/Tab.cpp:2051 +#: src/slic3r/GUI/Tab.cpp:2049 msgid "Toolchange parameters with single extruder MM printers" msgstr "単一エクストルーダーMMプリンターのツールチェンジパラメーター" @@ -13835,7 +13837,7 @@ msgid "" msgstr "" "上部/下部シェルの厚さのヒント:レイヤーの高さが無効なため使用できません。" -#: src/slic3r/GUI/MainFrame.cpp:1121 +#: src/slic3r/GUI/MainFrame.cpp:1125 msgid "Top View" msgstr "上面" @@ -13928,7 +13930,7 @@ msgstr "移動" msgid "Triangles" msgstr "三角形" -#: src/libslic3r/PrintConfig.cpp:4522 +#: src/libslic3r/PrintConfig.cpp:4497 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)." @@ -13949,11 +13951,11 @@ msgstr "プリンターのタイプ。" msgid "Type:" msgstr "タイプ:" -#: src/slic3r/GUI/Tab.cpp:4324 +#: src/slic3r/GUI/Tab.cpp:4322 msgid "UNLOCKED LOCK" msgstr "開いたカギ" -#: src/slic3r/GUI/Tab.cpp:4350 +#: src/slic3r/GUI/Tab.cpp:4348 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" @@ -13965,7 +13967,7 @@ msgstr "" "クリックすると、現在のオプショングループのすべての設定がシステム(またはデ" "フォルト)値にリセットされます。" -#: src/slic3r/GUI/Tab.cpp:4365 +#: src/slic3r/GUI/Tab.cpp:4363 msgid "" "UNLOCKED LOCK icon indicates that the value was changed and is not equal to " "the system (or default) value.\n" @@ -14006,11 +14008,11 @@ msgstr "複数のボリュームに置き換えることはできません" msgid "Undef" msgstr "未定義" -#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1684 +#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1687 msgid "Undef category" msgstr "Undefカテゴリ" -#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1684 +#: src/slic3r/GUI/UnsavedChangesDialog.cpp:1687 msgid "Undef group" msgstr "未定義グループ" @@ -14024,7 +14026,7 @@ msgstr "アンダーフロー" #: src/slic3r/GUI/DesktopIntegrationDialog.cpp:486 #: src/slic3r/GUI/GLCanvas3D.cpp:4699 src/slic3r/GUI/KBShortcutsDialog.cpp:97 -#: src/slic3r/GUI/MainFrame.cpp:1338 +#: src/slic3r/GUI/MainFrame.cpp:1342 msgid "Undo" msgstr "元に戻す" @@ -14221,7 +14223,7 @@ msgstr "フリーカメラを使用" msgid "Use inches" msgstr "インチを使用" -#: src/libslic3r/PrintConfig.cpp:3746 +#: src/libslic3r/PrintConfig.cpp:3719 msgid "Use pad" msgstr "台座を使用" @@ -14310,11 +14312,11 @@ msgstr "" "値が変更されており、システム値または最後に保存されたプリセットとは異なってい" "ます" -#: src/slic3r/GUI/Tab.cpp:2644 +#: src/slic3r/GUI/Tab.cpp:2642 msgid "Values in this column are for Normal mode" msgstr "この列の値は通常モード用です" -#: src/slic3r/GUI/Tab.cpp:2650 +#: src/slic3r/GUI/Tab.cpp:2648 msgid "Values in this column are for Stealth mode" msgstr "この列の値はサイレントモード用です" @@ -14359,7 +14361,7 @@ msgstr "ベンダー:" msgid "Verbose G-code" msgstr "コメント付きGコード" -#: src/slic3r/GUI/AboutDialog.cpp:242 src/slic3r/GUI/AboutDialog.cpp:367 +#: src/slic3r/GUI/AboutDialog.cpp:244 src/slic3r/GUI/AboutDialog.cpp:369 #: src/slic3r/GUI/GUI_App.cpp:262 msgid "Version" msgstr "バージョン" @@ -14408,7 +14410,7 @@ msgstr "" "「設定」にアクセスして\"%1%\"を確認してください\n" "選択を変更します。" -#: src/libslic3r/PrintConfig.cpp:4423 +#: src/libslic3r/PrintConfig.cpp:4398 msgid "Visualize an already sliced and saved G-code" msgstr "すでにスライスされ保存されたGコードを視覚化する" @@ -14433,7 +14435,7 @@ msgstr "オブジェクトのボリュームが並べ替えられました" msgid "Volumetric" msgstr "体積押出し" -#: src/slic3r/GUI/Tab.cpp:2152 +#: src/slic3r/GUI/Tab.cpp:2150 msgid "Volumetric flow hints not available" msgstr "体積押出し流量のヒントは利用できません" @@ -14456,16 +14458,16 @@ msgstr "体積押出し速度" msgid "WARNING:" msgstr "警告:" -#: src/slic3r/GUI/Tab.cpp:4331 +#: src/slic3r/GUI/Tab.cpp:4329 msgid "WHITE BULLET" msgstr "白丸" -#: src/slic3r/GUI/Tab.cpp:4353 +#: src/slic3r/GUI/Tab.cpp:4351 msgid "WHITE BULLET icon indicates a non system (or non default) preset." msgstr "" "白丸アイコンは、システム(またはデフォルト)プリセットでないことを示します。" -#: src/slic3r/GUI/Tab.cpp:4356 +#: src/slic3r/GUI/Tab.cpp:4354 msgid "" "WHITE BULLET icon indicates that the settings are the same as in the last " "saved preset for the current option group." @@ -14473,14 +14475,14 @@ msgstr "" "白丸アイコンは、現在のオプショングループに最後に保存されたプリセットと同じ設" "定であることを示します。" -#: src/slic3r/GUI/Tab.cpp:4371 +#: src/slic3r/GUI/Tab.cpp:4369 msgid "" "WHITE BULLET icon indicates that the value is the same as in the last saved " "preset." msgstr "" "白丸アイコンは、値が最後に保存されたプリセットと同じであることを示します。" -#: src/libslic3r/PrintConfig.cpp:3881 +#: src/libslic3r/PrintConfig.cpp:3854 msgid "Wall thickness" msgstr "壁の厚さ" @@ -14632,7 +14634,7 @@ msgstr "" "移動後に引込みが補償されると、エクストルーダーはこの追加量のフィラメントを押" "し出します。 この設定はほとんど必要ありません。" -#: src/libslic3r/PrintConfig.cpp:3106 +#: src/libslic3r/PrintConfig.cpp:3108 msgid "" "When to create transitions between even and odd numbers of perimeters. A " "wedge shape with an angle greater than this setting will not have " @@ -14645,16 +14647,18 @@ msgstr "" "リントされません。この設定を小さくすると、中央の境界線の数と長さが減ります" "が、隙間ができたりはみ出したりすることがあります。" -#: src/libslic3r/PrintConfig.cpp:3081 +#: src/libslic3r/PrintConfig.cpp:3082 msgid "" "When transitioning between different numbers of perimeters as the part " "becomes thinner, a certain amount of space is allotted to split or join the " -"perimeter segments." +"perimeter segments. If expressed as a percentage (for example 100%), it will " +"be computed based on the nozzle diameter." msgstr "" "パーツが薄くなるにつれて、異なる数の境界線の間を移行する場合、境界線のセグメ" -"ントを分割または結合するために一定のスペースが割り当てられます。" +"ントを分割または結合するために一定のスペースが割り当てられます。パーセントで" +"表すと(例えば100%)、ノズルの直径に基づいて計算されます。" -#: src/slic3r/GUI/Tab.cpp:4052 +#: src/slic3r/GUI/Tab.cpp:4050 msgid "Whole word" msgstr "全文" @@ -14666,7 +14670,7 @@ msgstr "幅" msgid "Width (mm)" msgstr "幅(mm)" -#: src/libslic3r/PrintConfig.cpp:3585 +#: src/libslic3r/PrintConfig.cpp:3558 msgid "Width from the back sphere center to the front sphere center" msgstr "後部ボールの中心から前部ボールの中心までの幅" @@ -14674,16 +14678,16 @@ msgstr "後部ボールの中心から前部ボールの中心までの幅" msgid "Width of a wipe tower" msgstr "ワイプタワーの幅" -#: src/libslic3r/PrintConfig.cpp:3857 +#: src/libslic3r/PrintConfig.cpp:3830 msgid "" "Width of the connector sticks which connect the object and the generated pad." msgstr "オブジェクトと生成されたパッドを接続するコネクタスティックの幅。" -#: src/libslic3r/PrintConfig.cpp:3238 +#: src/libslic3r/PrintConfig.cpp:3211 msgid "Width of the display" msgstr "ディスプレイの幅" -#: src/libslic3r/PrintConfig.cpp:3169 +#: src/libslic3r/PrintConfig.cpp:3142 msgid "" "Width of the perimeter that will replace thin features (according to the " "Minimum feature size) of the model. If the Minimum perimeter width is " @@ -14696,7 +14700,7 @@ msgstr "" "の厚みと同じになります。パーセントで表すと(例えば85%)、ノズルの直径に基づ" "いて計算されます。" -#: src/libslic3r/PrintConfig.cpp:3358 +#: src/libslic3r/PrintConfig.cpp:3331 msgid "" "Will inflate or deflate the sliced 2D polygons according to the sign of the " "correction." @@ -14738,7 +14742,7 @@ msgstr "ワイプタワー-パージ量調整" msgid "Wipe tower brim width" msgstr "ワイプタワーのブリム幅" -#: src/slic3r/GUI/Tab.cpp:2048 +#: src/slic3r/GUI/Tab.cpp:2046 msgid "Wipe tower parameters" msgstr "ワイプタワーのパラメータ" @@ -14800,7 +14804,7 @@ msgstr "" "バージョンに問題がある場合はいつでも復元できます。アップデートされた設定に含" "まれるもの:" -#: src/libslic3r/PrintConfig.cpp:4451 +#: src/libslic3r/PrintConfig.cpp:4426 msgid "Write information about the model to the console." msgstr "コンソールにモデル情報をリストします。" @@ -14890,7 +14894,7 @@ msgstr "" "ここにメモを書いておくことができます。 このテキストは、Gコードヘッダーのコメ" "ントに追加されます。" -#: src/libslic3r/PrintConfig.cpp:3524 +#: src/libslic3r/PrintConfig.cpp:3497 msgid "You can put your notes regarding the SLA print material here." msgstr "SLAプリント材料に関するメモをここに記入できます。" @@ -15050,7 +15054,7 @@ msgstr "" "現在の変更により、保存されているすべてのエクストルーダー(ツール)の変更が削" "除されます。" -#: src/slic3r/GUI/MainFrame.cpp:1750 +#: src/slic3r/GUI/MainFrame.cpp:1754 msgid "Your file was repaired." msgstr "ファイルが修復されました。" @@ -15089,7 +15093,7 @@ msgstr "Zオフセット" msgid "Z travel" msgstr "Z移動" -#: src/libslic3r/PrintConfig.cpp:3633 +#: src/libslic3r/PrintConfig.cpp:3606 msgid "Zig-Zag" msgstr "ジグザグ" @@ -15211,7 +15215,7 @@ msgstr "デフォルトプリントプロファイル" msgid "default value" msgstr "デフォルト値" -#: src/slic3r/GUI/Tab.cpp:3701 +#: src/slic3r/GUI/Tab.cpp:3699 msgid "delete" msgstr "削除" @@ -15289,7 +15293,7 @@ msgid "flow rate is maximized" msgstr "最大送り量になります" #. TRN Description for "WHITE BULLET" -#: src/slic3r/GUI/Tab.cpp:4333 +#: src/slic3r/GUI/Tab.cpp:4331 msgid "" "for the left button: indicates a non-system (or non-default) preset,\n" "for the right button: indicates that the settings hasn't been modified." @@ -15313,7 +15317,7 @@ msgstr "g" msgid "g/cm³" msgstr "g/cm³" -#: src/libslic3r/PrintConfig.cpp:3429 +#: src/libslic3r/PrintConfig.cpp:3402 msgid "g/ml" msgstr "g/ml" @@ -15330,7 +15334,7 @@ msgid "in" msgstr "インチ" #. TRN Description for "UNLOCKED LOCK" -#: src/slic3r/GUI/Tab.cpp:4326 +#: src/slic3r/GUI/Tab.cpp:4324 msgid "" "indicates that some settings were changed and are not equal to the system " "(or default) values for the current option group.\n" @@ -15343,7 +15347,7 @@ msgstr "" "ステム(またはデフォルト)値にリセットします。" #. TRN Description for "LOCKED LOCK" -#: src/slic3r/GUI/Tab.cpp:4322 +#: src/slic3r/GUI/Tab.cpp:4320 msgid "" "indicates that the settings are the same as the system (or default) values " "for the current option group" @@ -15351,7 +15355,7 @@ msgstr "" "設定が現在の設定グループのシステム(デフォルト)値と同じであることを示します" #. TRN Description for "BACK ARROW" -#: src/slic3r/GUI/Tab.cpp:4338 +#: src/slic3r/GUI/Tab.cpp:4336 msgid "" "indicates that the settings were changed and are not equal to the last saved " "preset for the current option group.\n" @@ -15389,11 +15393,11 @@ msgstr "" "Alessandro RanellucciとRepRapコミュニティによるSlic3rに基づいています。 " #. TRN "Slic3r _is licensed under the_ License" -#: src/slic3r/GUI/AboutDialog.cpp:269 src/slic3r/GUI/GUI_App.cpp:268 +#: src/slic3r/GUI/AboutDialog.cpp:271 src/slic3r/GUI/GUI_App.cpp:268 msgid "is licensed under the" msgstr "の下でライセンスされています" -#: src/libslic3r/PrintConfig.cpp:3422 +#: src/libslic3r/PrintConfig.cpp:3395 msgid "kg" msgstr "kg" @@ -15417,7 +15421,7 @@ msgstr "最大PrusaSclierバージョン" msgid "min PrusaSlicer version" msgstr "最小PrusaSlicerバージョン" -#: src/libslic3r/PrintConfig.cpp:3415 +#: src/libslic3r/PrintConfig.cpp:3388 msgid "ml" msgstr "ml" @@ -15457,19 +15461,18 @@ msgstr "ml" #: src/libslic3r/PrintConfig.cpp:2995 src/libslic3r/PrintConfig.cpp:3002 #: src/libslic3r/PrintConfig.cpp:3016 src/libslic3r/PrintConfig.cpp:3040 #: src/libslic3r/PrintConfig.cpp:3050 src/libslic3r/PrintConfig.cpp:3060 -#: src/libslic3r/PrintConfig.cpp:3083 src/libslic3r/PrintConfig.cpp:3161 -#: src/libslic3r/PrintConfig.cpp:3367 src/libslic3r/PrintConfig.cpp:3408 -#: src/libslic3r/PrintConfig.cpp:3568 src/libslic3r/PrintConfig.cpp:3577 -#: src/libslic3r/PrintConfig.cpp:3586 src/libslic3r/PrintConfig.cpp:3596 -#: src/libslic3r/PrintConfig.cpp:3661 src/libslic3r/PrintConfig.cpp:3671 -#: src/libslic3r/PrintConfig.cpp:3683 src/libslic3r/PrintConfig.cpp:3703 -#: src/libslic3r/PrintConfig.cpp:3713 src/libslic3r/PrintConfig.cpp:3723 -#: src/libslic3r/PrintConfig.cpp:3741 src/libslic3r/PrintConfig.cpp:3756 -#: src/libslic3r/PrintConfig.cpp:3770 src/libslic3r/PrintConfig.cpp:3781 -#: src/libslic3r/PrintConfig.cpp:3794 src/libslic3r/PrintConfig.cpp:3839 -#: src/libslic3r/PrintConfig.cpp:3849 src/libslic3r/PrintConfig.cpp:3858 -#: src/libslic3r/PrintConfig.cpp:3868 src/libslic3r/PrintConfig.cpp:3884 -#: src/libslic3r/PrintConfig.cpp:3908 +#: src/libslic3r/PrintConfig.cpp:3340 src/libslic3r/PrintConfig.cpp:3381 +#: src/libslic3r/PrintConfig.cpp:3541 src/libslic3r/PrintConfig.cpp:3550 +#: src/libslic3r/PrintConfig.cpp:3559 src/libslic3r/PrintConfig.cpp:3569 +#: src/libslic3r/PrintConfig.cpp:3634 src/libslic3r/PrintConfig.cpp:3644 +#: src/libslic3r/PrintConfig.cpp:3656 src/libslic3r/PrintConfig.cpp:3676 +#: src/libslic3r/PrintConfig.cpp:3686 src/libslic3r/PrintConfig.cpp:3696 +#: src/libslic3r/PrintConfig.cpp:3714 src/libslic3r/PrintConfig.cpp:3729 +#: src/libslic3r/PrintConfig.cpp:3743 src/libslic3r/PrintConfig.cpp:3754 +#: src/libslic3r/PrintConfig.cpp:3767 src/libslic3r/PrintConfig.cpp:3812 +#: src/libslic3r/PrintConfig.cpp:3822 src/libslic3r/PrintConfig.cpp:3831 +#: src/libslic3r/PrintConfig.cpp:3841 src/libslic3r/PrintConfig.cpp:3857 +#: src/libslic3r/PrintConfig.cpp:3881 msgid "mm" msgstr "mm" @@ -15483,8 +15486,9 @@ msgstr "mm (0で無効化)" #: src/libslic3r/PrintConfig.cpp:1478 src/libslic3r/PrintConfig.cpp:1505 #: src/libslic3r/PrintConfig.cpp:1979 src/libslic3r/PrintConfig.cpp:2365 #: src/libslic3r/PrintConfig.cpp:2539 src/libslic3r/PrintConfig.cpp:2628 -#: src/libslic3r/PrintConfig.cpp:2863 src/libslic3r/PrintConfig.cpp:3098 -#: src/libslic3r/PrintConfig.cpp:3173 +#: src/libslic3r/PrintConfig.cpp:2863 src/libslic3r/PrintConfig.cpp:3085 +#: src/libslic3r/PrintConfig.cpp:3100 src/libslic3r/PrintConfig.cpp:3134 +#: src/libslic3r/PrintConfig.cpp:3146 msgid "mm or %" msgstr "mmまたは%" @@ -15546,7 +15550,7 @@ msgstr "モデル" msgid "modified" msgstr "変更あり" -#: src/libslic3r/PrintConfig.cpp:3436 +#: src/libslic3r/PrintConfig.cpp:3409 msgid "money/bottle" msgstr "金額/ボトル" @@ -15604,7 +15608,7 @@ msgstr "プリンター" msgid "printer model" msgstr "プリンターモデル" -#: src/slic3r/GUI/Tab.cpp:3701 +#: src/slic3r/GUI/Tab.cpp:3699 msgid "remove" msgstr "外す" @@ -15625,11 +15629,11 @@ msgstr "最小%sと最大%sが必要です" #: src/slic3r/GUI/RammingChart.cpp:90 src/slic3r/GUI/WipeTowerDialog.cpp:114 #: src/libslic3r/PrintConfig.cpp:951 src/libslic3r/PrintConfig.cpp:995 -#: src/libslic3r/PrintConfig.cpp:1010 src/libslic3r/PrintConfig.cpp:3292 -#: src/libslic3r/PrintConfig.cpp:3301 src/libslic3r/PrintConfig.cpp:3310 -#: src/libslic3r/PrintConfig.cpp:3451 src/libslic3r/PrintConfig.cpp:3459 -#: src/libslic3r/PrintConfig.cpp:3467 src/libslic3r/PrintConfig.cpp:3474 -#: src/libslic3r/PrintConfig.cpp:3482 src/libslic3r/PrintConfig.cpp:3490 +#: src/libslic3r/PrintConfig.cpp:1010 src/libslic3r/PrintConfig.cpp:3265 +#: src/libslic3r/PrintConfig.cpp:3274 src/libslic3r/PrintConfig.cpp:3283 +#: src/libslic3r/PrintConfig.cpp:3424 src/libslic3r/PrintConfig.cpp:3432 +#: src/libslic3r/PrintConfig.cpp:3440 src/libslic3r/PrintConfig.cpp:3447 +#: src/libslic3r/PrintConfig.cpp:3455 src/libslic3r/PrintConfig.cpp:3463 msgid "s" msgstr "s" @@ -15752,8 +15756,8 @@ msgstr "コールバックの書込に失敗しました" #: src/libslic3r/PrintConfig.cpp:478 src/libslic3r/PrintConfig.cpp:1097 #: src/libslic3r/PrintConfig.cpp:2250 src/libslic3r/PrintConfig.cpp:2260 #: src/libslic3r/PrintConfig.cpp:2551 src/libslic3r/PrintConfig.cpp:2792 -#: src/libslic3r/PrintConfig.cpp:3009 src/libslic3r/PrintConfig.cpp:3110 -#: src/libslic3r/PrintConfig.cpp:3693 src/libslic3r/PrintConfig.cpp:3814 +#: src/libslic3r/PrintConfig.cpp:3009 src/libslic3r/PrintConfig.cpp:3112 +#: src/libslic3r/PrintConfig.cpp:3666 src/libslic3r/PrintConfig.cpp:3787 msgid "°" msgstr "°" diff --git a/resources/localization/zh_CN/PrusaSlicer.mo b/resources/localization/zh_CN/PrusaSlicer.mo index a9b2a5e83..66c4a83ca 100644 Binary files a/resources/localization/zh_CN/PrusaSlicer.mo and b/resources/localization/zh_CN/PrusaSlicer.mo differ diff --git a/resources/localization/zh_CN/PrusaSlicer_zh_CN.po b/resources/localization/zh_CN/PrusaSlicer_zh_CN.po index 2453b584d..42b5540df 100644 --- a/resources/localization/zh_CN/PrusaSlicer_zh_CN.po +++ b/resources/localization/zh_CN/PrusaSlicer_zh_CN.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-08-12 10:25+0200\n" -"PO-Revision-Date: 2022-07-22 21:49+0800\n" +"PO-Revision-Date: 2022-08-22 12:37+0800\n" "Last-Translator: Bral@qq.com\n" "Language-Team: \n" "Language: zh_CN\n" @@ -3291,11 +3291,11 @@ msgstr "" #: src/slic3r/GUI/GUI_App.cpp:980 msgid "Import" -msgstr "进口" +msgstr "导入" #: src/slic3r/GUI/GUI_App.cpp:981 msgid "Don't import" -msgstr "不要进口" +msgstr "不要导入" #: src/slic3r/GUI/GUI_App.cpp:989 msgid "Continue and import newer configuration?" @@ -13639,6 +13639,8 @@ msgid "" "perimeters with variable extrusion width. This setting also affects the " "Concentric infill." msgstr "" +"经典轮廓生成器产生具有恒定挤出宽度的轮廓,并且对于非常薄的区域使用间隙填充。 " +"Arachne 引擎产生具有可变挤出宽度的轮廓。这个设置也会影响回环填充。" #: src/libslic3r/PrintConfig.cpp:3074 msgid "Classic" @@ -13659,6 +13661,8 @@ msgid "" "perimeter segments. If expressed as a percentage (for example 100%), it will " "be computed based on the nozzle diameter." msgstr "" +"当随着零件变薄而在不同数量的轮廓之间过渡时,会分配出一定的空间来分割或连接轮" +"廓段。如果用百分比表示(例如 100%),它将根据喷嘴直径计算出来。" #: src/libslic3r/PrintConfig.cpp:3091 msgid "Perimeter transitioning filter margin" @@ -13721,6 +13725,9 @@ msgid "" "a percentage (for example 25%), it will be computed based on the nozzle " "diameter." msgstr "" +"薄型特征的最小厚度。比这个值薄的模型特征将不被打印,而比最小特征尺寸厚的特征" +"将被加宽到最小轮廓的宽度。如果以百分比表示(例如 25%),它将根据喷嘴直径计" +"算。" #: src/libslic3r/PrintConfig.cpp:3140 msgid "Minimum perimeter width" diff --git a/resources/profiles/Creality.idx b/resources/profiles/Creality.idx index 00db32f4f..471ca8c56 100644 --- a/resources/profiles/Creality.idx +++ b/resources/profiles/Creality.idx @@ -1,4 +1,5 @@ min_slic3r_version = 2.5.0-alpha0 +0.2.1 Added Ender 3 Neo and Ender 3 S1 Plus. Various updates. 0.2.0 Added alternative nozzle support 0.1.5 Added Ender-3 S1 Pro min_slic3r_version = 2.4.1 diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 292b99d70..82c1c0bd6 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -5,7 +5,7 @@ name = Creality # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 0.2.0 +config_version = 0.2.1 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Creality/ # changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% @@ -41,6 +41,15 @@ bed_model = ender3_bed.stl bed_texture = ender3.svg default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY +[printer_model:ENDER3NEO] +name = Creality Ender-3 Neo +variants = 0.4; 0.3; 0.5; 0.6 +technology = FFF +family = ENDER +bed_model = ender3_bed.stl +bed_texture = ender3.svg +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY + [printer_model:ENDER3V2] name = Creality Ender-3 V2 variants = 0.4; 0.3; 0.5; 0.6 @@ -77,6 +86,15 @@ bed_model = ender3v2_bed.stl bed_texture = ender3v2.svg default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY +[printer_model:ENDER3S1PLUS] +name = Creality Ender-3 S1 Plus +variants = 0.4; 0.3; 0.5; 0.6 +technology = FFF +family = ENDER +bed_model = cr10v2_bed.stl +bed_texture = cr10spro.svg +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY + [printer_model:ENDER3MAX] name = Creality Ender-3 Max variants = 0.4; 0.3; 0.5; 0.6 @@ -531,11 +549,11 @@ support_material_spacing = 1 first_layer_height = 0.2 extrusion_width = 0.33 external_perimeter_extrusion_width = 0.31 -first_layer_extrusion_width = 0.33 +first_layer_extrusion_width = 0.31 infill_extrusion_width = 0.33 perimeter_extrusion_width = 0.33 solid_infill_extrusion_width = 0.33 -top_infill_extrusion_width = 0.33 +top_infill_extrusion_width = 0.30 support_material_extrusion_width = 0.27 [print:*0.4nozzle*] @@ -545,11 +563,11 @@ support_material_spacing = 1 first_layer_height = 0.2 extrusion_width = 0.44 external_perimeter_extrusion_width = 0.42 -first_layer_extrusion_width = 0.44 +first_layer_extrusion_width = 0.42 infill_extrusion_width = 0.44 perimeter_extrusion_width = 0.44 solid_infill_extrusion_width = 0.44 -top_infill_extrusion_width = 0.44 +top_infill_extrusion_width = 0.40 support_material_extrusion_width = 0.36 [print:*0.5nozzle*] @@ -559,11 +577,11 @@ support_material_spacing = 1.1 first_layer_height = 0.2 extrusion_width = 0.55 external_perimeter_extrusion_width = 0.52 -first_layer_extrusion_width = 0.55 +first_layer_extrusion_width = 0.52 infill_extrusion_width = 0.55 perimeter_extrusion_width = 0.55 solid_infill_extrusion_width = 0.55 -top_infill_extrusion_width = 0.55 +top_infill_extrusion_width = 0.50 support_material_extrusion_width = 0.45 [print:*0.6nozzle*] @@ -573,11 +591,11 @@ support_material_spacing = 1.2 first_layer_height = 0.3 extrusion_width = 0.66 external_perimeter_extrusion_width = 0.63 -first_layer_extrusion_width = 0.66 +first_layer_extrusion_width = 0.63 infill_extrusion_width = 0.66 perimeter_extrusion_width = 0.66 solid_infill_extrusion_width = 0.66 -top_infill_extrusion_width = 0.66 +top_infill_extrusion_width = 0.60 support_material_extrusion_width = 0.54 @@ -1268,6 +1286,25 @@ inherits = *ENDER3PRO*; *0.6nozzle* +[printer:*ENDER3NEO*] +inherits = *ENDER3*; *fastabl* +printer_model = ENDER3NEO +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3NEO\nPRINTER_HAS_BOWDEN + +[printer:Creality Ender-3 Neo (0.3 mm nozzle)] +inherits = *ENDER3NEO*; *0.3nozzle* + +[printer:Creality Ender-3 Neo (0.4 mm nozzle)] +inherits = *ENDER3NEO*; *0.4nozzle* + +[printer:Creality Ender-3 Neo (0.5 mm nozzle)] +inherits = *ENDER3NEO*; *0.5nozzle* + +[printer:Creality Ender-3 Neo (0.6 mm nozzle)] +inherits = *ENDER3NEO*; *0.6nozzle* + + + [printer:*ENDER3V2*] inherits = *common*; *bowden*; *pauseprint* bed_shape = 5x0,215x0,215x220,5x220 @@ -1312,7 +1349,7 @@ inherits = *ENDER3V2NEO*; *0.6nozzle* [printer:*ENDER3S1*] -inherits = *common*; *bowden*; *spriteextruder*; *pauseprint* +inherits = *common*; *fastabl*; *spriteextruder*; *pauseprint* bed_shape = 5x0,215x0,215x220,5x220 max_print_height = 270 printer_model = ENDER3S1 @@ -1334,7 +1371,7 @@ inherits = *ENDER3S1*; *0.6nozzle* [printer:*ENDER3S1PRO*] -inherits = *common*; *bowden*; *spriteextruder*; *pauseprint* +inherits = *common*; *fastabl*; *spriteextruder*; *pauseprint* bed_shape = 5x0,215x0,215x220,5x220 max_print_height = 270 printer_model = ENDER3S1PRO @@ -1355,6 +1392,26 @@ inherits = *ENDER3S1PRO*; *0.6nozzle* +[printer:*ENDER3S1PLUS*] +inherits = *common*; *slowabl*; *spriteextruder*; *pauseprint* +bed_shape = 5x5,295x5,295x295,5x295 +max_print_height = 300 +printer_model = ENDER3S1PLUS +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3S1PLUS + +[printer:Creality Ender-3 S1 Plus (0.3 mm nozzle)] +inherits = *ENDER3S1PLUS*; *0.3nozzle* + +[printer:Creality Ender-3 S1 Plus (0.4 mm nozzle)] +inherits = *ENDER3S1PLUS*; *0.4nozzle* + +[printer:Creality Ender-3 S1 Plus (0.5 mm nozzle)] +inherits = *ENDER3S1PLUS*; *0.5nozzle* + +[printer:Creality Ender-3 S1 Plus (0.6 mm nozzle)] +inherits = *ENDER3S1PLUS*; *0.6nozzle* + + [printer:*ENDER3MAX*] inherits = *common*; *bowdenlong*; *pauseprint* @@ -1669,7 +1726,7 @@ inherits = *CR10SMART*; *0.6nozzle* [printer:*CR10SMARTPRO*] -inherits = *common*; *bowdenlong*; *slowabl*; *spriteextruder* +inherits = *common*; *slowabl*; *spriteextruder* bed_shape = 5x5,295x5,295x295,5x295 max_print_height = 400 printer_model = CR10SMARTPRO diff --git a/resources/profiles/Creality/ENDER3NEO_thumbnail.png b/resources/profiles/Creality/ENDER3NEO_thumbnail.png new file mode 100644 index 000000000..809e186d5 Binary files /dev/null and b/resources/profiles/Creality/ENDER3NEO_thumbnail.png differ diff --git a/resources/profiles/Creality/ENDER3S1PLUS_thumbnail.png b/resources/profiles/Creality/ENDER3S1PLUS_thumbnail.png new file mode 100644 index 000000000..ae7024f84 Binary files /dev/null and b/resources/profiles/Creality/ENDER3S1PLUS_thumbnail.png differ diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index ce0505857..dae1399b2 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,6 +1,10 @@ min_slic3r_version = 2.5.0-alpha0 +1.5.1 Renamed filament type "NYLON" to "PA". Updated Adura X profile. Updated PETG fan settings for Prusa MINI (removed fan ramp up). +1.5.0 Updated arachne parameters. Added profiles for Jessie filaments. +1.5.0-alpha1 Added filament profile for Prusament PA11 Carbon Fiber. Added profiles for multiple 3D-Fuel filaments. 1.5.0-alpha0 Added parameters for Arachne perimeter generator. Changed default seam position. Updated output filename format. min_slic3r_version = 2.4.0-rc +1.4.7 Added filament profile for Prusament PA11 Carbon Fiber. Added profiles for multiple 3D-Fuel filaments. 1.4.6 Added SLA materials. Updated filament profiles. 1.4.5 Added MMU2/S profiles for 0.25mm nozzle. Updated FW version. Enabled g-code thumbnails for MK3 family printers. Updated end g-code. 1.4.4 Added multiple Fiberlogy filament profiles. Updated Extrudr filament profiles. diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 1760743ea..17d8486c7 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -5,7 +5,7 @@ name = Prusa Research # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 1.5.0-alpha0 +config_version = 1.5.1 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/ changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% @@ -30,7 +30,7 @@ technology = FFF family = MK3 bed_model = mk3_bed.stl bed_texture = mk3.svg -default_materials = Generic PLA; Generic ABS; Generic PETG; Prusament PLA; Prusament PETG; Prusament ASA; Prusament PC Blend; Prusament PC Blend Carbon Fiber; Prusament PVB +default_materials = Generic PLA; Generic ABS; Generic PETG; Prusament PLA; Prusament PETG; Prusament ASA; Prusament PC Blend; Prusament PC Blend Carbon Fiber; Prusament PVB; Prusament PA11 Carbon Fiber [printer_model:MK3] name = Original Prusa i3 MK3 @@ -39,7 +39,7 @@ technology = FFF family = MK3 bed_model = mk3_bed.stl bed_texture = mk3.svg -default_materials = Generic PLA; Generic ABS; Generic PETG; Prusament PLA; Prusament PETG; Prusament ASA; Prusament PC Blend; Prusament PC Blend Carbon Fiber; Prusament PVB +default_materials = Generic PLA; Generic ABS; Generic PETG; Prusament PLA; Prusament PETG; Prusament ASA; Prusament PC Blend; Prusament PC Blend Carbon Fiber; Prusament PVB; Prusament PA11 Carbon Fiber [printer_model:MK3SMMU2S] name = Original Prusa i3 MK3S && MK3S+ MMU2S @@ -66,7 +66,7 @@ technology = FFF family = MK2.5 bed_model = mk3_bed.stl bed_texture = mk3.svg -default_materials = Generic PLA; Generic ABS; Generic PETG; Prusament PLA; Prusament PETG; Prusament ASA; Prusament PC Blend; Prusament PC Blend Carbon Fiber; Prusament PVB +default_materials = Generic PLA; Generic ABS; Generic PETG; Prusament PLA; Prusament PETG; Prusament ASA; Prusament PC Blend; Prusament PC Blend Carbon Fiber; Prusament PVB; Prusament PA11 Carbon Fiber @MK2 [printer_model:MK2.5] name = Original Prusa i3 MK2.5 @@ -75,7 +75,7 @@ technology = FFF family = MK2.5 bed_model = mk3_bed.stl bed_texture = mk3.svg -default_materials = Generic PLA; Generic ABS; Generic PETG; Prusament PLA; Prusament PETG; Prusament ASA; Prusament PC Blend; Prusament PC Blend Carbon Fiber; Prusament PVB +default_materials = Generic PLA; Generic ABS; Generic PETG; Prusament PLA; Prusament PETG; Prusament ASA; Prusament PC Blend; Prusament PC Blend Carbon Fiber; Prusament PVB; Prusament PA11 Carbon Fiber @MK2 [printer_model:MK2.5SMMU2S] name = Original Prusa i3 MK2.5S MMU2S @@ -102,7 +102,7 @@ technology = FFF family = MK2 bed_model = mk2_bed.stl bed_texture = mk2.svg -default_materials = Generic PLA; Generic ABS; Generic PETG; Prusament PLA; Prusament PETG; Prusament ASA; Prusament PC Blend; Prusament PC Blend Carbon Fiber; Prusament PVB +default_materials = Generic PLA; Generic ABS; Generic PETG; Prusament PLA; Prusament PETG; Prusament ASA; Prusament PC Blend; Prusament PC Blend Carbon Fiber; Prusament PVB; Prusament PA11 Carbon Fiber @MK2 [printer_model:MK2SMM] name = Original Prusa i3 MK2S MMU1 @@ -243,14 +243,11 @@ bottom_solid_min_thickness = 0.5 gcode_label_objects = 1 infill_anchor = 2.5 infill_anchor_max = 12 -wall_add_middle_threshold = 75% -wall_split_middle_threshold = 50% wall_transition_angle = 10 wall_transition_filter_deviation = 25% wall_transition_length = 0.4 wall_distribution_count = 1 min_bead_width = 85% -min_feature_size = 0.1 [print:*MK3*] fill_pattern = grid @@ -297,14 +294,11 @@ thick_bridges = 0 bridge_flow_ratio = 1 bridge_speed = 20 wipe_tower_bridging = 6 -wall_add_middle_threshold = 85% -wall_split_middle_threshold = 70% wall_transition_angle = 10 wall_transition_filter_deviation = 25% wall_transition_length = 0.25 wall_distribution_count = 1 min_bead_width = 85% -min_feature_size = 0.0625 [print:*0.25nozzleMK3*] inherits = *0.25nozzle* @@ -353,14 +347,11 @@ bottom_solid_min_thickness = 0.6 thick_bridges = 1 bridge_flow_ratio = 0.95 bridge_speed = 25 -wall_add_middle_threshold = 85% -wall_split_middle_threshold = 70% wall_transition_angle = 10 wall_transition_filter_deviation = 25% wall_transition_length = 0.6 wall_distribution_count = 1 min_bead_width = 85% -min_feature_size = 0.15 [print:*0.6nozzleMK3*] inherits = *0.6nozzle* @@ -422,14 +413,11 @@ bottom_solid_min_thickness = 0.8 single_extruder_multi_material_priming = 0 thick_bridges = 1 overhangs = 0 -wall_add_middle_threshold = 85% -wall_split_middle_threshold = 70% wall_transition_angle = 10 wall_transition_filter_deviation = 25% wall_transition_length = 0.8 wall_distribution_count = 1 min_bead_width = 85% -min_feature_size = 0.2 [print:*soluble_support*] overhangs = 1 @@ -926,6 +914,7 @@ top_solid_infill_speed = 40 fill_pattern = gyroid fill_density = 15% perimeters = 3 +wipe_tower_bridging = 5 [print:0.15mm QUALITY @MK3] inherits = *0.15mm*; *MK3* @@ -1620,9 +1609,9 @@ filament_retract_lift = 0.2 compatible_printers_condition = printer_model=="MK2SMM" [filament:*PETMINI*] -# inherits = *PET* +full_fan_speed_layer = 0 filament_retract_length = nil -filament_retract_speed = 40 +filament_retract_speed = 45 filament_deretract_speed = 25 filament_retract_lift = nil filament_retract_before_travel = 1 @@ -1631,9 +1620,9 @@ compatible_printers_condition = printer_model=="MINI" start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.6}0.12{elsif nozzle_diameter[0]==0.8}0.06{else}0.2{endif} ; Filament gcode" [filament:*PETMINI06*] -# inherits = *PET* +full_fan_speed_layer = 0 filament_retract_length = nil -filament_retract_speed = 40 +filament_retract_speed = 45 filament_deretract_speed = 25 filament_retract_lift = nil filament_retract_before_travel = 1 @@ -1642,7 +1631,6 @@ start_filament_gcode = "M900 K0.12 ; Filament gcode" filament_max_volumetric_speed = 13 [filament:*ABSMINI*] -# inherits = *ABS* bed_temperature = 100 first_layer_bed_temperature = 100 filament_retract_length = 2.7 @@ -2057,6 +2045,28 @@ disable_fan_first_layers = 6 compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! (printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and single_extruder_multi_material) 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.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.07{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" +[filament:Prusament PA11 Carbon Fiber] +inherits = Prusament PC Blend Carbon Fiber +filament_cost = 151.24 +filament_density = 1.11 +filament_type = PA +filament_max_volumetric_speed = 6.5 +extrusion_multiplier = 1.05 +first_layer_temperature = 275 +temperature = 285 +first_layer_bed_temperature = 90 +bed_temperature = 115 +fan_below_layer_time = 10 +compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and ! single_extruder_multi_material + +[filament:Prusament PA11 Carbon Fiber @MK2] +inherits = Prusament PA11 Carbon Fiber +first_layer_bed_temperature = 90 +bed_temperature = 110 +disable_fan_first_layers = 6 +compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! (printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and single_extruder_multi_material) +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.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.07{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" + [filament:Fillamentum CPE] inherits = *PET* filament_vendor = Fillamentum @@ -2376,10 +2386,10 @@ inherits = *PET* filament_vendor = addnorth filament_cost = 29.99 filament_density = 1.27 -filament_type = NYLON +filament_type = PA extrusion_multiplier = 0.98 -bed_temperature = 60 -first_layer_bed_temperature = 60 +bed_temperature = 115 +first_layer_bed_temperature = 105 first_layer_temperature = 265 temperature = 270 fan_always_on = 0 @@ -2396,7 +2406,7 @@ filament_retract_lift = 0.4 filament_max_volumetric_speed = 4 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.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.08{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" filament_spool_weight = 0 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" 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 = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and printer_model!="MINI" 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:addnorth Adura X @MINI] inherits = addnorth Adura X @@ -2404,14 +2414,24 @@ filament_retract_length = nil filament_retract_lift = nil filament_retract_speed = 40 filament_deretract_speed = 25 +bed_temperature = 60 +first_layer_bed_temperature = 60 compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MINI" [filament:addnorth Adura X @MMU1] inherits = addnorth Adura X filament_retract_length = nil filament_retract_lift = nil +bed_temperature = 60 +first_layer_bed_temperature = 60 compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MK2SMM" +[filament:addnorth Adura X @MK2] +inherits = addnorth Adura X +bed_temperature = 110 +first_layer_bed_temperature = 105 +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! (printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and single_extruder_multi_material) + [filament:addnorth E-PLA] inherits = *PLA* filament_vendor = addnorth @@ -2684,6 +2704,75 @@ filament_cost = 25.4 filament_density = 1.24 compatible_printers_condition = nozzle_diameter[0]!=0.8 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +[filament:3D-Fuel Standard PLA] +inherits = *PLA* +filament_vendor = 3D-Fuel +filament_cost = 22.14 +filament_density = 1.24 +filament_max_volumetric_speed = 10 +first_layer_temperature = 210 +temperature = 200 + +[filament:3D-Fuel EasiPrint PLA] +inherits = 3D-Fuel Standard PLA +filament_cost = 30.44 + +[filament:3D-Fuel Pro PLA] +inherits = *PLA* +filament_vendor = 3D-Fuel +filament_cost = 26.57 +filament_density = 1.22 +filament_max_volumetric_speed = 12 +first_layer_temperature = 220 +temperature = 215 +filament_retract_lift = 0 + +[filament:3D-Fuel Buzzed] +inherits = 3D-Fuel Standard PLA +filament_cost = 44.27 +filament_retract_lift = 0 +first_layer_temperature = 210 +temperature = 195 +filament_max_volumetric_speed = 8 + +[filament:3D-Fuel Wound up] +inherits = 3D-Fuel Buzzed +filament_cost = 44.27 +filament_retract_lift = nil +first_layer_temperature = 215 +temperature = 210 +filament_max_volumetric_speed = 8 + +[filament:3D-Fuel Workday ABS] +inherits = *ABSC* +filament_vendor = 3D-Fuel +filament_cost = 23.25 +filament_density = 1.04 + +[filament:3D-Fuel Workday ABS @MINI] +inherits = 3D-Fuel Workday ABS; *ABSMINI* + +[filament:Jessie PLA] +inherits = *PLA* +filament_vendor = Printed Solid +filament_cost = 21 +filament_density = 1.24 +filament_max_volumetric_speed = 12 + +[filament:Jessie PETG] +inherits = *PET* +filament_vendor = Printed Solid +filament_cost = 22 +filament_density = 1.27 +first_layer_temperature = 240 +first_layer_bed_temperature = 85 +temperature = 245 +bed_temperature = 90 +filament_max_volumetric_speed = 7 + +[filament:Jessie PETG @MINI] +inherits = Jessie PETG; *PETMINI* + [filament:Devil Design PLA] inherits = *PLA* filament_vendor = Devil Design @@ -2941,7 +3030,7 @@ bed_temperature = 115 fan_always_on = 0 cooling = 0 bridge_fan_speed = 25 -filament_type = NYLON +filament_type = PA filament_max_volumetric_speed = 8 compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) @@ -4085,7 +4174,7 @@ filament_cost = 27.65 [filament:Fiberlogy Nylon PA12] inherits = Fiberlogy ASA -filament_type = NYLON +filament_type = PA filament_density = 1.01 filament_cost = 48 first_layer_bed_temperature = 105 @@ -4318,7 +4407,7 @@ inherits = *common* filament_vendor = Taulman filament_cost = 40 filament_density = 1.13 -bed_temperature = 90 +bed_temperature = 110 bridge_fan_speed = 40 cooling = 0 disable_fan_first_layers = 3 @@ -4327,13 +4416,22 @@ fan_below_layer_time = 20 filament_colour = #DEE0E6 filament_max_volumetric_speed = 7 filament_soluble = 0 -filament_type = NYLON -first_layer_bed_temperature = 60 -first_layer_temperature = 240 +filament_type = PA +first_layer_bed_temperature = 90 +first_layer_temperature = 260 +temperature = 260 max_fan_speed = 0 min_fan_speed = 0 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.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.08{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" +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:Taulman Bridge @MINI] +inherits = Taulman Bridge +bed_temperature = 90 +first_layer_bed_temperature = 60 +first_layer_temperature = 240 temperature = 250 +compatible_printers_condition = printer_model=="MINI" [filament:Fillamentum Nylon FX256] inherits = *common* @@ -4352,7 +4450,7 @@ slowdown_below_layer_time = 20 filament_colour = #DEE0E6 filament_max_volumetric_speed = 6 filament_soluble = 0 -filament_type = NYLON +filament_type = PA first_layer_bed_temperature = 90 first_layer_temperature = 250 max_fan_speed = 0 @@ -4365,10 +4463,10 @@ inherits = *common* filament_vendor = Fiberthree filament_cost = 200.84 filament_density = 1.2 -bed_temperature = 70 -first_layer_bed_temperature = 75 -first_layer_temperature = 270 -temperature = 270 +bed_temperature = 90 +first_layer_bed_temperature = 90 +first_layer_temperature = 285 +temperature = 285 bridge_fan_speed = 30 cooling = 1 disable_fan_first_layers = 3 @@ -4379,7 +4477,7 @@ slowdown_below_layer_time = 10 filament_colour = #DEE0E6 filament_max_volumetric_speed = 5 filament_soluble = 0 -filament_type = NYLON +filament_type = PA max_fan_speed = 20 min_fan_speed = 20 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.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.04{else}0.05{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K18{elsif nozzle_diameter[0]==0.8};{else}M900 K30{endif} ; Filament gcode LA 1.0" @@ -4395,10 +4493,10 @@ inherits = *common* filament_vendor = Fiberthree filament_cost = 208.1 filament_density = 1.25 -bed_temperature = 70 -first_layer_bed_temperature = 75 -first_layer_temperature = 275 -temperature = 275 +bed_temperature = 90 +first_layer_bed_temperature = 90 +first_layer_temperature = 285 +temperature = 285 bridge_fan_speed = 30 cooling = 1 disable_fan_first_layers = 3 @@ -4409,7 +4507,7 @@ slowdown_below_layer_time = 10 filament_colour = #DEE0E6 filament_max_volumetric_speed = 5 filament_soluble = 0 -filament_type = NYLON +filament_type = PA max_fan_speed = 0 min_fan_speed = 0 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.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.04{else}0.05{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K18{elsif nozzle_diameter[0]==0.8};{else}M900 K30{endif} ; Filament gcode LA 1.0" @@ -4695,6 +4793,10 @@ compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0. [filament:Fiberthree F3 PA Pure Pro @MINI] inherits = Fiberthree F3 PA Pure Pro filament_max_volumetric_speed = 4 +first_layer_temperature = 280 +temperature = 280 +first_layer_bed_temperature = 75 +bed_temperature = 70 filament_retract_length = nil filament_retract_speed = nil filament_retract_lift = nil @@ -4704,6 +4806,10 @@ compatible_printers_condition = printer_model=="MINI" [filament:Fiberthree F3 PA-CF Pro @MINI] inherits = Fiberthree F3 PA-CF Pro +first_layer_temperature = 280 +temperature = 280 +first_layer_bed_temperature = 75 +bed_temperature = 70 filament_max_volumetric_speed = 4 filament_retract_length = nil filament_retract_speed = nil @@ -5083,6 +5189,16 @@ filament_max_volumetric_speed = 13 filament_retract_lift = 0.25 compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]==0.8 and printer_model!="MINI" and ! single_extruder_multi_material +[filament:Prusament PA11 Carbon Fiber @0.8 nozzle] +inherits = Prusament PA11 Carbon Fiber +filament_max_volumetric_speed = 11 +compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]==0.8 and printer_model!="MINI" and ! single_extruder_multi_material + +[filament:Prusament PA11 Carbon Fiber @0.8 nozzle MK2] +inherits = Prusament PA11 Carbon Fiber @MK2 +filament_max_volumetric_speed = 11 +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! (printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and single_extruder_multi_material) + [filament:Prusament PC Blend @0.8 nozzle MK2] inherits = Prusament PC Blend @MK2 filament_max_volumetric_speed = 13 @@ -8839,7 +8955,7 @@ inherits = Original Prusa i3 MK2S printer_model = MK2.5 remaining_times = 1 machine_max_jerk_e = 4.5 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.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 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.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\n{if filament_settings_id[initial_tool]=~/.*Prusament PA11.*/}\nG1 Z0.3 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E9 F1000 ; intro line\n{else}\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\n{endif}\nG92 E0 [printer:Original Prusa i3 MK2.5 0.25 nozzle] inherits = Original Prusa i3 MK2S 0.25 nozzle @@ -8853,7 +8969,8 @@ inherits = Original Prusa i3 MK2S 0.6 nozzle printer_model = MK2.5 remaining_times = 1 machine_max_jerk_e = 4.5 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.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 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 +deretract_speed = 25 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.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\n{if filament_settings_id[initial_tool]=~/.*Prusament PA11.*/}\nG1 Z0.3 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E9 F1000 ; intro line\n{else}\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\n{endif}\nG92 E0 [printer:Original Prusa i3 MK2.5 0.8 nozzle] inherits = Original Prusa i3 MK2S 0.6 nozzle @@ -8862,10 +8979,13 @@ nozzle_diameter = 0.8 printer_variant = 0.8 max_layer_height = 0.6 min_layer_height = 0.2 -retract_length = 1 +retract_length = 0.7 +retract_speed = 35 +deretract_speed = 20 +retract_lift = 0.25 remaining_times = 1 machine_max_jerk_e = 4.5 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.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 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.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\n{if filament_settings_id[initial_tool]=~/.*Prusament PA11.*/}\nG1 Z0.3 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E9 F1000 ; intro line\n{else}\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\n{endif}\nG92 E0 default_print_profile = 0.40mm QUALITY @0.8 nozzle default_filament_profile = Prusament PLA @0.8 nozzle color_change_gcode = M600\nG1 E0.6 F1500 ; prime after color change @@ -8939,7 +9059,10 @@ max_layer_height = 0.6 min_layer_height = 0.2 nozzle_diameter = 0.8 printer_variant = 0.8 -retract_length = 1 +retract_length = 0.7 +retract_speed = 35 +deretract_speed = 20 +retract_lift = 0.25 start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.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\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n default_print_profile = 0.40mm QUALITY @0.8 nozzle default_filament_profile = Prusament PLA @0.8 nozzle @@ -8952,6 +9075,7 @@ max_layer_height = 0.35 min_layer_height = 0.1 nozzle_diameter = 0.6 printer_variant = 0.6 +deretract_speed = 25 default_print_profile = 0.20mm NORMAL @0.6 nozzle color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change @@ -8985,6 +9109,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 +deretract_speed = 25 default_print_profile = 0.20mm NORMAL @0.6 nozzle color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change @@ -8994,6 +9119,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 +deretract_speed = 25 default_print_profile = 0.20mm NORMAL @0.6 nozzle color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change @@ -9067,7 +9193,7 @@ 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.11.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 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.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\n{if filament_settings_id[initial_tool]=~/.*Prusament PA11.*/}\nG1 Z0.3 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E9 F1000 ; intro line\n{else}\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\n{endif}\nG92 E0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} printer_model = MK3 default_print_profile = 0.15mm QUALITY @MK3 thumbnails = 160x120 @@ -9089,7 +9215,8 @@ nozzle_diameter = 0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.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 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0\nM221 S{if layer_height<0.075}100{else}95{endif} +deretract_speed = 25 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.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\n{if filament_settings_id[initial_tool]=~/.*Prusament PA11.*/}\nG1 Z0.3 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E9 F1000 ; intro line\n{else}\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\n{endif}\nG92 E0\nM221 S{if layer_height<0.075}100{else}95{endif} default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change @@ -9099,8 +9226,11 @@ nozzle_diameter = 0.8 max_layer_height = 0.6 min_layer_height = 0.2 printer_variant = 0.8 -retract_length = 1 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.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 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0\nM221 S95 +retract_length = 0.7 +retract_speed = 35 +deretract_speed = 20 +retract_lift = 0.25 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.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\n{if filament_settings_id[initial_tool]=~/.*Prusament PA11.*/}\nG1 Z0.3 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E9 F1000 ; intro line\n{else}\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\n{endif}\nG92 E0\nM221 S95 default_print_profile = 0.40mm QUALITY @0.8 nozzle default_filament_profile = Prusament PLA @0.8 nozzle color_change_gcode = M600\nG1 E0.6 F1500 ; prime after color change @@ -9182,6 +9312,7 @@ nozzle_diameter = 0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 +deretract_speed = 25 start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.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\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change @@ -9193,7 +9324,10 @@ nozzle_diameter = 0.8 max_layer_height = 0.6 min_layer_height = 0.2 printer_variant = 0.8 -retract_length = 1 +retract_length = 0.7 +retract_speed = 35 +deretract_speed = 20 +retract_lift = 0.25 start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.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\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.40mm QUALITY @0.8 nozzle default_filament_profile = Prusament PLA @0.8 nozzle @@ -9235,6 +9369,7 @@ nozzle_diameter = 0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 +deretract_speed = 25 start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.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\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change @@ -9246,7 +9381,10 @@ nozzle_diameter = 0.8 max_layer_height = 0.6 min_layer_height = 0.2 printer_variant = 0.8 -retract_length = 1 +retract_length = 0.7 +retract_speed = 35 +deretract_speed = 20 +retract_lift = 0.25 start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.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\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.40mm QUALITY @0.8 nozzle default_filament_profile = Prusament PLA @0.8 nozzle @@ -9283,6 +9421,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 +deretract_speed = 25 start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.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\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 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E29 F1073\nG1 X5 E29 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change @@ -9293,6 +9432,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 +deretract_speed = 25 start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.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\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 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E32 F1073\nG1 X5 E32 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change @@ -9424,8 +9564,10 @@ max_layer_height = 0.55 min_layer_height = 0.2 default_print_profile = 0.40mm QUALITY @0.8 nozzle MINI default_filament_profile = Prusament PLA @0.8 nozzle -retract_length = 3.5 +retract_length = 3 retract_before_travel = 1.5 +retract_speed = 45 +deretract_speed = 20 [printer:Original Prusa SL1] printer_technology = SLA diff --git a/resources/profiles/gCreate.idx b/resources/profiles/gCreate.idx index 70770916c..ad3382435 100644 --- a/resources/profiles/gCreate.idx +++ b/resources/profiles/gCreate.idx @@ -1,3 +1,4 @@ min_slic3r_version = 2.4.0-alpha0 +1.0.1 Speed improvements, start gcode changes, added HIPS filament. 1.0.0 Initial version diff --git a/resources/profiles/gCreate.ini b/resources/profiles/gCreate.ini index 61d6047ce..39cd17edc 100644 --- a/resources/profiles/gCreate.ini +++ b/resources/profiles/gCreate.ini @@ -1,14 +1,14 @@ # Print profiles for the gCreate printers. # -# GTL Modified 210706 (at gCreate Shop) -# +# GTL Modified 220825 (at gCreate Shop) +# Speed improvements, start/end gCode changes [vendor] # Vendor name will be shown by the Config Wizard. name = gCreate # 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.0 +config_version = 1.0.1 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/gCreate/ # changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% @@ -548,6 +548,7 @@ compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_GCREATE.*/ # Common filament preset across all PLA filaments [filament:*PLA*] inherits = *common* +extrusion_multiplier = 0.94 bed_temperature = 60 fan_below_layer_time = 15 filament_colour = #FF3232 @@ -749,11 +750,20 @@ filament_vendor = ProtoPasta temperature = 250 bed_temperature = 70 +[filament:Generic HIPS @GCREATE] +inherits = *PET* +filament_vendor = Generic +filament_density = 1.04 +temperature = 230 +first_layer_temperature = 235 +bed_temperature = 100 +first_layer_bed_temperature = 100 +filament_type = HIPS + [filament:Generic TPU 90A @GCREATE] inherits = *TPU90A* filament_vendor = Generic - [filament:Generic CF PETG @GCREATE] inherits = *CFPETG* filament_vendor = Generic @@ -770,8 +780,6 @@ filament_vendor = Generic inherits = *WOODFILLPLA* filament_vendor = ColorFabb - - [filament:Generic PVA @GCREATE - PLA and PVA Support] bed_temperature = 0 bridge_fan_speed = 75 @@ -830,8 +838,8 @@ machine_max_feedrate_x = 500 machine_max_feedrate_y = 500 machine_max_feedrate_z = 10 machine_max_jerk_e = 2.5 -machine_max_jerk_x = 3 -machine_max_jerk_y = 3 +machine_max_jerk_x = 10 +machine_max_jerk_y = 10 machine_max_jerk_z = 0.4 machine_min_extruding_rate = 0 machine_min_travel_rate = 0 @@ -893,7 +901,7 @@ retract_speed = 70 deretract_speed = 40 retract_before_wipe = 70% default_print_profile = 0.20mm - Standard Layers @GCREATE -start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 ; home all\nG92 E0.0 +start_gcode = M420 Z20\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 ; home all\nG92 E0.0 end_gcode = M104 S0 T0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+5, max_print_height)} F600{endif} ; Move print head up\nG1 X5 Y170 F3000 ; present print\n{if layer_z < max_print_height-10}G1 Z{z_offset+min(layer_z+70, max_print_height-10)} F600{endif} ; Move print head up\nM84 X Y E ; disable motors #[printer:*abl*] @@ -906,6 +914,7 @@ printer_model = GMAX2PRO max_layer_height = 0.7 min_layer_height = 0.08 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_GCREATE\nPRINTER_MODEL_GMAX2PRO +start_gcode = G90\nG28\nM420 Z20\nG1 Z5 F5000 bed_shape = 0x0,457x0,457x457,0x457 max_print_height = 610 @@ -929,7 +938,7 @@ printer_model = GMAX2DUAL2IN1 max_layer_height = 0.7 min_layer_height = 0.08 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_GCREATE\nPRINTER_MODEL_GMAX2DUAL2IN1 -start_gcode = G90 ;\n G28 ;\n G1 Z5 F5000 +start_gcode = G90\nG28\nM420 Z20\nG1 Z5 F5000 bed_shape = 0x0,457x0,457x457,0x457 max_print_height = 610 default_print_profile = 0.20mm - Standard Layers @GCREATE @@ -951,7 +960,7 @@ printer_model = GMAX2DUAL max_layer_height = 0.7 min_layer_height = 0.08 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_GCREATE\nPRINTER_MODEL_GMAX2DUAL -start_gcode = G90\nG28\nG1 Z5 F5000\nM218 T1 X20 Y0 ; Set second extruder offset +start_gcode = G90\nG28\nM420 Z20\nG1 Z5 F5000\nM218 T1 X20 Y0 ; Set second extruder offset end_gcode = M104 S0 T0 ; turn off temperature\nM104 S0 T1 ; turn off 2nd extruder\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+5, max_print_height)} F600{endif} ; Move print head up\nG1 X5 Y170 F3000 ; present print\n{if layer_z < max_print_height-10}G1 Z{z_offset+min(layer_z+70, max_print_height-10)} F600{endif} ; Move print head up\nM84 X Y E ; disable motors bed_shape = 0x0,457x0,457x457,0x457 max_print_height = 610 diff --git a/sandboxes/aabb-evaluation/aabb-evaluation.cpp b/sandboxes/aabb-evaluation/aabb-evaluation.cpp index 1019ecf28..c11af70e4 100644 --- a/sandboxes/aabb-evaluation/aabb-evaluation.cpp +++ b/sandboxes/aabb-evaluation/aabb-evaluation.cpp @@ -6,8 +6,6 @@ #include #include -#include - #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable: 4244) @@ -42,53 +40,42 @@ void profile(const TriangleMesh &mesh) Eigen::MatrixXd occlusion_output0; { - AABBTreeIndirect::Tree3f tree; - { - PROFILE_BLOCK(AABBIndirect_Init); - tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(mesh.its.vertices, mesh.its.indices); - } - { - PROFILE_BLOCK(EigenMesh3D_AABBIndirectF_AmbientOcclusion); - occlusion_output0.resize(num_vertices, 1); - for (int ivertex = 0; ivertex < num_vertices; ++ ivertex) { - const Eigen::Vector3d origin = mesh.its.vertices[ivertex].template cast(); - const Eigen::Vector3d normal = vertex_normals.row(ivertex).template cast(); - int num_hits = 0; - for (int s = 0; s < num_samples; s++) { - Eigen::Vector3d d = dirs.row(s); - if(d.dot(normal) < 0) { - // reverse ray - d *= -1; - } - igl::Hit hit; - if (AABBTreeIndirect::intersect_ray_first_hit(mesh.its.vertices, mesh.its.indices, tree, (origin + 1e-4 * d).eval(), d, hit)) - ++ num_hits; + AABBTreeIndirect::Tree3f tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(mesh.its.vertices, mesh.its.indices); + occlusion_output0.resize(num_vertices, 1); + for (int ivertex = 0; ivertex < num_vertices; ++ ivertex) { + const Eigen::Vector3d origin = mesh.its.vertices[ivertex].template cast(); + const Eigen::Vector3d normal = vertex_normals.row(ivertex).template cast(); + int num_hits = 0; + for (int s = 0; s < num_samples; s++) { + Eigen::Vector3d d = dirs.row(s); + if(d.dot(normal) < 0) { + // reverse ray + d *= -1; } - occlusion_output0(ivertex) = (double)num_hits/(double)num_samples; + igl::Hit hit; + if (AABBTreeIndirect::intersect_ray_first_hit(mesh.its.vertices, mesh.its.indices, tree, (origin + 1e-4 * d).eval(), d, hit)) + ++ num_hits; } + occlusion_output0(ivertex) = (double)num_hits/(double)num_samples; } - - { - PROFILE_BLOCK(EigenMesh3D_AABBIndirectFF_AmbientOcclusion); - occlusion_output0.resize(num_vertices, 1); - for (int ivertex = 0; ivertex < num_vertices; ++ ivertex) { - const Eigen::Vector3d origin = mesh.its.vertices[ivertex].template cast(); - const Eigen::Vector3d normal = vertex_normals.row(ivertex).template cast(); - int num_hits = 0; - for (int s = 0; s < num_samples; s++) { - Eigen::Vector3d d = dirs.row(s); - if(d.dot(normal) < 0) { - // reverse ray - d *= -1; - } - igl::Hit hit; - if (AABBTreeIndirect::intersect_ray_first_hit(mesh.its.vertices, mesh.its.indices, tree, - Eigen::Vector3f((origin + 1e-4 * d).template cast()), - Eigen::Vector3f(d.template cast()), hit)) - ++ num_hits; + occlusion_output0.resize(num_vertices, 1); + for (int ivertex = 0; ivertex < num_vertices; ++ ivertex) { + const Eigen::Vector3d origin = mesh.its.vertices[ivertex].template cast(); + const Eigen::Vector3d normal = vertex_normals.row(ivertex).template cast(); + int num_hits = 0; + for (int s = 0; s < num_samples; s++) { + Eigen::Vector3d d = dirs.row(s); + if(d.dot(normal) < 0) { + // reverse ray + d *= -1; } - occlusion_output0(ivertex) = (double)num_hits/(double)num_samples; + igl::Hit hit; + if (AABBTreeIndirect::intersect_ray_first_hit(mesh.its.vertices, mesh.its.indices, tree, + Eigen::Vector3f((origin + 1e-4 * d).template cast()), + Eigen::Vector3f(d.template cast()), hit)) + ++ num_hits; } + occlusion_output0(ivertex) = (double)num_hits/(double)num_samples; } } @@ -100,31 +87,23 @@ void profile(const TriangleMesh &mesh) vertices.emplace_back(V.row(i).transpose()); for (int i = 0; i < F.rows(); ++ i) triangles.emplace_back(F.row(i).transpose()); - AABBTreeIndirect::Tree3d tree; - { - PROFILE_BLOCK(AABBIndirectD_Init); - tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(vertices, triangles); - } - - { - PROFILE_BLOCK(EigenMesh3D_AABBIndirectD_AmbientOcclusion); - occlusion_output1.resize(num_vertices, 1); - for (int ivertex = 0; ivertex < num_vertices; ++ ivertex) { - const Eigen::Vector3d origin = V.row(ivertex).template cast(); - const Eigen::Vector3d normal = vertex_normals.row(ivertex).template cast(); - int num_hits = 0; - for (int s = 0; s < num_samples; s++) { - Eigen::Vector3d d = dirs.row(s); - if(d.dot(normal) < 0) { - // reverse ray - d *= -1; - } - igl::Hit hit; - if (AABBTreeIndirect::intersect_ray_first_hit(vertices, triangles, tree, Eigen::Vector3d(origin + 1e-4 * d), d, hit)) - ++ num_hits; + AABBTreeIndirect::Tree3d tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(vertices, triangles); + occlusion_output1.resize(num_vertices, 1); + for (int ivertex = 0; ivertex < num_vertices; ++ ivertex) { + const Eigen::Vector3d origin = V.row(ivertex).template cast(); + const Eigen::Vector3d normal = vertex_normals.row(ivertex).template cast(); + int num_hits = 0; + for (int s = 0; s < num_samples; s++) { + Eigen::Vector3d d = dirs.row(s); + if(d.dot(normal) < 0) { + // reverse ray + d *= -1; } - occlusion_output1(ivertex) = (double)num_hits/(double)num_samples; + igl::Hit hit; + if (AABBTreeIndirect::intersect_ray_first_hit(vertices, triangles, tree, Eigen::Vector3d(origin + 1e-4 * d), d, hit)) + ++ num_hits; } + occlusion_output1(ivertex) = (double)num_hits/(double)num_samples; } } @@ -133,29 +112,23 @@ void profile(const TriangleMesh &mesh) Eigen::MatrixXd occlusion_output2; { igl::AABB AABB; - { - PROFILE_BLOCK(EigenMesh3D_AABB_Init); - AABB.init(V, F); - } - { - PROFILE_BLOCK(EigenMesh3D_AABB_AmbientOcclusion); - occlusion_output2.resize(num_vertices, 1); - for (int ivertex = 0; ivertex < num_vertices; ++ ivertex) { - const Eigen::Vector3d origin = V.row(ivertex).template cast(); - const Eigen::Vector3d normal = vertex_normals.row(ivertex).template cast(); - int num_hits = 0; - for (int s = 0; s < num_samples; s++) { - Eigen::Vector3d d = dirs.row(s); - if(d.dot(normal) < 0) { - // reverse ray - d *= -1; - } - igl::Hit hit; - if (AABB.intersect_ray(V, F, origin + 1e-4 * d, d, hit)) - ++ num_hits; + AABB.init(V, F); + occlusion_output2.resize(num_vertices, 1); + for (int ivertex = 0; ivertex < num_vertices; ++ ivertex) { + const Eigen::Vector3d origin = V.row(ivertex).template cast(); + const Eigen::Vector3d normal = vertex_normals.row(ivertex).template cast(); + int num_hits = 0; + for (int s = 0; s < num_samples; s++) { + Eigen::Vector3d d = dirs.row(s); + if(d.dot(normal) < 0) { + // reverse ray + d *= -1; } - occlusion_output2(ivertex) = (double)num_hits/(double)num_samples; + igl::Hit hit; + if (AABB.intersect_ray(V, F, origin + 1e-4 * d, d, hit)) + ++ num_hits; } + occlusion_output2(ivertex) = (double)num_hits/(double)num_samples; } } @@ -166,37 +139,28 @@ void profile(const TriangleMesh &mesh) igl::AABB AABB; auto vertices = MapMatrixXfUnaligned(mesh.its.vertices.front().data(), mesh.its.vertices.size(), 3); auto faces = MapMatrixXiUnaligned(mesh.its.indices.front().data(), mesh.its.indices.size(), 3); - { - PROFILE_BLOCK(EigenMesh3D_AABBf_Init); - AABB.init( - vertices, - faces); - } + AABB.init( + vertices, + faces); - { - PROFILE_BLOCK(EigenMesh3D_AABBf_AmbientOcclusion); - occlusion_output3.resize(num_vertices, 1); - for (int ivertex = 0; ivertex < num_vertices; ++ ivertex) { - const Eigen::Vector3d origin = mesh.its.vertices[ivertex].template cast(); - const Eigen::Vector3d normal = vertex_normals.row(ivertex).template cast(); - int num_hits = 0; - for (int s = 0; s < num_samples; s++) { - Eigen::Vector3d d = dirs.row(s); - if(d.dot(normal) < 0) { - // reverse ray - d *= -1; - } - igl::Hit hit; - if (AABB.intersect_ray(vertices, faces, (origin + 1e-4 * d).eval().template cast(), d.template cast(), hit)) - ++ num_hits; + occlusion_output3.resize(num_vertices, 1); + for (int ivertex = 0; ivertex < num_vertices; ++ ivertex) { + const Eigen::Vector3d origin = mesh.its.vertices[ivertex].template cast(); + const Eigen::Vector3d normal = vertex_normals.row(ivertex).template cast(); + int num_hits = 0; + for (int s = 0; s < num_samples; s++) { + Eigen::Vector3d d = dirs.row(s); + if(d.dot(normal) < 0) { + // reverse ray + d *= -1; } - occlusion_output3(ivertex) = (double)num_hits/(double)num_samples; + igl::Hit hit; + if (AABB.intersect_ray(vertices, faces, (origin + 1e-4 * d).eval().template cast(), d.template cast(), hit)) + ++ num_hits; } + occlusion_output3(ivertex) = (double)num_hits/(double)num_samples; } } - - PROFILE_UPDATE(); - PROFILE_OUTPUT(nullptr); } int main(const int argc, const char *argv[]) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 131a69b5a..50eccfc84 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,7 +9,6 @@ add_subdirectory(boost) add_subdirectory(clipper) add_subdirectory(miniz) add_subdirectory(glu-libtess) -add_subdirectory(Shiny) add_subdirectory(semver) add_subdirectory(libigl) add_subdirectory(hints) @@ -67,6 +66,7 @@ if (SLIC3R_GUI) if (NOT wxWidgets_FOUND) message(STATUS "Trying to find wxWidgets in CONFIG mode...") find_package(wxWidgets 3.2 CONFIG REQUIRED COMPONENTS html adv gl core base) + slic3r_remap_configs(wx::wxhtml wx::wxadv wx::wxgl wx::wxcore wx::wxbase RelWithDebInfo Release) else () include(${wxWidgets_USE_FILE}) endif () diff --git a/src/Shiny/CMakeLists.txt b/src/Shiny/CMakeLists.txt deleted file mode 100644 index abdb96a72..000000000 --- a/src/Shiny/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -cmake_minimum_required(VERSION 2.8.12) -project(Shiny) - -add_library(Shiny STATIC - Shiny.h - ShinyConfig.h - ShinyData.h - ShinyMacros.h - ShinyManager.c - ShinyManager.h - ShinyNode.c - ShinyNode.h - ShinyNodePool.c - ShinyNodePool.h - ShinyNodeState.c - ShinyNodeState.h - ShinyOutput.c - ShinyOutput.h - ShinyPrereqs.h - ShinyTools.c - ShinyTools.h - ShinyVersion.h - ShinyZone.c - ShinyZone.h -) diff --git a/src/Shiny/Shiny.h b/src/Shiny/Shiny.h deleted file mode 100644 index a2905aef0..000000000 --- a/src/Shiny/Shiny.h +++ /dev/null @@ -1,36 +0,0 @@ -/* -The MIT License - -Copyright (c) 2007-2010 Aidin Abedi http://code.google.com/p/shinyprofiler/ - -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. -*/ - -#ifndef SHINY_H -#define SHINY_H - -/*---------------------------------------------------------------------------*/ - -#include "ShinyMacros.h" - -#ifdef SLIC3R_PROFILE -#include "ShinyManager.h" -#endif /* SLIC3R_PROFILE */ - -#endif /* SHINY_H */ diff --git a/src/Shiny/ShinyConfig.h b/src/Shiny/ShinyConfig.h deleted file mode 100644 index 54c632151..000000000 --- a/src/Shiny/ShinyConfig.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -The MIT License - -Copyright (c) 2007-2010 Aidin Abedi http://code.google.com/p/shinyprofiler/ - -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. -*/ - -#ifndef SHINY_CONFIG_H -#define SHINY_CONFIG_H - - -/*---------------------------------------------------------------------------*/ - -/* if SHINY_LOOKUP_RATE is defined to TRUE then Shiny will record the success of its hash function. This is useful for debugging. Default is FALSE. - */ -#ifndef SHINY_LOOKUP_RATE -// #define SHINY_LOOKUP_RATE FALSE -#endif - -/* if SHINY_HAS_ENABLED is defined to TRUE then Shiny can be enabled and disabled at runtime. TODO: bla bla... - */ -#ifndef SHINY_HAS_ENABLED -// #define SHINY_HAS_ENABLED FALSE -#endif - -/* TODO: - */ -#define SHINY_OUTPUT_MODE_FLAT 0x1 - -/* TODO: - */ -#define SHINY_OUTPUT_MODE_TREE 0x2 - -/* TODO: - */ -#define SHINY_OUTPUT_MODE_BOTH 0x3 - -/* TODO: - */ -#ifndef SHINY_OUTPUT_MODE -#define SHINY_OUTPUT_MODE SHINY_OUTPUT_MODE_BOTH -#endif - -#endif /* SHINY_CONFIG_H */ diff --git a/src/Shiny/ShinyData.h b/src/Shiny/ShinyData.h deleted file mode 100644 index d75d4f5bf..000000000 --- a/src/Shiny/ShinyData.h +++ /dev/null @@ -1,102 +0,0 @@ -/* -The MIT License - -Copyright (c) 2007-2010 Aidin Abedi http://code.google.com/p/shinyprofiler/ - -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. -*/ - -#ifndef SHINY_DATA_H -#define SHINY_DATA_H - -#include "ShinyPrereqs.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/*---------------------------------------------------------------------------*/ - -typedef struct { - uint32_t entryCount; - shinytick_t selfTicks; -} ShinyLastData; - - -/*---------------------------------------------------------------------------*/ - -typedef struct { - shinytick_t cur; - float avg; -} ShinyTickData; - -typedef struct { - uint32_t cur; - float avg; -} ShinyCountData; - -typedef struct { - ShinyCountData entryCount; - ShinyTickData selfTicks; - ShinyTickData childTicks; -} ShinyData; - -SHINY_INLINE shinytick_t ShinyData_totalTicksCur(const ShinyData *self) { - return self->selfTicks.cur + self->childTicks.cur; -} - -SHINY_INLINE float ShinyData_totalTicksAvg(const ShinyData *self) { - return self->selfTicks.avg + self->childTicks.avg; -} - -SHINY_INLINE void ShinyData_computeAverage(ShinyData *self, float a_damping) { - self->entryCount.avg = self->entryCount.cur + - a_damping * (self->entryCount.avg - self->entryCount.cur); - self->selfTicks.avg = self->selfTicks.cur + - a_damping * (self->selfTicks.avg - self->selfTicks.cur); - self->childTicks.avg = self->childTicks.cur + - a_damping * (self->childTicks.avg - self->childTicks.cur); -} - -SHINY_INLINE void ShinyData_copyAverage(ShinyData *self) { - self->entryCount.avg = (float) self->entryCount.cur; - self->selfTicks.avg = (float) self->selfTicks.cur; - self->childTicks.avg = (float) self->childTicks.cur; -} - -SHINY_INLINE void ShinyData_clearAll(ShinyData *self) { - self->entryCount.cur = 0; - self->entryCount.avg = 0; - self->selfTicks.cur = 0; - self->selfTicks.avg = 0; - self->childTicks.cur = 0; - self->childTicks.avg = 0; -} - -SHINY_INLINE void ShinyData_clearCurrent(ShinyData *self) { - self->entryCount.cur = 0; - self->selfTicks.cur = 0; - self->childTicks.cur = 0; -} - -#if __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* SHINY_DATA_H */ diff --git a/src/Shiny/ShinyMacros.h b/src/Shiny/ShinyMacros.h deleted file mode 100644 index 79dfa4381..000000000 --- a/src/Shiny/ShinyMacros.h +++ /dev/null @@ -1,281 +0,0 @@ -/* -The MIT License - -Copyright (c) 2007-2010 Aidin Abedi http://code.google.com/p/shinyprofiler/ - -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. -*/ - -#ifndef SHINY_MACROS_H -#define SHINY_MACROS_H - -#ifdef SLIC3R_PROFILE - -#include "ShinyManager.h" - -/*---------------------------------------------------------------------------*/ -/* public preprocessors */ - -#define PROFILE_UPDATE() \ - ShinyManager_update(&Shiny_instance) - -#define PROFILE_SET_DAMPING(floatfrom0to1) \ - Shiny_instance.damping = (floatfrom0to1); - -#define PROFILE_GET_DAMPING() \ - (Shiny_instance.damping) - -#define PROFILE_OUTPUT(filename) \ - ShinyManager_output(&Shiny_instance, (filename)) - -#define PROFILE_OUTPUT_STREAM(stream) \ - ShinyManager_outputToStream(&Shiny_instance, (stream)) - -#ifdef __cplusplus -#define PROFILE_GET_TREE_STRING() \ - ShinyManager_outputTreeToString(&Shiny_instance) - -#define PROFILE_GET_FLAT_STRING() \ - ShinyManager_outputFlatToString(&Shiny_instance) -#endif /* __cplusplus */ - -#define PROFILE_DESTROY() \ - ShinyManager_destroy(&Shiny_instance) - -#define PROFILE_CLEAR() \ - ShinyManager_clear(&Shiny_instance) - -#define PROFILE_SORT_ZONES() \ - ShinyManager_sortZones(&Shiny_instance) - - -/*---------------------------------------------------------------------------*/ -/* public preprocessors */ - -#define PROFILE_GET_TOTAL_TICKS_CUR() \ - ShinyData_totalTicksCur(&Shiny_instance.rootZone.data) - -#define PROFILE_GET_TOTAL_TICKS() \ - ShinyData_totalTicksAvg(&Shiny_instance.rootZone.data) - -#define PROFILE_GET_PROFILED_TICKS_CUR() \ - (Shiny_instance.rootZone.data.selfTicks.cur) - -#define PROFILE_GET_PROFILED_TICKS() \ - (Shiny_instance.rootZone.data.selfTicks.avg) - -#define PROFILE_GET_UNPROFILED_TICKS_CUR() \ - (Shiny_instance.rootZone.data.childTicks.cur) - -#define PROFILE_GET_UNPROFILED_TICKS() \ - (Shiny_instance.rootZone.data.childTicks.avg) - -#define PROFILE_GET_SHARED_TOTAL_TICKS_CUR(name) \ - ShinyData_totalTicksCur(&(_PROFILE_ID_ZONE_SHARED(name).data)) - -#define PROFILE_GET_SHARED_TOTAL_TICKS(name) \ - ShinyData_totalTicksAvg(&(_PROFILE_ID_ZONE_SHARED(name).data)) - -#define PROFILE_GET_SHARED_SELF_TICKS_CUR(name) \ - (_PROFILE_ID_ZONE_SHARED(name).data.selfTicks.cur) - -#define PROFILE_GET_SHARED_SELF_TICKS(name) \ - (_PROFILE_ID_ZONE_SHARED(name).data.selfTicks.avg) - - -/*---------------------------------------------------------------------------*/ -/* public preprocessors */ - -#define PROFILE_IS_SHARED_SELF_BELOW(name, floatfrom0to1) \ - ShinyManager_isZoneSelfTimeBelow( \ - &Shiny_instance, _PROFILE_ID_ZONE_SHARED(name), floatfrom0to1) - -#define PROFILE_IS_SHARED_TOTAL_BELOW(name, floatfrom0to1) \ - ShinyManager_isZoneTotalTimeBelow( \ - &Shiny_instance, _PROFILE_ID_ZONE_SHARED(name), floatfrom0to1) - - -/*---------------------------------------------------------------------------*/ -/* public preprocessors */ - -#define PROFILE_END() \ - ShinyManager_endCurNode(&Shiny_instance) - - -/*---------------------------------------------------------------------------*/ -/* public preprocessors */ - -#define PROFILE_BEGIN( name ) \ - \ - static _PROFILE_ZONE_DEFINE(_PROFILE_ID_ZONE(name), #name); \ - _PROFILE_ZONE_BEGIN(_PROFILE_ID_ZONE(name)) - - -/*---------------------------------------------------------------------------*/ -/* public preprocessors */ - -#ifdef __cplusplus -#define PROFILE_BLOCK( name ) \ - \ - _PROFILE_BLOCK_DEFINE(_PROFILE_ID_BLOCK()); \ - PROFILE_BEGIN(name) -#endif /* __cplusplus */ - -/*---------------------------------------------------------------------------*/ -/* public preprocessors */ - -#ifdef __cplusplus -#define PROFILE_FUNC() \ - \ - _PROFILE_BLOCK_DEFINE(_PROFILE_ID_BLOCK()); \ - static _PROFILE_ZONE_DEFINE(_PROFILE_ID_ZONE_FUNC(), __FUNCTION__); \ - _PROFILE_ZONE_BEGIN(_PROFILE_ID_ZONE_FUNC()) -#endif /* __cplusplus */ - -/*---------------------------------------------------------------------------*/ -/* public preprocessors */ - -#define PROFILE_CODE( code ) \ - \ - do { \ - static _PROFILE_ZONE_DEFINE(_PROFILE_ID_ZONE_CODE(), #code); \ - _PROFILE_ZONE_BEGIN(_PROFILE_ID_ZONE_CODE()); \ - { code; } \ - PROFILE_END(); \ - } while(0) - - -/*---------------------------------------------------------------------------*/ -/* public preprocessors */ - -#define PROFILE_SHARED_EXTERN( name ) \ - \ - _PROFILE_ZONE_DECLARE(extern, _PROFILE_ID_ZONE_SHARED(name)) - - -/*---------------------------------------------------------------------------*/ -/* public preprocessors */ - -#define PROFILE_SHARED_DEFINE( name ) \ - \ - _PROFILE_ZONE_DEFINE(_PROFILE_ID_ZONE_SHARED(name), #name) - - -/*---------------------------------------------------------------------------*/ -/* public preprocessors */ - -#define PROFILE_SHARED_BEGIN( name ) \ - \ - _PROFILE_ZONE_BEGIN(_PROFILE_ID_ZONE_SHARED(name)) - - -/*---------------------------------------------------------------------------*/ -/* public preprocessors */ - -#ifdef __cplusplus -#define PROFILE_SHARED_BLOCK( name ) \ - \ - _PROFILE_BLOCK_DEFINE(_PROFILE_ID_BLOCK()); \ - _PROFILE_ZONE_BEGIN(_PROFILE_ID_ZONE_SHARED(name)) -#endif /* __cplusplus */ - - -/*---------------------------------------------------------------------------*/ -/* public preprocessors */ - -#ifdef SHINY_HAS_ENABLED -#define PROFILE_SET_ENABLED( boolean ) \ - Shiny_instance.enabled = boolean -#endif - - -/*---------------------------------------------------------------------------*/ -/* internal preprocessors */ - -#define _PROFILE_ID_ZONE( name ) __ShinyZone_##name -#define _PROFILE_ID_ZONE_FUNC() __ShinyZoneFunc -#define _PROFILE_ID_ZONE_CODE() __ShinyZoneCode -#define _PROFILE_ID_ZONE_SHARED( name ) name##__ShinyZoneShared -#define _PROFILE_ID_BLOCK() __ShinyBlock - - -/*---------------------------------------------------------------------------*/ -/* internal preprocessor */ - -#define _PROFILE_ZONE_DEFINE( id, string ) \ - \ - ShinyZone id = { \ - NULL, SHINY_ZONE_STATE_HIDDEN, string, \ - { { 0, 0 }, { 0, 0 }, { 0, 0 } } \ - } - - -/*---------------------------------------------------------------------------*/ -/* internal preprocessor */ - -#define _PROFILE_ZONE_DECLARE( prefix, id ) \ - \ - prefix ShinyZone id - - -/*---------------------------------------------------------------------------*/ -/* internal preprocessor */ - -#define _PROFILE_BLOCK_DEFINE( id ) \ - \ - ShinyEndNodeOnDestruction SHINY_UNUSED id - - -/*---------------------------------------------------------------------------*/ -/* internal preprocessor */ - -#define _PROFILE_ZONE_BEGIN( id ) \ - \ - do { \ - static ShinyNodeCache cache = &_ShinyNode_dummy; \ - ShinyManager_lookupAndBeginNode(&Shiny_instance, &cache, &id); \ - } while(0) - -/*---------------------------------------------------------------------------*/ - -#else /* SLIC3R_PROFILE */ - -#define PROFILE_UPDATE() -#define PROFILE_SET_DAMPING(x) -#define PROFILE_GET_DAMPING() 0.0f -#define PROFILE_OUTPUT(x) -#define PROFILE_OUTPUT_STREAM(x) -#define PROFILE_CLEAR() -#define PROFILE_GET_TREE_STRING() std::string() -#define PROFILE_GET_FLAT_STRING() std::string() -#define PROFILE_DESTROY() -#define PROFILE_BEGIN(name) -#define PROFILE_BLOCK(name) -#define PROFILE_FUNC() -#define PROFILE_CODE(code) do { code; } while (0) -#define PROFILE_SHARED_GLOBAL(name) -#define PROFILE_SHARED_MEMBER(name) -#define PROFILE_SHARED_DEFINE(name) -#define PROFILE_SHARED_BEGIN(name) -#define PROFILE_SHARED_BLOCK(name) -#define PROFILE_SET_ENABLED(boolean) - -#endif /* SLIC3R_PROFILE */ - -#endif /* SHINY_MACROS_H */ diff --git a/src/Shiny/ShinyManager.c b/src/Shiny/ShinyManager.c deleted file mode 100644 index 6b2811851..000000000 --- a/src/Shiny/ShinyManager.c +++ /dev/null @@ -1,445 +0,0 @@ -/* -The MIT License - -Copyright (c) 2007-2010 Aidin Abedi http://code.google.com/p/shinyprofiler/ - -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. -*/ - -#ifdef SLIC3R_PROFILE - -#include "ShinyManager.h" - -#include -#include -#include -#include - -/*---------------------------------------------------------------------------*/ - -#define TABLE_SIZE_INIT 256 - -/*---------------------------------------------------------------------------*/ - -ShinyManager Shiny_instance = { -#if SHINY_HAS_ENABLED == TRUE - /* enabled = */ false, -#endif - /* _lastTick = */ 0, - /* _curNode = */ &Shiny_instance.rootNode, - /* _tableMask = */ 0, - /* _nodeTable = */ _ShinyManager_dummyNodeTable, -#if SHINY_LOOKUP_RATE == TRUE - /* _lookupCount = */ 0, - /* _lookupSuccessCount = */ 0, -#endif - /* _tableSize = */ 1, - /* nodeCount = */ 1, - /* zoneCount = */ 1, - /* _lastZone = */ &Shiny_instance.rootZone, - /* _lastNodePool = */ NULL, - /* _firstNodePool = */ NULL, - /* rootNode = */ { - /* _last = */ { 0, 0 }, - /* zone = */ &Shiny_instance.rootZone, - /* parent = */ &Shiny_instance.rootNode, - /* nextSibling = */ NULL, - /* firstChild = */ NULL, - /* lastChild = */ NULL, - /* childCount = */ 0, - /* entryLevel = */ 0, - /* _cache = */ NULL, - /* data = */ { { 0, 0 }, { 0, 0 }, { 0, 0 } } - }, - /* rootZone = */ { - /* next = */ NULL, - /* _state = */ SHINY_ZONE_STATE_HIDDEN, - /* name = */ "", - /* data = */ { { 0, 0 }, { 0, 0 }, { 0, 0 } } - }, - /* damping = */ 0.f, // Damping disabled, every PROFILE_UPDATE will be performed from scratch. Original value: 0.9f - /* _initialized = */ FALSE, - /* _firstUpdate = */ TRUE -}; - -ShinyNode* _ShinyManager_dummyNodeTable[] = { NULL }; - - -/*---------------------------------------------------------------------------*/ - -#if SHINY_COMPILER == SHINY_COMPILER_MSVC -# pragma warning (push) -# pragma warning (disable: 4311) -#endif - -/* primary hash function */ -SHINY_INLINE uint32_t hash_value(void* a_pParent, void* a_pZone) { - uint32_t a = (uint32_t) a_pParent + (uint32_t) a_pZone; -// uint32_t a = *reinterpret_cast(&a_pParent) + *reinterpret_cast(&a_pZone); - - a = (a+0x7ed55d16) + (a<<12); - a = (a^0xc761c23c) ^ (a>>19); - return a; -} - -/* - * secondary hash used as index offset: force it to be odd - * so it's relatively prime to the power-of-two table size - */ -SHINY_INLINE uint32_t hash_offset(uint32_t a) { - return ((a << 8) + (a >> 4)) | 1; -} - -#if SHINY_COMPILER == SHINY_COMPILER_MSVC -# pragma warning (pop) -#endif - - -/*---------------------------------------------------------------------------*/ - -void ShinyManager_preLoad(ShinyManager *self) { - if (!self->_initialized) { - _ShinyManager_init(self); - - _ShinyManager_createNodeTable(self, TABLE_SIZE_INIT); - _ShinyManager_createNodePool(self, TABLE_SIZE_INIT / 2); - } -} - - -/*---------------------------------------------------------------------------*/ - -void ShinyManager_update(ShinyManager *self) { -#if SHINY_HAS_ENABLED == TRUE - if (!enabled) return; -#endif - - _ShinyManager_appendTicksToCurNode(self); - ShinyZone_preUpdateChain(&self->rootZone); - - if (self->_firstUpdate || self->damping == 0) { - self->_firstUpdate = FALSE; - ShinyNode_updateTreeClean(&self->rootNode); - ShinyZone_updateChainClean(&self->rootZone); - - } else { - ShinyNode_updateTree(&self->rootNode, self->damping); - ShinyZone_updateChain(&self->rootZone, self->damping); - } -} - - -/*---------------------------------------------------------------------------*/ - -void ShinyManager_updateClean(ShinyManager *self) { -#if SHINY_HAS_ENABLED == TRUE - if (!enabled) return; -#endif - - _ShinyManager_appendTicksToCurNode(self); - ShinyZone_preUpdateChain(&self->rootZone); - - self->_firstUpdate = FALSE; - ShinyNode_updateTreeClean(&self->rootNode); - ShinyZone_updateChainClean(&self->rootZone); -} - - -/*---------------------------------------------------------------------------*/ - -void ShinyManager_clear(ShinyManager *self) { - ShinyManager_destroy(self); - ShinyManager_preLoad(self); -} - - -/*---------------------------------------------------------------------------*/ - -void ShinyManager_destroy(ShinyManager *self) { - ShinyManager_destroyNodes(self); - ShinyManager_resetZones(self); - _ShinyManager_uninit(self); -} - - -/*---------------------------------------------------------------------------*/ - -ShinyNode* _ShinyManager_lookupNode(ShinyManager *self, ShinyNodeCache *a_cache, ShinyZone *a_zone) { - uint32_t nHash = hash_value(self->_curNode, a_zone); - uint32_t nIndex = nHash & self->_tableMask; - ShinyNode* pNode = self->_nodeTable[nIndex]; - - _ShinyManager_incLookup(self); - _ShinyManager_incLookupSuccess(self); - - if (pNode) { - uint32_t nStep; - - if (ShinyNode_isEqual(pNode, self->_curNode, a_zone)) return pNode; /* found it! */ - - /* hash collision: */ - - /* compute a secondary hash function for stepping */ - nStep = hash_offset(nHash); - - for (;;) { - _ShinyManager_incLookup(self); - - nIndex = (nIndex + nStep) & self->_tableMask; - pNode = self->_nodeTable[nIndex]; - - if (!pNode) break; /* found empty slot */ - else if (ShinyNode_isEqual(pNode, self->_curNode, a_zone)) return pNode; /* found it! */ - } - - /* loop is guaranteed to end because the hash table is never full */ - } - - if (a_zone->_state == SHINY_ZONE_STATE_HIDDEN) { /* zone is not initialized */ - ShinyZone_init(a_zone, self->_lastZone); - - self->_lastZone = a_zone; - self->zoneCount++; - - if (self->_initialized == FALSE) { /* first time init */ - _ShinyManager_init(self); - - _ShinyManager_createNodeTable(self, TABLE_SIZE_INIT); - _ShinyManager_createNodePool(self, TABLE_SIZE_INIT / 2); - - /* initialization has invalidated nIndex - * we must compute nIndex again - */ - return _ShinyManager_createNode(self, a_cache, a_zone); - } - } - - /* Althouth nodeCount is not updated - * it includes rootNode so it adds up. - * - * check if we need to grow the table - * we keep it at most 1/2 full to be very fast - */ - if (self->_tableSize < 2 * self->nodeCount) { - - _ShinyManager_resizeNodeTable(self, 2 * self->_tableSize); - _ShinyManager_resizeNodePool(self, self->nodeCount - 1); - - /* resize has invalidated nIndex - * we must compute nIndex again - */ - return _ShinyManager_createNode(self, a_cache, a_zone); - } - - self->nodeCount++; - - { - ShinyNode* pNewNode = ShinyNodePool_newItem(self->_lastNodePool); - ShinyNode_init(pNewNode, self->_curNode, a_zone, a_cache); - - self->_nodeTable[nIndex] = pNewNode; - return pNewNode; - } -} - - -/*---------------------------------------------------------------------------*/ - -void _ShinyManager_insertNode(ShinyManager *self, ShinyNode* a_pNode) { - uint32_t nHash = hash_value(a_pNode->parent, a_pNode->zone); - uint32_t nIndex = nHash & self->_tableMask; - - if (self->_nodeTable[nIndex]) { - uint32_t nStep = hash_offset(nHash); - - while (self->_nodeTable[nIndex]) - nIndex = (nIndex + nStep) & self->_tableMask; - } - - self->_nodeTable[nIndex] = a_pNode; -} - - -/*---------------------------------------------------------------------------*/ - -ShinyNode* _ShinyManager_createNode(ShinyManager *self, ShinyNodeCache* a_cache, ShinyZone* a_pZone) { - ShinyNode* pNewNode = ShinyNodePool_newItem(self->_lastNodePool); - ShinyNode_init(pNewNode, self->_curNode, a_pZone, a_cache); - - self->nodeCount++; - _ShinyManager_insertNode(self, pNewNode); - return pNewNode; -} - - -/*---------------------------------------------------------------------------*/ - -void _ShinyManager_createNodePool(ShinyManager *self, uint32_t a_nCount) { - self->_firstNodePool = ShinyNodePool_create(a_nCount); - self->_lastNodePool = self->_firstNodePool; -} - - -/*---------------------------------------------------------------------------*/ - -void _ShinyManager_resizeNodePool(ShinyManager *self, uint32_t a_nCount) { - ShinyNodePool* pPool = ShinyNodePool_create(a_nCount); - self->_lastNodePool->nextPool = pPool; - self->_lastNodePool = pPool; -} - - -/*---------------------------------------------------------------------------*/ - -void _ShinyManager_createNodeTable(ShinyManager *self, uint32_t a_nCount) { - self->_tableSize = a_nCount; - self->_tableMask = a_nCount - 1; - - self->_nodeTable = (ShinyNodeTable*) - malloc(sizeof(ShinyNode) * a_nCount); - - memset(self->_nodeTable, 0, a_nCount * sizeof(ShinyNode*)); -} - - -/*---------------------------------------------------------------------------*/ - -void _ShinyManager_resizeNodeTable(ShinyManager *self, uint32_t a_nCount) { - ShinyNodePool* pPool; - - free(self->_nodeTable); - _ShinyManager_createNodeTable(self, a_nCount); - - pPool = self->_firstNodePool; - while (pPool) { - - ShinyNode *pIter = ShinyNodePool_firstItem(pPool); - - while (pIter != pPool->_nextItem) - _ShinyManager_insertNode(self, pIter++); - - pPool = pPool->nextPool; - } -} - - -/*---------------------------------------------------------------------------*/ - -void ShinyManager_resetZones(ShinyManager *self) { - ShinyZone_resetChain(&self->rootZone); - self->_lastZone = &self->rootZone; - self->zoneCount = 1; -} - - -/*---------------------------------------------------------------------------*/ - -void ShinyManager_destroyNodes(ShinyManager *self) { - if (self->_firstNodePool) { - ShinyNodePool_destroy(self->_firstNodePool); - self->_firstNodePool = NULL; - } - - if (self->_nodeTable != _ShinyManager_dummyNodeTable) { - free(self->_nodeTable); - - self->_nodeTable = _ShinyManager_dummyNodeTable; - self->_tableSize = 1; - self->_tableMask = 0; - } - - self->_curNode = &self->rootNode; - self->nodeCount = 1; - - _ShinyManager_init(self); -} - - -/*---------------------------------------------------------------------------*/ - -const char* ShinyManager_getOutputErrorString(ShinyManager *self) { - if (self->_firstUpdate) return "!!! Profile data must first be updated !!!"; - else if (!self->_initialized) return "!!! No profiles where executed !!!"; - else return NULL; -} - - -/*---------------------------------------------------------------------------*/ - -#if SHINY_COMPILER == SHINY_COMPILER_MSVC -# pragma warning (push) -# pragma warning (disable: 4996) -#endif - -int ShinyManager_output(ShinyManager *self, const char *a_filename) { - if (!a_filename) { - ShinyManager_outputToStream(self, stdout); - - } else { - FILE *file = fopen(a_filename, "w"); - if (!file) return FALSE; - ShinyManager_outputToStream(self, file); - fclose(file); - } - - return TRUE; -} - -#if SHINY_COMPILER == SHINY_COMPILER_MSVC -# pragma warning (pop) -#endif - - -/*---------------------------------------------------------------------------*/ - -void ShinyManager_outputToStream(ShinyManager *self, FILE *a_stream) { - const char *error = ShinyManager_getOutputErrorString(self); - - if (error) { - fwrite(error, 1, strlen(error), a_stream); - fwrite("\n\n", 1, 2, a_stream); - return; - } - -#if SHINY_OUTPUT_MODE & SHINY_OUTPUT_MODE_FLAT - ShinyManager_sortZones(self); - - { - int size = ShinyPrintZonesSize(self->zoneCount); - char *buffer = (char*) malloc(size); - ShinyPrintZones(buffer, &self->rootZone); - fwrite(buffer, 1, size - 1, a_stream); - fwrite("\n\n", 1, 2, a_stream); - free(buffer); - } -#endif - -#if SHINY_OUTPUT_MODE & SHINY_OUTPUT_MODE_TREE - { - int size = ShinyPrintNodesSize(self->nodeCount); - char *buffer = (char*) malloc(size); - ShinyPrintNodes(buffer, &self->rootNode); - fwrite(buffer, 1, size - 1, a_stream); - fwrite("\n\n", 1, 2, a_stream); - free(buffer); - } -#endif -} - -#endif /* SLIC3R_PROFILE */ diff --git a/src/Shiny/ShinyManager.h b/src/Shiny/ShinyManager.h deleted file mode 100644 index 403e0e878..000000000 --- a/src/Shiny/ShinyManager.h +++ /dev/null @@ -1,263 +0,0 @@ -/* -The MIT License - -Copyright (c) 2007-2010 Aidin Abedi http://code.google.com/p/shinyprofiler/ - -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. -*/ - -#ifndef SHINY_MANAGER_H -#define SHINY_MANAGER_H - -#include "ShinyZone.h" -#include "ShinyNode.h" -#include "ShinyNodePool.h" -#include "ShinyTools.h" -#include "ShinyOutput.h" - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/*---------------------------------------------------------------------------*/ - -typedef struct { -#ifdef SHINY_HAS_ENABLED - bool enabled; -#endif - - shinytick_t _lastTick; - - ShinyNode* _curNode; - - uint32_t _tableMask; /* = _tableSize - 1 */ - - ShinyNodeTable* _nodeTable; - -#ifdef SHINY_LOOKUP_RATE - uint64_t _lookupCount; - uint64_t _lookupSuccessCount; -#endif - - uint32_t _tableSize; - - uint32_t nodeCount; - uint32_t zoneCount; - - ShinyZone* _lastZone; - - ShinyNodePool* _lastNodePool; - ShinyNodePool* _firstNodePool; - - ShinyNode rootNode; - ShinyZone rootZone; - - float damping; - - int _initialized; - int _firstUpdate; -} ShinyManager; - - -/*---------------------------------------------------------------------------*/ - -extern ShinyNode* _ShinyManager_dummyNodeTable[]; - -extern ShinyManager Shiny_instance; - - -/*---------------------------------------------------------------------------*/ - -SHINY_INLINE void _ShinyManager_appendTicksToCurNode(ShinyManager *self) { - shinytick_t curTick; - ShinyGetTicks(&curTick); - - ShinyNode_appendTicks(self->_curNode, curTick - self->_lastTick); - self->_lastTick = curTick; -} - -SHINY_API ShinyNode* _ShinyManager_lookupNode(ShinyManager *self, ShinyNodeCache* a_cache, ShinyZone* a_zone); - -SHINY_API void _ShinyManager_createNodeTable(ShinyManager *self, uint32_t a_count); -SHINY_API void _ShinyManager_resizeNodeTable(ShinyManager *self, uint32_t a_count); - -SHINY_API void _ShinyManager_createNodePool(ShinyManager *self, uint32_t a_count); -SHINY_API void _ShinyManager_resizeNodePool(ShinyManager *self, uint32_t a_count); - -SHINY_API ShinyNode* _ShinyManager_createNode(ShinyManager *self, ShinyNodeCache* a_cache, ShinyZone* a_pZone); -SHINY_API void _ShinyManager_insertNode(ShinyManager *self, ShinyNode* a_pNode); - -SHINY_INLINE void _ShinyManager_init(ShinyManager *self) { - self->_initialized = TRUE; - - self->rootNode._last.entryCount = 1; - self->rootNode._last.selfTicks = 0; - ShinyGetTicks(&self->_lastTick); -} - -SHINY_INLINE void _ShinyManager_uninit(ShinyManager *self) { - self->_initialized = FALSE; - - ShinyNode_clear(&self->rootNode); - self->rootNode.parent = &self->rootNode; - self->rootNode.zone = &self->rootZone; -} - -#ifdef SHINY_LOOKUP_RATE -SHINY_INLINE void _ShinyManager_incLookup(ShinyManager *self) { self->_lookupCount++; } -SHINY_INLINE void _ShinyManager_incLookupSuccess(ShinyManager *self) { self->_lookupSuccessCount++; } -SHINY_INLINE float ShinyManager_lookupRate(const ShinyManager *self) { return ((float) self->_lookupSuccessCount) / ((float) self->_lookupCount); } - -#else -SHINY_INLINE void _ShinyManager_incLookup(ShinyManager * self) { self = self; } -SHINY_INLINE void _ShinyManager_incLookupSuccess(ShinyManager * self) { self = self; } -SHINY_INLINE float ShinyManager_lookupRate(const ShinyManager * self) { self = self; return -1; } -#endif - -SHINY_API void ShinyManager_resetZones(ShinyManager *self); -SHINY_API void ShinyManager_destroyNodes(ShinyManager *self); - -SHINY_INLINE float ShinyManager_tableUsage(const ShinyManager *self) { - return ((float) self->nodeCount) / ((float) self->_tableSize); -} - -SHINY_INLINE uint32_t ShinyManager_allocMemInBytes(const ShinyManager *self) { - return self->_tableSize * sizeof(ShinyNode*) - + (self->_firstNodePool)? ShinyNodePool_memoryUsageChain(self->_firstNodePool) : 0; -} - -SHINY_INLINE void ShinyManager_beginNode(ShinyManager *self, ShinyNode* a_node) { - ShinyNode_beginEntry(a_node); - - _ShinyManager_appendTicksToCurNode(self); - self->_curNode = a_node; -} - -SHINY_INLINE void ShinyManager_lookupAndBeginNode(ShinyManager *self, ShinyNodeCache* a_cache, ShinyZone* a_zone) { -#ifdef SHINY_HAS_ENABLED - if (!self->enabled) return; -#endif - - if (self->_curNode != (*a_cache)->parent) - *a_cache = _ShinyManager_lookupNode(self, a_cache, a_zone); - - ShinyManager_beginNode(self, *a_cache); -} - -SHINY_INLINE void ShinyManager_endCurNode(ShinyManager *self) { -#ifdef SHINY_HAS_ENABLED - if (!self->enabled) return; -#endif - - _ShinyManager_appendTicksToCurNode(self); - self->_curNode = self->_curNode->parent; -} - -/**/ - -SHINY_API void ShinyManager_preLoad(ShinyManager *self); - -SHINY_API void ShinyManager_updateClean(ShinyManager *self); -SHINY_API void ShinyManager_update(ShinyManager *self); - -SHINY_API void ShinyManager_clear(ShinyManager *self); -SHINY_API void ShinyManager_destroy(ShinyManager *self); - -SHINY_INLINE void ShinyManager_sortZones(ShinyManager *self) { - if (self->rootZone.next) - self->_lastZone = ShinyZone_sortChain(&self->rootZone.next); -} - -SHINY_API const char* ShinyManager_getOutputErrorString(ShinyManager *self); - -SHINY_API int ShinyManager_output(ShinyManager *self, const char *a_filename); -SHINY_API void ShinyManager_outputToStream(ShinyManager *self, FILE *stream); - -#if __cplusplus -} /* end of extern "C" */ - -SHINY_INLINE std::string ShinyManager_outputTreeToString(ShinyManager *self) { - const char* error = ShinyManager_getOutputErrorString(self); - if (error) return error; - else return ShinyNodesToString(&self->rootNode, self->nodeCount); -} - -SHINY_INLINE std::string ShinyManager_outputFlatToString(ShinyManager *self) { - const char* error = ShinyManager_getOutputErrorString(self); - if (error) return error; - - ShinyManager_sortZones(self); - return ShinyZonesToString(&self->rootZone, self->zoneCount); -} - -extern "C" { /* end of c++ */ -#endif - -SHINY_INLINE int ShinyManager_isZoneSelfTimeBelow(ShinyManager *self, ShinyZone* a_zone, float a_percentage) { - return a_percentage * (float) self->rootZone.data.childTicks.cur - <= (float) a_zone->data.selfTicks.cur; -} - -SHINY_INLINE int ShinyManager_isZoneTotalTimeBelow(ShinyManager *self, ShinyZone* a_zone, float a_percentage) { - return a_percentage * (float) self->rootZone.data.childTicks.cur - <= (float) ShinyData_totalTicksCur(&a_zone->data); -} - -/**/ - -SHINY_INLINE void ShinyManager_enumerateNodes(ShinyManager *self, void (*a_func)(const ShinyNode*)) { - ShinyNode_enumerateNodes(&self->rootNode, a_func); -} - -SHINY_INLINE void ShinyManager_enumerateZones(ShinyManager *self, void (*a_func)(const ShinyZone*)) { - ShinyZone_enumerateZones(&self->rootZone, a_func); -} - -#if __cplusplus -} /* end of extern "C" */ - -template void ShinyManager_enumerateNodes(ShinyManager *self, T* a_this, void (T::*a_func)(const ShinyNode*)) { - ShinyNode_enumerateNodes(&self->rootNode, a_this, a_func); -} - -template void ShinyManager_enumerateZones(ShinyManager *self, T* a_this, void (T::*a_func)(const ShinyZone*)) { - ShinyZone_enumerateZones(&self->rootZone, a_this, a_func); -} - -extern "C" { /* end of c++ */ -#endif - - -/*---------------------------------------------------------------------------*/ - -#if __cplusplus -} /* end of extern "C" */ - -class ShinyEndNodeOnDestruction { -public: - - SHINY_INLINE ~ShinyEndNodeOnDestruction() { - ShinyManager_endCurNode(&Shiny_instance); - } -}; -#endif - -#endif /* SHINY_MANAGER_H */ diff --git a/src/Shiny/ShinyNode.c b/src/Shiny/ShinyNode.c deleted file mode 100644 index 9d777073b..000000000 --- a/src/Shiny/ShinyNode.c +++ /dev/null @@ -1,129 +0,0 @@ -/* -The MIT License - -Copyright (c) 2007-2010 Aidin Abedi http://code.google.com/p/shinyprofiler/ - -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. -*/ - -#ifdef SLIC3R_PROFILE - -#include "ShinyNode.h" -#include "ShinyZone.h" -#include "ShinyNodeState.h" - -#include - -/*---------------------------------------------------------------------------*/ - -ShinyNode _ShinyNode_dummy = { - /* _last = */ { 0, 0 }, - /* zone = */ NULL, - /* parent = */ NULL, - /* nextSibling = */ NULL, - /* firstChild = */ NULL, - /* lastChild = */ NULL -}; - - -/*---------------------------------------------------------------------------*/ - -void ShinyNode_updateTree(ShinyNode* first, float a_damping) { - ShinyNodeState *top = NULL; - ShinyNode *node = first; - - for (;;) { - do { - top = ShinyNodeState_push(top, node); - node = node->firstChild; - } while (node); - - for (;;) { - node = ShinyNodeState_finishAndGetNext(top, a_damping); - top = ShinyNodeState_pop(top); - - if (node) break; - else if (!top) return; - } - } -} - - -/*---------------------------------------------------------------------------*/ - -void ShinyNode_updateTreeClean(ShinyNode* first) { - ShinyNodeState *top = NULL; - ShinyNode *node = first; - - for (;;) { - do { - top = ShinyNodeState_push(top, node); - node = node->firstChild; - } while (node); - - for (;;) { - node = ShinyNodeState_finishAndGetNextClean(top); - top = ShinyNodeState_pop(top); - - if (node) break; - else if (!top) return; - } - } -} - - -/*---------------------------------------------------------------------------*/ - -const ShinyNode* ShinyNode_findNextInTree(const ShinyNode* self) { - if (self->firstChild) { - return self->firstChild; - - } else if (self->nextSibling) { - return self->nextSibling; - - } else { - ShinyNode* pParent = self->parent; - - while (!ShinyNode_isRoot(pParent)) { - if (pParent->nextSibling) return pParent->nextSibling; - else pParent = pParent->parent; - } - - return NULL; - } -} - - -/*---------------------------------------------------------------------------*/ - -void ShinyNode_clear(ShinyNode* self) { - memset(self, 0, sizeof(ShinyNode)); -} - - -/*---------------------------------------------------------------------------*/ - -void ShinyNode_enumerateNodes(const ShinyNode* a_node, void (*a_func)(const ShinyNode*)) { - a_func(a_node); - - if (a_node->firstChild) ShinyNode_enumerateNodes(a_node->firstChild, a_func); - if (a_node->nextSibling) ShinyNode_enumerateNodes(a_node->nextSibling, a_func); -} - -#endif /* SLIC3R_PROFILE */ diff --git a/src/Shiny/ShinyNode.h b/src/Shiny/ShinyNode.h deleted file mode 100644 index ee5fdb0de..000000000 --- a/src/Shiny/ShinyNode.h +++ /dev/null @@ -1,133 +0,0 @@ -/* -The MIT License - -Copyright (c) 2007-2010 Aidin Abedi http://code.google.com/p/shinyprofiler/ - -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. -*/ - -#ifndef SHINY_NODE_H -#define SHINY_NODE_H - -#include "ShinyData.h" -#include "ShinyTools.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/*---------------------------------------------------------------------------*/ - -typedef struct _ShinyNode { - - ShinyLastData _last; - - struct _ShinyZone* zone; - struct _ShinyNode* parent; - struct _ShinyNode* nextSibling; - - struct _ShinyNode* firstChild; - struct _ShinyNode* lastChild; - - uint32_t childCount; - uint32_t entryLevel; - - ShinyNodeCache* _cache; - - ShinyData data; - -} ShinyNode; - - -/*---------------------------------------------------------------------------*/ - -extern ShinyNode _ShinyNode_dummy; - - -/*---------------------------------------------------------------------------*/ - -SHINY_INLINE void ShinyNode_addChild(ShinyNode* self, ShinyNode* a_child) { - if (self->childCount++) { - self->lastChild->nextSibling = a_child; - self->lastChild = a_child; - - } else { - self->lastChild = a_child; - self->firstChild = a_child; - } -} - -SHINY_INLINE void ShinyNode_init(ShinyNode* self, ShinyNode* a_parent, struct _ShinyZone* a_zone, ShinyNodeCache* a_cache) { - /* NOTE: all member variables are assumed to be zero when allocated */ - - self->zone = a_zone; - self->parent = a_parent; - - self->entryLevel = a_parent->entryLevel + 1; - ShinyNode_addChild(a_parent, self); - - self->_cache = a_cache; -} - -SHINY_API void ShinyNode_updateTree(ShinyNode* self, float a_damping); -SHINY_API void ShinyNode_updateTreeClean(ShinyNode* self); - -SHINY_INLINE void ShinyNode_destroy(ShinyNode* self) { - *(self->_cache) = &_ShinyNode_dummy; -} - -SHINY_INLINE void ShinyNode_appendTicks(ShinyNode* self, shinytick_t a_elapsedTicks) { - self->_last.selfTicks += a_elapsedTicks; -} - -SHINY_INLINE void ShinyNode_beginEntry(ShinyNode* self) { - self->_last.entryCount++; -} - -SHINY_INLINE int ShinyNode_isRoot(ShinyNode* self) { - return (self->entryLevel == 0); -} - -SHINY_INLINE int ShinyNode_isDummy(ShinyNode* self) { - return (self == &_ShinyNode_dummy); -} - -SHINY_INLINE int ShinyNode_isEqual(ShinyNode* self, const ShinyNode* a_parent, const struct _ShinyZone* a_zone) { - return (self->parent == a_parent && self->zone == a_zone); -} - -SHINY_API const ShinyNode* ShinyNode_findNextInTree(const ShinyNode* self); - -SHINY_API void ShinyNode_clear(ShinyNode* self); - -SHINY_API void ShinyNode_enumerateNodes(const ShinyNode* a_node, void (*a_func)(const ShinyNode*)); - -#if __cplusplus -} /* end of extern "C" */ - -template -void ShinyNode_enumerateNodes(const ShinyNode* a_node, T* a_this, void (T::*a_func)(const ShinyNode*)) { - (a_this->*a_func)(a_node); - - if (a_node->firstChild) ShinyNode_enumerateNodes(a_node->firstChild, a_this, a_func); - if (a_node->nextSibling) ShinyNode_enumerateNodes(a_node->nextSibling, a_this, a_func); -} -#endif /* __cplusplus */ - -#endif /* SHINY_NODE_H */ diff --git a/src/Shiny/ShinyNodePool.c b/src/Shiny/ShinyNodePool.c deleted file mode 100644 index f00293252..000000000 --- a/src/Shiny/ShinyNodePool.c +++ /dev/null @@ -1,77 +0,0 @@ -/* -The MIT License - -Copyright (c) 2007-2010 Aidin Abedi http://code.google.com/p/shinyprofiler/ - -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. -*/ - -#ifdef SLIC3R_PROFILE - -#include "ShinyNodePool.h" -#include "ShinyTools.h" - -#include -#include - -/*---------------------------------------------------------------------------*/ - -ShinyNodePool* ShinyNodePool_create(uint32_t a_items) { - ShinyNodePool* pPool = (ShinyNodePool*) - malloc(sizeof(ShinyNodePool) + sizeof(ShinyNode) * (a_items - 1)); - - pPool->nextPool = NULL; - pPool->_nextItem = &pPool->_items[0]; - pPool->endOfItems = &pPool->_items[a_items]; - - memset(&pPool->_items[0], 0, a_items * sizeof(ShinyNode)); - return pPool; -} - - -/*---------------------------------------------------------------------------*/ - -uint32_t ShinyNodePool_memoryUsageChain(ShinyNodePool *first) { - uint32_t bytes = (uint32_t) ((char*) first->endOfItems - (char*) first); - ShinyNodePool *pool = first->nextPool; - - while (pool) { - bytes += (uint32_t) ((char*) pool->endOfItems - (char*) pool); - pool = pool->nextPool; - } - - return bytes; -} - - -/*---------------------------------------------------------------------------*/ - -void ShinyNodePool_destroy(ShinyNodePool *self) { - ShinyNode* firstNode = ShinyNodePool_firstItem(self); - ShinyNode* lastNode = self->_nextItem; - - while (firstNode != lastNode) - ShinyNode_destroy(firstNode++); - - /* TODO: make this into a loop or a tail recursion */ - if (self->nextPool) ShinyNodePool_destroy(self->nextPool); - free(self); -} - -#endif /* SLIC3R_PROFILE */ diff --git a/src/Shiny/ShinyNodePool.h b/src/Shiny/ShinyNodePool.h deleted file mode 100644 index 5e30a3306..000000000 --- a/src/Shiny/ShinyNodePool.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -The MIT License - -Copyright (c) 2007-2010 Aidin Abedi http://code.google.com/p/shinyprofiler/ - -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. -*/ - -#ifndef SHINY_NODE_POOL_H -#define SHINY_NODE_POOL_H - -#include "ShinyNode.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/*---------------------------------------------------------------------------*/ - -typedef struct _ShinyNodePool { - - struct _ShinyNodePool* nextPool; - - ShinyNode *_nextItem; - ShinyNode *endOfItems; - - ShinyNode _items[1]; - -} ShinyNodePool; - - -/*---------------------------------------------------------------------------*/ - -SHINY_INLINE ShinyNode* ShinyNodePool_firstItem(ShinyNodePool *self) { - return &(self->_items[0]); -} - -SHINY_INLINE ShinyNode* ShinyNodePool_newItem(ShinyNodePool *self) { - return self->_nextItem++; -} - -ShinyNodePool* ShinyNodePool_create(uint32_t a_items); -void ShinyNodePool_destroy(ShinyNodePool *self); - -uint32_t ShinyNodePool_memoryUsageChain(ShinyNodePool *first); - -#if __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* SHINY_NODE_POOL_H */ diff --git a/src/Shiny/ShinyNodeState.c b/src/Shiny/ShinyNodeState.c deleted file mode 100644 index fbf6dc870..000000000 --- a/src/Shiny/ShinyNodeState.c +++ /dev/null @@ -1,108 +0,0 @@ -/* -The MIT License - -Copyright (c) 2007-2010 Aidin Abedi http://code.google.com/p/shinyprofiler/ - -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. -*/ - -#ifdef SLIC3R_PROFILE - -#include "ShinyNodeState.h" -#include "ShinyNode.h" -#include "ShinyZone.h" - -#include - -/*---------------------------------------------------------------------------*/ - -ShinyNodeState* ShinyNodeState_push(ShinyNodeState *a_top, ShinyNode *a_node) { - ShinyZone *zone = a_node->zone; - ShinyNodeState *self = (ShinyNodeState*) malloc(sizeof(ShinyNodeState)); - self->node = a_node; - self->_prev = a_top; - - a_node->data.selfTicks.cur = a_node->_last.selfTicks; - a_node->data.entryCount.cur = a_node->_last.entryCount; - - zone->data.selfTicks.cur += a_node->_last.selfTicks; - zone->data.entryCount.cur += a_node->_last.entryCount; - - a_node->data.childTicks.cur = 0; - a_node->_last.selfTicks = 0; - a_node->_last.entryCount = 0; - - self->zoneUpdating = zone->_state != SHINY_ZONE_STATE_UPDATING; - if (self->zoneUpdating) { - zone->_state = SHINY_ZONE_STATE_UPDATING; - } else { - zone->data.childTicks.cur -= a_node->data.selfTicks.cur; - } - - return self; -} - -/*---------------------------------------------------------------------------*/ - -ShinyNodeState* ShinyNodeState_pop(ShinyNodeState *a_top) { - ShinyNodeState *prev = a_top->_prev; - free(a_top); - return prev; -} - -/*---------------------------------------------------------------------------*/ - -ShinyNode* ShinyNodeState_finishAndGetNext(ShinyNodeState *self, float a_damping) { - ShinyNode *node = self->node; - ShinyZone *zone = node->zone; - - if (self->zoneUpdating) { - zone->data.childTicks.cur += node->data.childTicks.cur; - zone->_state = SHINY_ZONE_STATE_INITIALIZED; - } - - ShinyData_computeAverage(&node->data, a_damping); - - if (!ShinyNode_isRoot(node)) - node->parent->data.childTicks.cur += node->data.selfTicks.cur + node->data.childTicks.cur; - - return node->nextSibling; -} - - -/*---------------------------------------------------------------------------*/ - -ShinyNode* ShinyNodeState_finishAndGetNextClean(ShinyNodeState *self) { - ShinyNode *node = self->node; - ShinyZone *zone = node->zone; - - if (self->zoneUpdating) { - zone->data.childTicks.cur += node->data.childTicks.cur; - zone->_state = SHINY_ZONE_STATE_INITIALIZED; - } - - ShinyData_copyAverage(&node->data); - - if (!ShinyNode_isRoot(node)) - node->parent->data.childTicks.cur += node->data.selfTicks.cur + node->data.childTicks.cur; - - return node->nextSibling; -} - -#endif /* SLIC3R_PROFILE */ diff --git a/src/Shiny/ShinyNodeState.h b/src/Shiny/ShinyNodeState.h deleted file mode 100644 index 62fdd4ba8..000000000 --- a/src/Shiny/ShinyNodeState.h +++ /dev/null @@ -1,56 +0,0 @@ -/* -The MIT License - -Copyright (c) 2007-2010 Aidin Abedi http://code.google.com/p/shinyprofiler/ - -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. -*/ - -#ifndef SHINY_NODE_STATE_H -#define SHINY_NODE_STATE_H - -#include "ShinyNode.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/*---------------------------------------------------------------------------*/ - -typedef struct _ShinyNodeState { - ShinyNode *node; - int zoneUpdating; - - struct _ShinyNodeState *_prev; -} ShinyNodeState; - - -/*---------------------------------------------------------------------------*/ - -ShinyNodeState* ShinyNodeState_push(ShinyNodeState *a_top, ShinyNode *a_node); -ShinyNodeState* ShinyNodeState_pop(ShinyNodeState *a_top); - -ShinyNode* ShinyNodeState_finishAndGetNext(ShinyNodeState *self, float a_damping); -ShinyNode* ShinyNodeState_finishAndGetNextClean(ShinyNodeState *self); - -#if __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* SHINY_NODE_STATE_H */ diff --git a/src/Shiny/ShinyOutput.c b/src/Shiny/ShinyOutput.c deleted file mode 100644 index c2c624d58..000000000 --- a/src/Shiny/ShinyOutput.c +++ /dev/null @@ -1,189 +0,0 @@ -/* -The MIT License - -Copyright (c) 2007-2010 Aidin Abedi http://code.google.com/p/shinyprofiler/ - -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. -*/ - -#ifdef SLIC3R_PROFILE - -#include "ShinyOutput.h" - -#include - -#if SHINY_COMPILER == SHINY_COMPILER_MSVC -# pragma warning(disable: 4996) -# define snprintf _snprintf -# define TRAILING 0 - -#else -# define TRAILING 1 -#endif - -/*---------------------------------------------------------------------------*/ - -#define OUTPUT_WIDTH_CALL 6 -#define OUTPUT_WIDTH_TIME (6+3) -#define OUTPUT_WIDTH_PERC (4+3) -#define OUTPUT_WIDTH_SUM 120 - -#define OUTPUT_WIDTH_DATA (1+OUTPUT_WIDTH_CALL + 1 + 2*(OUTPUT_WIDTH_TIME+4+OUTPUT_WIDTH_PERC+1) + 1) -#define OUTPUT_WIDTH_NAME (OUTPUT_WIDTH_SUM - OUTPUT_WIDTH_DATA) - - -/*---------------------------------------------------------------------------*/ - -SHINY_INLINE char* printHeader(char *output, const char *a_title) { - snprintf(output, OUTPUT_WIDTH_SUM + TRAILING, - "%-*s %*s %*s %*s", - OUTPUT_WIDTH_NAME, a_title, - OUTPUT_WIDTH_CALL, "calls", - OUTPUT_WIDTH_TIME+4+OUTPUT_WIDTH_PERC+1, "self time", - OUTPUT_WIDTH_TIME+4+OUTPUT_WIDTH_PERC+1, "total time"); - - return output + OUTPUT_WIDTH_SUM; -} - - -/*---------------------------------------------------------------------------*/ - -SHINY_INLINE char* printData(char *output, const ShinyData *a_data, float a_topercent) { - float totalTicksAvg = ShinyData_totalTicksAvg(a_data); - const ShinyTimeUnit *selfUnit = ShinyGetTimeUnit(a_data->selfTicks.avg); - const ShinyTimeUnit *totalUnit = ShinyGetTimeUnit(totalTicksAvg); - - snprintf(output, OUTPUT_WIDTH_DATA + TRAILING, - " %*.1f %*.2f %-2s %*.2f%% %*.2f %-2s %*.2f%%", - OUTPUT_WIDTH_CALL, a_data->entryCount.avg, - OUTPUT_WIDTH_TIME, a_data->selfTicks.avg * selfUnit->invTickFreq, selfUnit->suffix, - OUTPUT_WIDTH_PERC, a_data->selfTicks.avg * a_topercent, - OUTPUT_WIDTH_TIME, totalTicksAvg * totalUnit->invTickFreq, totalUnit->suffix, - OUTPUT_WIDTH_PERC, totalTicksAvg * a_topercent); - - return output + OUTPUT_WIDTH_DATA; -} - - -/*---------------------------------------------------------------------------*/ - -SHINY_INLINE char* printNode(char* output, const ShinyNode *a_node, float a_topercent) { - int offset = a_node->entryLevel * 2; - - snprintf(output, OUTPUT_WIDTH_NAME + TRAILING, "%*s%-*s", - offset, "", OUTPUT_WIDTH_NAME - offset, a_node->zone->name); - - output += OUTPUT_WIDTH_NAME; - - output = printData(output, &a_node->data, a_topercent); - return output; -} - - -/*---------------------------------------------------------------------------*/ - -SHINY_INLINE char* printZone(char* output, const ShinyZone *a_zone, float a_topercent) { - snprintf(output, OUTPUT_WIDTH_NAME + TRAILING, "%-*s", - OUTPUT_WIDTH_NAME, a_zone->name); - - output += OUTPUT_WIDTH_NAME; - - output = printData(output, &a_zone->data, a_topercent); - return output; -} - - -/*---------------------------------------------------------------------------*/ - -int ShinyPrintNodesSize(uint32_t a_count) { - return (1 + a_count) * (OUTPUT_WIDTH_SUM + 1); -} - - -/*---------------------------------------------------------------------------*/ - -int ShinyPrintZonesSize(uint32_t a_count) { - return (1 + a_count) * (OUTPUT_WIDTH_SUM + 1); -} - - -/*---------------------------------------------------------------------------*/ - -void ShinyPrintANode(char* output, const ShinyNode *a_node, const ShinyNode *a_root) { - float fTicksToPc = 100.0f / a_root->data.childTicks.avg; - output = printNode(output, a_node, fTicksToPc); - (*output++) = '\0'; -} - - -/*---------------------------------------------------------------------------*/ - -void ShinyPrintAZone(char* output, const ShinyZone *a_zone, const ShinyZone *a_root) { - float fTicksToPc = 100.0f / a_root->data.childTicks.avg; - output = printZone(output, a_zone, fTicksToPc); - (*output++) = '\0'; -} - - -/*---------------------------------------------------------------------------*/ - -void ShinyPrintNodes(char* output, const ShinyNode *a_root) { - float fTicksToPc = 100.0f / a_root->data.childTicks.avg; - const ShinyNode *node = a_root; - - output = printHeader(output, "call tree"); - (*output++) = '\n'; - - for (;;) { - output = printNode(output, node, fTicksToPc); - - node = ShinyNode_findNextInTree(node); - if (node) { - (*output++) = '\n'; - } else { - (*output++) = '\0'; - return; - } - } -} - - -/*---------------------------------------------------------------------------*/ - -void ShinyPrintZones(char* output, const ShinyZone *a_root) { - float fTicksToPc = 100.0f / a_root->data.childTicks.avg; - const ShinyZone *zone = a_root; - - output = printHeader(output, "sorted list"); - (*output++) = '\n'; - - for (;;) { - output = printZone(output, zone, fTicksToPc); - - zone = zone->next; - if (zone) { - (*output++) = '\n'; - } else { - (*output++) = '\0'; - return; - } - } -} - -#endif /* SLIC3R_PROFILE */ diff --git a/src/Shiny/ShinyOutput.h b/src/Shiny/ShinyOutput.h deleted file mode 100644 index 81c1783db..000000000 --- a/src/Shiny/ShinyOutput.h +++ /dev/null @@ -1,68 +0,0 @@ -/* -The MIT License - -Copyright (c) 2007-2010 Aidin Abedi http://code.google.com/p/shinyprofiler/ - -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. -*/ - -#ifndef SHINY_OUTPUT_H -#define SHINY_OUTPUT_H - -#include "ShinyNode.h" -#include "ShinyZone.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/*---------------------------------------------------------------------------*/ - -SHINY_API int ShinyPrintNodesSize(uint32_t a_count); -SHINY_API int ShinyPrintZonesSize(uint32_t a_count); - -SHINY_API void ShinyPrintANode(char* output, const ShinyNode *a_node, const ShinyNode *a_root); -SHINY_API void ShinyPrintAZone(char* output, const ShinyZone *a_zone, const ShinyZone *a_root); - -SHINY_API void ShinyPrintNodes(char* output, const ShinyNode *a_root); -SHINY_API void ShinyPrintZones(char* output, const ShinyZone *a_root); - - -/*---------------------------------------------------------------------------*/ - -#if __cplusplus -} /* end of extern "C" */ -#include - -SHINY_INLINE std::string ShinyNodesToString(const ShinyNode *a_root, uint32_t a_count) { - std::string str; - str.resize(ShinyPrintNodesSize(a_count) - 1); - ShinyPrintNodes(&str[0], a_root); - return str; -} - -SHINY_INLINE std::string ShinyZonesToString(const ShinyZone *a_root, uint32_t a_count) { - std::string str; - str.resize(ShinyPrintZonesSize(a_count) - 1); - ShinyPrintZones(&str[0], a_root); - return str; -} -#endif /* __cplusplus */ - -#endif /* SHINY_OUTPUT_H */ diff --git a/src/Shiny/ShinyPrereqs.h b/src/Shiny/ShinyPrereqs.h deleted file mode 100644 index 5a3044dbc..000000000 --- a/src/Shiny/ShinyPrereqs.h +++ /dev/null @@ -1,138 +0,0 @@ -/* -The MIT License - -Copyright (c) 2007-2010 Aidin Abedi http://code.google.com/p/shinyprofiler/ - -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. -*/ - -#ifndef SHINY_PREREQS_H -#define SHINY_PREREQS_H - - -#include - -/*---------------------------------------------------------------------------*/ - -#ifndef FALSE -#define FALSE 0x0 -#endif - -#ifndef TRUE -#define TRUE 0x1 -#endif - -#ifndef NULL -#define NULL 0 -#endif - -#include "ShinyConfig.h" -#include "ShinyVersion.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/*---------------------------------------------------------------------------*/ - -#define SHINY_PLATFORM_WIN32 0x1 -#define SHINY_PLATFORM_POSIX 0x2 - -#if defined (_WIN32) -# define SHINY_PLATFORM SHINY_PLATFORM_WIN32 - -#else /* ASSUME: POSIX-compliant OS */ -# define SHINY_PLATFORM SHINY_PLATFORM_POSIX -#endif - - -/*---------------------------------------------------------------------------*/ - -#define SHINY_COMPILER_MSVC 0x1 -#define SHINY_COMPILER_GNUC 0x2 -#define SHINY_COMPILER_OTHER 0x3 - -#if defined (_MSC_VER) -# define SHINY_COMPILER SHINY_COMPILER_MSVC - -#elif defined (__GNUG__) -# define SHINY_COMPILER SHINY_COMPILER_GNUC - -#else -# define SHINY_COMPILER SHINY_COMPILER_OTHER -#endif - - -/*---------------------------------------------------------------------------*/ - -#if SHINY_COMPILER == SHINY_COMPILER_GNUC -#include -#include -#endif - - -/*---------------------------------------------------------------------------*/ - -struct _ShinyNode; -struct _ShinyZone; - -typedef struct _ShinyNode* ShinyNodeCache; -typedef struct _ShinyNode* ShinyNodeTable; - -/*---------------------------------------------------------------------------*/ - -#define SHINY_API - -/*---------------------------------------------------------------------------*/ - -#if SHINY_COMPILER == SHINY_COMPILER_MSVC -# define SHINY_INLINE __inline -# define SHINY_UNUSED -#elif SHINY_COMPILER == SHINY_COMPILER_GNUC -# define SHINY_INLINE inline -# define SHINY_UNUSED __attribute__((unused)) -#elif SHINY_COMPILER == SHINY_COMPILER_OTHER -# define SHINY_INLINE inline -# define SHINY_UNUSED -#endif - - -/*---------------------------------------------------------------------------*/ - -#if SHINY_COMPILER == SHINY_COMPILER_MSVC - typedef int int32_t; - typedef unsigned int uint32_t; - - typedef __int64 int64_t; - typedef unsigned __int64 uint64_t; - -/* -#elif defined(__CYGWIN__) - typedef u_int32_t uint32_t; - typedef u_int64_t uint64_t; -*/ -#endif - - typedef uint64_t shinytick_t; - -#if __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* SHINY_PREREQS_H */ diff --git a/src/Shiny/ShinyTools.c b/src/Shiny/ShinyTools.c deleted file mode 100644 index 4058e2285..000000000 --- a/src/Shiny/ShinyTools.c +++ /dev/null @@ -1,116 +0,0 @@ -/* -The MIT License - -Copyright (c) 2007-2010 Aidin Abedi http://code.google.com/p/shinyprofiler/ - -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. -*/ - -#ifdef SLIC3R_PROFILE - -#include "ShinyTools.h" - -#if SHINY_PLATFORM == SHINY_PLATFORM_WIN32 -#define WIN32_LEAN_AND_MEAN -#ifndef NOMINMAX - #define NOMINMAX -#endif /* NOMINMAX */ -#include - -#elif SHINY_PLATFORM == SHINY_PLATFORM_POSIX -#include -#endif - - -/*---------------------------------------------------------------------------*/ - -const ShinyTimeUnit* ShinyGetTimeUnit(float ticks) { - static ShinyTimeUnit units[4] = { 0 }; - - if (units[0].tickFreq == 0) { /* auto initialize first time */ - units[0].tickFreq = ShinyGetTickFreq() / 1.0f; - units[0].invTickFreq = ShinyGetTickInvFreq() * 1.0f; - units[0].suffix = "s"; - - units[1].tickFreq = ShinyGetTickFreq() / 1000.0f; - units[1].invTickFreq = ShinyGetTickInvFreq() * 1000.0f; - units[1].suffix = "ms"; - - units[2].tickFreq = ShinyGetTickFreq() / 1000000.0f; - units[2].invTickFreq = ShinyGetTickInvFreq() * 1000000.0f; - units[2].suffix = "us"; - - units[3].tickFreq = ShinyGetTickFreq() / 1000000000.0f; - units[3].invTickFreq = ShinyGetTickInvFreq() * 1000000000.0f; - units[3].suffix = "ns"; - } - - if (units[0].tickFreq < ticks) return &units[0]; - else if (units[1].tickFreq < ticks) return &units[1]; - else if (units[2].tickFreq < ticks) return &units[2]; - else return &units[3]; -} - - -/*---------------------------------------------------------------------------*/ - -#if SHINY_PLATFORM == SHINY_PLATFORM_WIN32 - -void ShinyGetTicks(shinytick_t *p) { - QueryPerformanceCounter((LARGE_INTEGER*)(p)); -} - -shinytick_t ShinyGetTickFreq(void) { - static shinytick_t freq = 0; - if (freq == 0) QueryPerformanceFrequency((LARGE_INTEGER*)(&freq)); - return freq; -} - -float ShinyGetTickInvFreq(void) { - static float invfreq = 0; - if (invfreq == 0) invfreq = 1.0f / ShinyGetTickFreq(); - return invfreq; -} - - -/*---------------------------------------------------------------------------*/ - -#elif SHINY_PLATFORM == SHINY_PLATFORM_POSIX - -//#include -//#include - -void ShinyGetTicks(shinytick_t *p) { - struct timeval time; - gettimeofday(&time, NULL); - - *p = time.tv_sec * 1000000 + time.tv_usec; -} - -shinytick_t ShinyGetTickFreq(void) { - return 1000000; -} - -float ShinyGetTickInvFreq(void) { - return 1.0f / 1000000.0f; -} - -#endif - -#endif /* SLIC3R_PROFILE */ diff --git a/src/Shiny/ShinyTools.h b/src/Shiny/ShinyTools.h deleted file mode 100644 index 379703ee6..000000000 --- a/src/Shiny/ShinyTools.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -The MIT License - -Copyright (c) 2007-2010 Aidin Abedi http://code.google.com/p/shinyprofiler/ - -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. -*/ - -#ifndef SHINY_TOOLS_H -#define SHINY_TOOLS_H - -#include "ShinyPrereqs.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/*---------------------------------------------------------------------------*/ - -typedef struct { - float tickFreq; - float invTickFreq; - const char* suffix; -} ShinyTimeUnit; - - -/*---------------------------------------------------------------------------*/ - -SHINY_API const ShinyTimeUnit* ShinyGetTimeUnit(float ticks); - -SHINY_API void ShinyGetTicks(shinytick_t *p); - -SHINY_API shinytick_t ShinyGetTickFreq(void); - -SHINY_API float ShinyGetTickInvFreq(void); - -#if __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* SHINY_TOOLS_H */ diff --git a/src/Shiny/ShinyVersion.h b/src/Shiny/ShinyVersion.h deleted file mode 100644 index fe6cd4a33..000000000 --- a/src/Shiny/ShinyVersion.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -The MIT License - -Copyright (c) 2007-2010 Aidin Abedi http://code.google.com/p/shinyprofiler/ - -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. -*/ - -#ifndef SHINY_VERSION_H -#define SHINY_VERSION_H - - -/*---------------------------------------------------------------------------*/ - -#define SHINY_VERSION "2.6 RC1" -#define SHINY_SHORTNAME "Shiny" -#define SHINY_FULLNAME "Shiny Profiler" -#define SHINY_COPYRIGHT "Copyright (C) 2007-2010 Aidin Abedi" -#define SHINY_DESCRIPTION "Shiny is a state of the art profiler designed to help finding bottlenecks in your project." - -#endif /* SHINY_VERSION_H */ diff --git a/src/Shiny/ShinyZone.c b/src/Shiny/ShinyZone.c deleted file mode 100644 index 99d90d927..000000000 --- a/src/Shiny/ShinyZone.c +++ /dev/null @@ -1,201 +0,0 @@ -/* -The MIT License - -Copyright (c) 2007-2010 Aidin Abedi http://code.google.com/p/shinyprofiler/ - -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. -*/ - -#ifdef SLIC3R_PROFILE - -#include "ShinyZone.h" - -#include - -/*---------------------------------------------------------------------------*/ - -void ShinyZone_preUpdateChain(ShinyZone *first) { - ShinyZone* zone = first; - - while (zone) { - ShinyData_clearCurrent(&(zone->data)); - zone = zone->next; - } -} - - -/*---------------------------------------------------------------------------*/ - -void ShinyZone_updateChain(ShinyZone *first, float a_damping) { - ShinyZone* zone = first; - - do { - ShinyData_computeAverage(&(zone->data), a_damping); - zone = zone->next; - } while (zone); -} - - -/*---------------------------------------------------------------------------*/ - -void ShinyZone_updateChainClean(ShinyZone *first) { - ShinyZone* zone = first; - - do { - ShinyData_copyAverage(&(zone->data)); - zone = zone->next; - } while (zone); -} - - -/*---------------------------------------------------------------------------*/ - -void ShinyZone_resetChain(ShinyZone *first) { - ShinyZone* zone = first, *temp; - - do { - zone->_state = SHINY_ZONE_STATE_HIDDEN; - temp = zone->next; - zone->next = NULL; - zone = temp; - } while (zone); -} - -/*---------------------------------------------------------------------------*/ - -/* A Linked-List Memory Sort - by Philip J. Erdelsky - pje@efgh.com - http://www.alumni.caltech.edu/~pje/ - - Modified by Aidin Abedi -*/ - -ShinyZone* ShinyZone_sortChain(ShinyZone **first) /* return ptr to last zone */ -{ - ShinyZone *p = *first; - - unsigned base; - unsigned long block_size; - - struct tape - { - ShinyZone *first, *last; - unsigned long count; - } tape[4]; - - /* Distribute the records alternately to tape[0] and tape[1]. */ - - tape[0].count = tape[1].count = 0L; - tape[0].first = NULL; - base = 0; - while (p != NULL) - { - ShinyZone *next = p->next; - p->next = tape[base].first; - tape[base].first = p; - tape[base].count++; - p = next; - base ^= 1; - } - - /* If the list is empty or contains only a single record, then */ - /* tape[1].count == 0L and this part is vacuous. */ - - for (base = 0, block_size = 1L; tape[base+1].count != 0L; - base ^= 2, block_size <<= 1) - { - int dest; - struct tape *tape0, *tape1; - tape0 = tape + base; - tape1 = tape + base + 1; - dest = base ^ 2; - tape[dest].count = tape[dest+1].count = 0; - for (; tape0->count != 0; dest ^= 1) - { - unsigned long n0, n1; - struct tape *output_tape = tape + dest; - n0 = n1 = block_size; - while (1) - { - ShinyZone *chosen_record; - struct tape *chosen_tape; - if (n0 == 0 || tape0->count == 0) - { - if (n1 == 0 || tape1->count == 0) - break; - chosen_tape = tape1; - n1--; - } - else if (n1 == 0 || tape1->count == 0) - { - chosen_tape = tape0; - n0--; - } - else if (ShinyZone_compare(tape1->first, tape0->first) > 0) - { - chosen_tape = tape1; - n1--; - } - else - { - chosen_tape = tape0; - n0--; - } - chosen_tape->count--; - chosen_record = chosen_tape->first; - chosen_tape->first = chosen_record->next; - if (output_tape->count == 0) - output_tape->first = chosen_record; - else - output_tape->last->next = chosen_record; - output_tape->last = chosen_record; - output_tape->count++; - } - } - } - - if (tape[base].count > 1L) { - ShinyZone* last = tape[base].last; - *first = tape[base].first; - last->next = NULL; - return last; - - } else { - return NULL; - } -} - - -/*---------------------------------------------------------------------------*/ - -void ShinyZone_clear(ShinyZone* self) { - memset(self, 0, sizeof(ShinyZone)); -} - - -/*---------------------------------------------------------------------------*/ - -void ShinyZone_enumerateZones(const ShinyZone* a_zone, void (*a_func)(const ShinyZone*)) { - a_func(a_zone); - - if (a_zone->next) ShinyZone_enumerateZones(a_zone->next, a_func); -} - -#endif /* SLIC3R_PROFILE */ diff --git a/src/Shiny/ShinyZone.h b/src/Shiny/ShinyZone.h deleted file mode 100644 index dde0d3624..000000000 --- a/src/Shiny/ShinyZone.h +++ /dev/null @@ -1,91 +0,0 @@ -/* -The MIT License - -Copyright (c) 2007-2010 Aidin Abedi http://code.google.com/p/shinyprofiler/ - -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. -*/ - -#ifndef SHINY_ZONE_H -#define SHINY_ZONE_H - -#include "ShinyData.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/*---------------------------------------------------------------------------*/ - -#define SHINY_ZONE_STATE_HIDDEN 0 -#define SHINY_ZONE_STATE_INITIALIZED 1 -#define SHINY_ZONE_STATE_UPDATING 2 - - -/*---------------------------------------------------------------------------*/ - -typedef struct _ShinyZone { - struct _ShinyZone* next; - int _state; - const char* name; - ShinyData data; -} ShinyZone; - - -/*---------------------------------------------------------------------------*/ - -SHINY_INLINE void ShinyZone_init(ShinyZone *self, ShinyZone* a_prev) { - self->_state = SHINY_ZONE_STATE_INITIALIZED; - a_prev->next = self; -} - -SHINY_INLINE void ShinyZone_uninit(ShinyZone *self) { - self->_state = SHINY_ZONE_STATE_HIDDEN; - self->next = NULL; -} - -SHINY_API void ShinyZone_preUpdateChain(ShinyZone *first); -SHINY_API void ShinyZone_updateChain(ShinyZone *first, float a_damping); -SHINY_API void ShinyZone_updateChainClean(ShinyZone *first); - -SHINY_API void ShinyZone_resetChain(ShinyZone *first); - -SHINY_API ShinyZone* ShinyZone_sortChain(ShinyZone **first); - -SHINY_INLINE float ShinyZone_compare(ShinyZone *a, ShinyZone *b) { - return a->data.selfTicks.avg - b->data.selfTicks.avg; -} - -SHINY_API void ShinyZone_clear(ShinyZone* self); - -SHINY_API void ShinyZone_enumerateZones(const ShinyZone* a_zone, void (*a_func)(const ShinyZone*)); - -#if __cplusplus -} /* end of extern "C" */ - -template -void ShinyZone_enumerateZones(const ShinyZone* a_zone, T* a_this, void (T::*a_func)(const ShinyZone*)) { - (a_this->*a_func)(a_zone); - - if (a_zone->next) ShinyZone_enumerateZones(a_zone->next, a_this, a_func); -} -#endif /* __cplusplus */ - -#endif /* SHINY_ZONE_H */ diff --git a/src/clipper/CMakeLists.txt b/src/clipper/CMakeLists.txt index f62508820..3cb7cb6bb 100644 --- a/src/clipper/CMakeLists.txt +++ b/src/clipper/CMakeLists.txt @@ -8,7 +8,3 @@ add_library(clipper STATIC clipper_z.cpp clipper_z.hpp ) - -if(SLIC3R_PROFILE) - target_link_libraries(clipper Shiny) -endif() diff --git a/src/clipper/clipper.cpp b/src/clipper/clipper.cpp index 84109398a..2ca643882 100644 --- a/src/clipper/clipper.cpp +++ b/src/clipper/clipper.cpp @@ -50,17 +50,6 @@ #include #include -// Profiling support using the Shiny intrusive profiler -//#define CLIPPERLIB_PROFILE -#if defined(SLIC3R_PROFILE) && defined(CLIPPERLIB_PROFILE) - #include - #define CLIPPERLIB_PROFILE_FUNC() PROFILE_FUNC() - #define CLIPPERLIB_PROFILE_BLOCK(name) PROFILE_BLOCK(name) -#else - #define CLIPPERLIB_PROFILE_FUNC() - #define CLIPPERLIB_PROFILE_BLOCK(name) -#endif - #ifdef CLIPPERLIB_NAMESPACE_PREFIX namespace CLIPPERLIB_NAMESPACE_PREFIX { #endif // CLIPPERLIB_NAMESPACE_PREFIX @@ -299,7 +288,6 @@ int PointInPolygon (const IntPoint &pt, OutPt *op) // This is potentially very expensive! O(n^2)! bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2) { - CLIPPERLIB_PROFILE_FUNC(); OutPt* op = OutPt1; do { @@ -602,9 +590,18 @@ bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b) // ClipperBase class methods ... //------------------------------------------------------------------------------ -#ifndef CLIPPERLIB_INT32 +#ifdef CLIPPERLIB_INT32 +static inline void RangeTest(const IntPoint &pt) +{ +#ifndef NDEBUG + static constexpr const int32_t hi = 65536 * 16383; + if (pt.x() > hi || pt.y() > hi || -pt.x() > hi || -pt.y() > hi) + throw clipperException("Coordinate outside allowed range"); +#endif // NDEBUG +} +#else // CLIPPERLIB_INT32 // Called from ClipperBase::AddPath() to verify the scale of the input polygon coordinates. -inline void RangeTest(const IntPoint& Pt, bool& useFullRange) +static inline void RangeTest(const IntPoint& Pt, bool& useFullRange) { if (useFullRange) { @@ -759,7 +756,6 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) { - CLIPPERLIB_PROFILE_FUNC(); // Remove duplicate end point from a closed input path. // Remove duplicate points from the end of the input path. int highI = (int)pg.size() -1; @@ -783,7 +779,6 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, bool Closed, TEdge* edges) { - CLIPPERLIB_PROFILE_FUNC(); #ifdef use_lines if (!Closed && PolyTyp == ptClip) throw clipperException("AddPath: Open paths must be subject."); @@ -798,7 +793,10 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b try { edges[1].Curr = pg[1]; -#ifndef CLIPPERLIB_INT32 +#ifdef CLIPPERLIB_INT32 + RangeTest(pg[0]); + RangeTest(pg[highI]); +#else RangeTest(pg[0], m_UseFullRange); RangeTest(pg[highI], m_UseFullRange); #endif // CLIPPERLIB_INT32 @@ -806,7 +804,9 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]); for (int i = highI - 1; i >= 1; --i) { -#ifndef CLIPPERLIB_INT32 +#ifdef CLIPPERLIB_INT32 + RangeTest(pg[i]); +#else RangeTest(pg[i], m_UseFullRange); #endif // CLIPPERLIB_INT32 InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]); @@ -961,7 +961,6 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b void ClipperBase::Clear() { - CLIPPERLIB_PROFILE_FUNC(); m_MinimaList.clear(); m_edges.clear(); #ifndef CLIPPERLIB_INT32 @@ -975,7 +974,6 @@ void ClipperBase::Clear() // Sort the LML entries, initialize the left / right bound edges of each Local Minima. void ClipperBase::Reset() { - CLIPPERLIB_PROFILE_FUNC(); if (m_MinimaList.empty()) return; //ie nothing to process std::sort(m_MinimaList.begin(), m_MinimaList.end(), [](const LocalMinimum& lm1, const LocalMinimum& lm2){ return lm1.Y < lm2.Y; }); @@ -1004,7 +1002,6 @@ void ClipperBase::Reset() // Returns (0,0,0,0) for an empty rectangle. IntRect ClipperBase::GetBounds() { - CLIPPERLIB_PROFILE_FUNC(); IntRect result; auto lm = m_MinimaList.begin(); if (lm == m_MinimaList.end()) @@ -1065,7 +1062,6 @@ Clipper::Clipper(int initOptions) : void Clipper::Reset() { - CLIPPERLIB_PROFILE_FUNC(); ClipperBase::Reset(); m_Scanbeam = std::priority_queue(); m_Maxima.clear(); @@ -1080,7 +1076,6 @@ void Clipper::Reset() bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType subjFillType, PolyFillType clipFillType) { - CLIPPERLIB_PROFILE_FUNC(); if (m_HasOpenPaths) throw clipperException("Error: PolyTree struct is needed for open path clipping."); solution.clear(); @@ -1098,7 +1093,6 @@ bool Clipper::Execute(ClipType clipType, Paths &solution, bool Clipper::Execute(ClipType clipType, PolyTree& polytree, PolyFillType subjFillType, PolyFillType clipFillType) { - CLIPPERLIB_PROFILE_FUNC(); m_SubjFillType = subjFillType; m_ClipFillType = clipFillType; m_ClipType = clipType; @@ -1112,10 +1106,8 @@ bool Clipper::Execute(ClipType clipType, PolyTree& polytree, bool Clipper::ExecuteInternal() { - CLIPPERLIB_PROFILE_FUNC(); bool succeeded = true; try { - CLIPPERLIB_PROFILE_BLOCK(Clipper_ExecuteInternal_Process); Reset(); if (m_MinimaList.empty()) return true; cInt botY = m_Scanbeam.top(); @@ -1140,13 +1132,11 @@ bool Clipper::ExecuteInternal() if (succeeded) { - CLIPPERLIB_PROFILE_BLOCK(Clipper_ExecuteInternal_Fix); //fix orientations ... //FIXME Vojtech: Does it not invalidate the loop hierarchy maintained as OutRec::FirstLeft pointers? //FIXME Vojtech: The area is calculated with floats, it may not be numerically stable! { - CLIPPERLIB_PROFILE_BLOCK(Clipper_ExecuteInternal_Fix_orientations); for (OutRec *outRec : m_PolyOuts) if (outRec->Pts && !outRec->IsOpen && (outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) ReversePolyPtLinks(outRec->Pts); @@ -1156,7 +1146,6 @@ bool Clipper::ExecuteInternal() //unfortunately FixupOutPolygon() must be done after JoinCommonEdges() { - CLIPPERLIB_PROFILE_BLOCK(Clipper_ExecuteInternal_Fix_fixup); for (OutRec *outRec : m_PolyOuts) if (outRec->Pts) { if (outRec->IsOpen) @@ -1410,7 +1399,6 @@ bool Clipper::IsContributing(const TEdge& edge) const // Called from Clipper::InsertLocalMinimaIntoAEL() and Clipper::IntersectEdges(). OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) { - CLIPPERLIB_PROFILE_FUNC(); OutPt* result; TEdge *e, *prevE; if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx )) @@ -1502,7 +1490,6 @@ void Clipper::CopyAELToSEL() // Called from Clipper::ExecuteInternal() void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) { - CLIPPERLIB_PROFILE_FUNC(); while (!m_MinimaList.empty() && m_MinimaList.back().Y == botY) { TEdge* lb = m_MinimaList.back().LeftBound; @@ -2052,7 +2039,6 @@ OutPt* Clipper::GetLastOutPt(TEdge *e) void Clipper::ProcessHorizontals() { - CLIPPERLIB_PROFILE_FUNC(); TEdge* horzEdge = m_SortedEdges; while(horzEdge) { @@ -2429,7 +2415,6 @@ void Clipper::UpdateEdgeIntoAEL(TEdge *&e) bool Clipper::ProcessIntersections(const cInt topY) { - CLIPPERLIB_PROFILE_FUNC(); if( !m_ActiveEdges ) return true; try { BuildIntersectList(topY); @@ -2584,7 +2569,6 @@ void Clipper::DoMaxima(TEdge *e) void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) { - CLIPPERLIB_PROFILE_FUNC(); TEdge* e = m_ActiveEdges; while( e ) { @@ -3195,7 +3179,6 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) // This is potentially very expensive! O(n^3)! void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const { - CLIPPERLIB_PROFILE_FUNC(); //tests if NewOutRec contains the polygon before reassigning FirstLeft for (OutRec *outRec : m_PolyOuts) { @@ -3219,7 +3202,6 @@ void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const void Clipper::JoinCommonEdges() { - CLIPPERLIB_PROFILE_FUNC(); for (Join &join : m_Joins) { OutRec *outRec1 = GetOutRec(join.OutPt1->Idx); @@ -3770,7 +3752,6 @@ void ClipperOffset::DoRound(int j, int k) // http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/Clipper/Properties/StrictlySimple.htm void Clipper::DoSimplePolygons() { - CLIPPERLIB_PROFILE_FUNC(); size_t i = 0; while (i < m_PolyOuts.size()) { diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 3ea1d80e4..d3b7a3b85 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -184,6 +184,9 @@ void AppConfig::set_defaults() if (get("show_hints").empty()) set("show_hints", "1"); + if (get("allow_auto_color_change").empty()) + set("allow_auto_color_change", "1"); + if (get("allow_ip_resolve").empty()) set("allow_ip_resolve", "1"); diff --git a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp index d78ce06d1..04dede546 100644 --- a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp +++ b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp @@ -451,8 +451,23 @@ SkeletalTrapezoidation::SkeletalTrapezoidation(const Polygons& polys, const Bead constructFromPolygons(polys); } +static bool has_finite_edge_with_non_finite_vertex(const Geometry::VoronoiDiagram &voronoi_diagram) +{ + for (const VoronoiUtils::vd_t::edge_type &edge : voronoi_diagram.edges()) { + if (edge.is_finite()) { + assert(edge.vertex0() != nullptr && edge.vertex1() != nullptr); + if (edge.vertex0() == nullptr || edge.vertex1() == nullptr || !VoronoiUtils::is_finite(*edge.vertex0()) || + !VoronoiUtils::is_finite(*edge.vertex1())) + return true; + } + } + return false; +} static bool detect_missing_voronoi_vertex(const Geometry::VoronoiDiagram &voronoi_diagram, const std::vector &segments) { + if (has_finite_edge_with_non_finite_vertex(voronoi_diagram)) + return true; + for (VoronoiUtils::vd_t::cell_type cell : voronoi_diagram.cells()) { if (!cell.incident_edge()) continue; // There is no spoon @@ -471,7 +486,8 @@ static bool detect_missing_voronoi_vertex(const Geometry::VoronoiDiagram &vorono VoronoiUtils::vd_t::edge_type *ending_vd_edge = nullptr; VoronoiUtils::vd_t::edge_type *edge = cell.incident_edge(); do { - if (edge->is_infinite()) continue; + if (edge->is_infinite() || edge->vertex0() == nullptr || edge->vertex1() == nullptr || !VoronoiUtils::is_finite(*edge->vertex0()) || !VoronoiUtils::is_finite(*edge->vertex1())) + continue; Vec2i64 v0 = VoronoiUtils::p(edge->vertex0()); Vec2i64 v1 = VoronoiUtils::p(edge->vertex1()); @@ -509,29 +525,36 @@ static bool has_missing_twin_edge(const SkeletalTrapezoidationGraph &graph) inline static std::unordered_map try_to_fix_degenerated_voronoi_diagram_by_rotation( Geometry::VoronoiDiagram &voronoi_diagram, const Polygons &polys, - Polygons &polys_copy, + Polygons &polys_rotated, std::vector &segments, const double fix_angle) { std::unordered_map vertex_mapping; - for (Polygon &poly : polys_copy) + for (Polygon &poly : polys_rotated) poly.rotate(fix_angle); - assert(polys_copy.size() == polys.size()); + assert(polys_rotated.size() == polys.size()); for (size_t poly_idx = 0; poly_idx < polys.size(); ++poly_idx) { - assert(polys_copy[poly_idx].size() == polys[poly_idx].size()); + assert(polys_rotated[poly_idx].size() == polys[poly_idx].size()); for (size_t point_idx = 0; point_idx < polys[poly_idx].size(); ++point_idx) - vertex_mapping.insert({polys[poly_idx][point_idx], polys_copy[poly_idx][point_idx]}); + vertex_mapping.insert({polys_rotated[poly_idx][point_idx], polys[poly_idx][point_idx]}); } segments.clear(); - for (size_t poly_idx = 0; poly_idx < polys_copy.size(); poly_idx++) - for (size_t point_idx = 0; point_idx < polys_copy[poly_idx].size(); point_idx++) - segments.emplace_back(&polys_copy, poly_idx, point_idx); + for (size_t poly_idx = 0; poly_idx < polys_rotated.size(); poly_idx++) + for (size_t point_idx = 0; point_idx < polys_rotated[poly_idx].size(); point_idx++) + segments.emplace_back(&polys_rotated, poly_idx, point_idx); voronoi_diagram.clear(); construct_voronoi(segments.begin(), segments.end(), &voronoi_diagram); +#ifdef ARACHNE_DEBUG_VORONOI + { + static int iRun = 0; + dump_voronoi_to_svg(debug_out_path("arachne_voronoi-diagram-rotated-%d.svg", iRun++).c_str(), voronoi_diagram, to_points(polys), to_lines(polys)); + } +#endif + assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(voronoi_diagram)); return vertex_mapping; @@ -591,10 +614,6 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys) } #endif -#ifdef ARACHNE_DEBUG - assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(voronoi_diagram)); -#endif - // Try to detect cases when some Voronoi vertex is missing and when // the Voronoi diagram is not planar. // When any Voronoi vertex is missing, or the Voronoi diagram is not @@ -715,6 +734,10 @@ process_voronoi_diagram: if (degenerated_voronoi_diagram) rotate_back_skeletal_trapezoidation_graph_after_fix(this->graph, fix_angle, vertex_mapping); +#ifdef ARACHNE_DEBUG + assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(voronoi_diagram)); +#endif + separatePointyQuadEndNodes(); graph.collapseSmallEdges(); diff --git a/src/libslic3r/Arachne/utils/ExtrusionLine.cpp b/src/libslic3r/Arachne/utils/ExtrusionLine.cpp index a5734f478..75e4d5338 100644 --- a/src/libslic3r/Arachne/utils/ExtrusionLine.cpp +++ b/src/libslic3r/Arachne/utils/ExtrusionLine.cpp @@ -268,13 +268,13 @@ void extrusion_paths_append(ExtrusionPaths &dst, const ClipperLib_Z::Paths &extr { for (const ClipperLib_Z::Path &extrusion_path : extrusion_paths) { ThickPolyline thick_polyline = Arachne::to_thick_polyline(extrusion_path); - Slic3r::append(dst, thick_polyline_to_extrusion_paths(thick_polyline, role, flow, scaled(0.05), float(SCALED_EPSILON))); + Slic3r::append(dst, thick_polyline_to_multi_path(thick_polyline, role, flow, scaled(0.05), float(SCALED_EPSILON)).paths); } } void extrusion_paths_append(ExtrusionPaths &dst, const Arachne::ExtrusionLine &extrusion, const ExtrusionRole role, const Flow &flow) { ThickPolyline thick_polyline = Arachne::to_thick_polyline(extrusion); - Slic3r::append(dst, thick_polyline_to_extrusion_paths(thick_polyline, role, flow, scaled(0.05), float(SCALED_EPSILON))); + Slic3r::append(dst, thick_polyline_to_multi_path(thick_polyline, role, flow, scaled(0.05), float(SCALED_EPSILON)).paths); } } // namespace Slic3r \ No newline at end of file diff --git a/src/libslic3r/Arachne/utils/VoronoiUtils.cpp b/src/libslic3r/Arachne/utils/VoronoiUtils.cpp index 3da556b47..82bd79523 100644 --- a/src/libslic3r/Arachne/utils/VoronoiUtils.cpp +++ b/src/libslic3r/Arachne/utils/VoronoiUtils.cpp @@ -15,6 +15,7 @@ Vec2i64 VoronoiUtils::p(const vd_t::vertex_type *node) { const double x = node->x(); const double y = node->y(); + assert(std::isfinite(x) && std::isfinite(y)); assert(x <= double(std::numeric_limits::max()) && x >= std::numeric_limits::lowest()); assert(y <= double(std::numeric_limits::max()) && y >= std::numeric_limits::lowest()); return {int64_t(x + 0.5 - (x < 0)), int64_t(y + 0.5 - (y < 0))}; // Round to the nearest integer coordinates. diff --git a/src/libslic3r/Arachne/utils/VoronoiUtils.hpp b/src/libslic3r/Arachne/utils/VoronoiUtils.hpp index e736f98bc..aa4693643 100644 --- a/src/libslic3r/Arachne/utils/VoronoiUtils.hpp +++ b/src/libslic3r/Arachne/utils/VoronoiUtils.hpp @@ -35,6 +35,11 @@ public: * The \p approximate_step_size is measured parallel to the \p source_segment, not along the parabola. */ static std::vector discretizeParabola(const Point &source_point, const Segment &source_segment, Point start, Point end, coord_t approximate_step_size, float transitioning_angle); + + static inline bool is_finite(const VoronoiUtils::vd_t::vertex_type &vertex) + { + return std::isfinite(vertex.x()) && std::isfinite(vertex.y()); + } }; } // namespace Slic3r::Arachne diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp index 6b06f8bab..1d6cd4ef1 100644 --- a/src/libslic3r/BoundingBox.hpp +++ b/src/libslic3r/BoundingBox.hpp @@ -18,7 +18,7 @@ public: 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)) {} + min(pmin), max(pmax), defined(pmin.x() < pmax.x() && pmin.y() < pmax.y()) {} BoundingBoxBase(const PointClass &p1, const PointClass &p2, const PointClass &p3) : min(p1), max(p1), defined(false) { merge(p2); merge(p3); } @@ -37,7 +37,7 @@ public: this->min = this->min.cwiseMin(vec); this->max = this->max.cwiseMax(vec); } - this->defined = (this->min(0) < this->max(0)) && (this->min(1) < this->max(1)); + this->defined = (this->min.x() < this->max.x()) && (this->min.y() < this->max.y()); } } @@ -58,15 +58,15 @@ public: BoundingBoxBase inflated(coordf_t delta) const throw() { BoundingBoxBase out(*this); out.offset(delta); return out; } PointClass center() const; bool contains(const PointClass &point) const { - return point(0) >= this->min(0) && point(0) <= this->max(0) - && point(1) >= this->min(1) && point(1) <= this->max(1); + return point.x() >= this->min.x() && point.x() <= this->max.x() + && point.y() >= this->min.y() && point.y() <= this->max.y(); } bool contains(const BoundingBoxBase &other) const { return contains(other.min) && contains(other.max); } bool overlap(const BoundingBoxBase &other) const { - return ! (this->max(0) < other.min(0) || this->min(0) > other.max(0) || - this->max(1) < other.min(1) || this->min(1) > other.max(1)); + return ! (this->max.x() < other.min.x() || this->min.x() > other.max.x() || + this->max.y() < other.min.y() || this->min.y() > other.max.y()); } bool operator==(const BoundingBoxBase &rhs) { return this->min == rhs.min && this->max == rhs.max; } bool operator!=(const BoundingBoxBase &rhs) { return ! (*this == rhs); } @@ -79,7 +79,7 @@ public: BoundingBox3Base() : BoundingBoxBase() {} BoundingBox3Base(const PointClass &pmin, const PointClass &pmax) : BoundingBoxBase(pmin, pmax) - { if (pmin(2) >= pmax(2)) BoundingBoxBase::defined = false; } + { if (pmin.z() >= pmax.z()) BoundingBoxBase::defined = false; } BoundingBox3Base(const PointClass &p1, const PointClass &p2, const PointClass &p3) : BoundingBoxBase(p1, p1) { merge(p2); merge(p3); } @@ -96,7 +96,7 @@ public: this->min = this->min.cwiseMin(vec); this->max = this->max.cwiseMax(vec); } - this->defined = (this->min(0) < this->max(0)) && (this->min(1) < this->max(1)) && (this->min(2) < this->max(2)); + this->defined = (this->min.x() < this->max.x()) && (this->min.y() < this->max.y()) && (this->min.z() < this->max.z()); } BoundingBox3Base(const std::vector &points) @@ -116,15 +116,17 @@ public: coordf_t max_size() const; bool contains(const PointClass &point) const { - return BoundingBoxBase::contains(point) && point(2) >= this->min(2) && point(2) <= this->max(2); + return BoundingBoxBase::contains(point) && point.z() >= this->min.z() && point.z() <= this->max.z(); } bool contains(const BoundingBox3Base& other) const { return contains(other.min) && contains(other.max); } + // Intersects without boundaries. bool intersects(const BoundingBox3Base& other) const { - return (this->min(0) < other.max(0)) && (this->max(0) > other.min(0)) && (this->min(1) < other.max(1)) && (this->max(1) > other.min(1)) && (this->min(2) < other.max(2)) && (this->max(2) > other.min(2)); + return this->min.x() < other.max.x() && this->max.x() > other.min.x() && this->min.y() < other.max.y() && this->max.y() > other.min.y() && + this->min.z() < other.max.z() && this->max.z() > other.min.z(); } }; @@ -212,13 +214,13 @@ public: template inline bool empty(const BoundingBoxBase &bb) { - return ! bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1); + return ! bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y(); } template inline bool empty(const BoundingBox3Base &bb) { - return ! bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1) || bb.min(2) >= bb.max(2); + return ! bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y() || bb.min.z() >= bb.max.z(); } inline BoundingBox scaled(const BoundingBoxf &bb) { return {scaled(bb.min), scaled(bb.max)}; } diff --git a/src/libslic3r/Brim.cpp b/src/libslic3r/Brim.cpp index f48d7ff0a..9ed56bea4 100644 --- a/src/libslic3r/Brim.cpp +++ b/src/libslic3r/Brim.cpp @@ -332,7 +332,7 @@ static std::vector inner_brim_area(const Print append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset, ClipperLib::jtSquare), ex_poly_holes_reversed)); if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim) - append(no_brim_area_object, diff_ex(ExPolygon(ex_poly.contour), shrink_ex(ex_poly_holes_reversed, no_brim_offset, ClipperLib::jtSquare))); + append(no_brim_area_object, diff_ex(ex_poly.contour, shrink_ex(ex_poly_holes_reversed, no_brim_offset, ClipperLib::jtSquare))); append(holes_reversed_object, ex_poly_holes_reversed); } diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index e47818835..f77bbaee8 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -258,6 +258,10 @@ set(SLIC3R_SOURCES Technologies.hpp Tesselate.cpp Tesselate.hpp + TreeSupport.cpp + TreeSupport.hpp + TreeModelVolumes.cpp + TreeModelVolumes.hpp TriangleMesh.cpp TriangleMesh.hpp TriangleMeshSlicer.cpp @@ -461,10 +465,6 @@ if(WIN32) target_link_libraries(libslic3r Psapi.lib) endif() -if(SLIC3R_PROFILE) - target_link_libraries(libslic3r Shiny) -endif() - if (APPLE) # This flag prevents the need for minimum SDK version 10.14 # currently, PS targets v10.12 diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index b2c5bd89e..d03cb5a70 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -8,17 +8,6 @@ #include "SVG.hpp" #endif /* CLIPPER_UTILS_DEBUG */ -// Profiling support using the Shiny intrusive profiler -//#define CLIPPER_UTILS_PROFILE -#if defined(SLIC3R_PROFILE) && defined(CLIPPER_UTILS_PROFILE) - #include - #define CLIPPERUTILS_PROFILE_FUNC() PROFILE_FUNC() - #define CLIPPERUTILS_PROFILE_BLOCK(name) PROFILE_BLOCK(name) -#else - #define CLIPPERUTILS_PROFILE_FUNC() - #define CLIPPERUTILS_PROFILE_BLOCK(name) -#endif - #define CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR (0.005f) namespace Slic3r { @@ -556,6 +545,8 @@ Slic3r::Polygons diff(const Slic3r::Surfaces &subject, const Slic3r::Polygons &c { return _clipper(ClipperLib::ctDifference, ClipperUtils::SurfacesProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset) { return _clipper(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); } +Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset) + { return _clipper(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonProvider(clip), do_safety_offset); } Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset) @@ -576,6 +567,8 @@ Slic3r::Polygons union_(const Slic3r::ExPolygons &subject) { return _clipper(ClipperLib::ctUnion, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::EmptyPathsProvider(), ApplySafetyOffset::No); } Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2) { return _clipper(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(subject2), ApplySafetyOffset::No); } +Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &subject2) + { return _clipper(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonProvider(subject2), ApplySafetyOffset::No); } template static ExPolygons _clipper_ex(ClipperLib::ClipType clipType, TSubject &&subject, TClip &&clip, ApplySafetyOffset do_safety_offset, ClipperLib::PolyFillType fill_type = ClipperLib::pftNonZero) @@ -585,6 +578,8 @@ Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygo { return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset) { return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::SurfacesProvider(clip), do_safety_offset); } +Slic3r::ExPolygons diff_ex(const Slic3r::Polygon &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset) + { return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset) @@ -715,6 +710,8 @@ Slic3r::Polylines diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygon { return _clipper_pl_closed(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip)); } Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip) { return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::SinglePathProvider(clip.points)); } +Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip) + { return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::ExPolygonProvider(clip)); } Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip) { return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::PolygonsProvider(clip)); } Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygons &clip) diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index e585e3011..0388af9a3 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -420,6 +420,7 @@ inline Slic3r::Lines diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygon // Safety offset is applied to the clipping polygons only. Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); +Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); @@ -436,6 +437,7 @@ Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip); +Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygons &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip); @@ -458,6 +460,7 @@ Slic3r::Polygons union_(const Slic3r::Polygons &subject); Slic3r::Polygons union_(const Slic3r::ExPolygons &subject); Slic3r::Polygons union_(const Slic3r::Polygons &subject, const ClipperLib::PolyFillType fillType); Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2); +Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &subject2); // May be used to "heal" unusual models (3DLabPrints etc.) by providing fill_type (pftEvenOdd, pftNonZero, pftPositive, pftNegative). Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, ClipperLib::PolyFillType fill_type = ClipperLib::pftNonZero); Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons &subject); diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index b8c046ceb..151043a04 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "libslic3r.h" #include "clonable_ptr.hpp" #include "Exception.hpp" @@ -1766,8 +1767,8 @@ public: // limit of a numeric input. // If not set, the is set to // By setting min=0, only nonnegative input is allowed. - int min = INT_MIN; - int max = INT_MAX; + float min = -FLT_MAX; + float max = FLT_MAX; // To check if it's not a typo and a % is missing double max_literal = 1; ConfigOptionMode mode = comSimple; diff --git a/src/libslic3r/CustomGCode.hpp b/src/libslic3r/CustomGCode.hpp index 334b40431..c4a47b9a3 100644 --- a/src/libslic3r/CustomGCode.hpp +++ b/src/libslic3r/CustomGCode.hpp @@ -66,6 +66,8 @@ struct Info bool operator==(const Info& rhs) const { + if (rhs.gcodes.empty() && this->gcodes.empty()) + return true; // don't respect to the comparison of the mode, when g_codes are empty return (rhs.mode == this->mode ) && (rhs.gcodes == this->gcodes ); } diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp index 069c6e602..7e22127cd 100644 --- a/src/libslic3r/ExPolygon.cpp +++ b/src/libslic3r/ExPolygon.cpp @@ -12,27 +12,6 @@ namespace Slic3r { -ExPolygon::operator Points() const -{ - Points points; - Polygons pp = *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); - } - return points; -} - -ExPolygon::operator Polygons() const -{ - return to_polygons(*this); -} - -ExPolygon::operator Polylines() const -{ - return to_polylines(*this); -} - void ExPolygon::scale(double factor) { contour.scale(factor); @@ -149,7 +128,7 @@ bool ExPolygon::overlaps(const ExPolygon &other) const svg.draw_outline(*this); svg.draw_outline(other, "blue"); #endif - Polylines pl_out = intersection_pl((Polylines)other, *this); + Polylines pl_out = intersection_pl(to_polylines(other), *this); #if 0 svg.draw(pl_out, "red"); #endif diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp index 7eccf2ec8..3e42c8670 100644 --- a/src/libslic3r/ExPolygon.hpp +++ b/src/libslic3r/ExPolygon.hpp @@ -34,9 +34,6 @@ public: Polygon contour; Polygons holes; - operator Points() const; - operator Polygons() const; - operator Polylines() const; void clear() { contour.points.clear(); holes.clear(); } void scale(double factor); void translate(double x, double y) { this->translate(Point(coord_t(x), coord_t(y))); } @@ -281,6 +278,19 @@ inline ExPolygons to_expolygons(Polygons &&polys) return ex_polys; } +inline Points to_points(const ExPolygon &expoly) +{ + size_t cnt = expoly.contour.size(); + for (const Polygon &hole : expoly.holes) + cnt += hole.size(); + Points out; + out.reserve(cnt); + append(out, expoly.contour.points); + for (const Polygon &hole : expoly.holes) + append(out, hole.points); + return out; +} + inline void polygons_append(Polygons &dst, const ExPolygon &src) { dst.reserve(dst.size() + src.holes.size() + 1); diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index aede1ad67..0591e8a26 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -179,6 +179,16 @@ private: ExtrusionRole m_role; }; +class ExtrusionPathOriented : public ExtrusionPath +{ +public: + ExtrusionPathOriented(ExtrusionRole role, double mm3_per_mm, float width, float height) : ExtrusionPath(role, mm3_per_mm, width, height) {} + ExtrusionEntity* clone() const override { return new ExtrusionPathOriented(*this); } + // Create a new object, initialize it with this object using the move semantics. + ExtrusionEntity* clone_move() override { return new ExtrusionPathOriented(std::move(*this)); } + virtual bool can_reverse() const { return false; } +}; + typedef std::vector ExtrusionPaths; // Single continuous extrusion path, possibly with varying extrusion thickness, extrusion height or bridging / non bridging. @@ -330,12 +340,12 @@ inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, E polylines.clear(); } -inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, const Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) +inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, const Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height, bool can_reverse = true) { dst.reserve(dst.size() + polylines.size()); for (const Polyline &polyline : polylines) if (polyline.is_valid()) { - ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height); + ExtrusionPath* extrusion_path = can_reverse ? new ExtrusionPath(role, mm3_per_mm, width, height) : new ExtrusionPathOriented(role, mm3_per_mm, width, height); dst.push_back(extrusion_path); extrusion_path->polyline = polyline; } diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 49c186df6..8278db960 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -427,14 +427,13 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: for (const ThickPolyline &thick_polyline : thick_polylines) { Flow new_flow = surface_fill.params.flow.with_spacing(float(f->spacing)); - ExtrusionPaths paths = thick_polyline_to_extrusion_paths(thick_polyline, surface_fill.params.extrusion_role, new_flow, scaled(0.05), 0); + ExtrusionMultiPath multi_path = thick_polyline_to_multi_path(thick_polyline, surface_fill.params.extrusion_role, new_flow, scaled(0.05), float(SCALED_EPSILON)); // Append paths to collection. - if (!paths.empty()) { - if (paths.front().first_point() == paths.back().last_point()) - eec->entities.emplace_back(new ExtrusionLoop(std::move(paths))); + if (!multi_path.empty()) { + if (multi_path.paths.front().first_point() == multi_path.paths.back().last_point()) + eec->entities.emplace_back(new ExtrusionLoop(std::move(multi_path.paths))); else - for (ExtrusionPath &path : paths) - eec->entities.emplace_back(new ExtrusionPath(std::move(path))); + eec->entities.emplace_back(new ExtrusionMultiPath(std::move(multi_path))); } } diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index a8d5d20e2..b38c5c9f8 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -21,6 +21,8 @@ #include "FillAdaptive.hpp" #include "FillLightning.hpp" +#include + // #define INFILL_DEBUG_OUTPUT namespace Slic3r { @@ -129,8 +131,8 @@ std::pair Fill::_infill_direction(const Surface *surface) const float out_angle = this->angle; if (out_angle == FLT_MAX) { - //FIXME Vojtech: Add a warning? - printf("Using undefined infill angle\n"); + assert(false); + BOOST_LOG_TRIVIAL(error) << "Using undefined infill angle"; out_angle = 0.f; } diff --git a/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp index 17bdfafaf..69f530720 100644 --- a/src/libslic3r/Fill/FillConcentric.cpp +++ b/src/libslic3r/Fill/FillConcentric.cpp @@ -106,6 +106,7 @@ void FillConcentric::_fill_surface_single(const FillParams ¶ms, thick_polyline.points.emplace_back(thick_polyline.points.front()); } thick_polylines_out.emplace_back(std::move(thick_polyline)); + last_pos = thick_polylines_out.back().last_point(); } // clip the paths to prevent the extruder from getting exactly on the first point of the loop diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp index ba7461c5f..bb93d824b 100644 --- a/src/libslic3r/Fill/FillRectilinear.cpp +++ b/src/libslic3r/Fill/FillRectilinear.cpp @@ -414,7 +414,7 @@ public: // bool sticks_removed = remove_sticks(polygons_src); // if (sticks_removed) BOOST_LOG_TRIVIAL(error) << "Sticks removed!"; - polygons_outer = aoffset1 == 0 ? polygons_src : offset(polygons_src, float(aoffset1), ClipperLib::jtMiter, miterLimit); + polygons_outer = aoffset1 == 0 ? to_polygons(polygons_src) : offset(polygons_src, float(aoffset1), ClipperLib::jtMiter, miterLimit); if (aoffset2 < 0) polygons_inner = shrink(polygons_outer, float(aoffset1 - aoffset2), ClipperLib::jtMiter, miterLimit); // Filter out contours with zero area or small area, contours with 2 points only. diff --git a/src/libslic3r/Fill/Lightning/TreeNode.cpp b/src/libslic3r/Fill/Lightning/TreeNode.cpp index 982d47b10..801a46df4 100644 --- a/src/libslic3r/Fill/Lightning/TreeNode.cpp +++ b/src/libslic3r/Fill/Lightning/TreeNode.cpp @@ -141,18 +141,6 @@ NodeSPtr Node::closestNode(const Point& loc) return result; } -bool inside(const Polygons &polygons, const Point &p) -{ - int poly_count_inside = 0; - for (const Polygon &poly : polygons) { - const int is_inside_this_poly = ClipperLib::PointInPolygon(p, poly.points); - if (is_inside_this_poly == -1) - return true; - poly_count_inside += is_inside_this_poly; - } - return (poly_count_inside % 2) == 1; -} - bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeGrid::Grid& outline_locator, Point& result, const coord_t within_max_dist) { struct Visitor { @@ -192,7 +180,7 @@ bool Node::realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locat if (outlines.empty()) return false; - if (inside(outlines, m_p)) { + if (contains(outlines, m_p)) { // Only keep children that have an unbroken connection to here, realign will put the rest in rerooted parts due to recursion: Point coll; bool reground_me = false; diff --git a/src/libslic3r/Format/STEP.cpp b/src/libslic3r/Format/STEP.cpp index 00d692fc6..081726704 100644 --- a/src/libslic3r/Format/STEP.cpp +++ b/src/libslic3r/Format/STEP.cpp @@ -64,11 +64,11 @@ LoadStepFn get_load_step_fn() load_step_fn = reinterpret_cast(dlsym(plugin_ptr, fn_name)); if (!load_step_fn) { dlclose(plugin_ptr); - throw Slic3r::RuntimeError(std::string("Cannot load function from OCCTWrapper.dll: ") + fn_name + throw Slic3r::RuntimeError(std::string("Cannot load function from OCCTWrapper.so: ") + fn_name + "\n\n" + dlerror()); } } else { - throw Slic3r::RuntimeError(std::string("Cannot load OCCTWrapper.dll:\n\n") + dlerror()); + throw Slic3r::RuntimeError(std::string("Cannot load OCCTWrapper.so:\n\n") + dlerror()); } #endif } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 80aac2691..e44dfb742 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -53,8 +53,6 @@ using slic3r_tbb_filtermode = tbb::filter; #endif -#include - using namespace std::literals::string_view_literals; #if 0 @@ -735,8 +733,6 @@ namespace DoExport { void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb) { - PROFILE_CLEAR(); - CNumericLocalesSetter locales_setter; // Does the file exist? If so, we hope that it is still valid. @@ -828,10 +824,6 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu BOOST_LOG_TRIVIAL(info) << "Exporting G-code finished" << log_memory_info(); print->set_done(psGCodeExport); - - // Write the profiler measurements to file - PROFILE_UPDATE(); - PROFILE_OUTPUT(debug_out_path("gcode-export-profile.txt").c_str()); } // free functions called by GCode::_do_export() @@ -1048,8 +1040,6 @@ std::vector sort_object_instances_by_model_order(const Pri void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb) { - PROFILE_FUNC(); - // modifies m_silent_time_estimator_enabled DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled); @@ -2639,6 +2629,12 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, const std::string_view descr // thus empty path segments will not be produced by G-code export. loop.split_at(last_pos, false, scaled(0.0015)); + for (auto it = std::next(loop.paths.begin()); it != loop.paths.end(); ++it) { + assert(it->polyline.points.size() >= 2); + assert(std::prev(it)->polyline.last_point() == it->polyline.first_point()); + } + assert(loop.paths.front().first_point() == loop.paths.back().last_point()); + // clip the path to avoid the extruder to get exactly on the first point of the loop; // if polyline was shorter than the clipping distance we'd get a null polyline, so // we discard it in that case @@ -2665,8 +2661,21 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, const std::string_view descr // reset acceleration gcode += m_writer.set_acceleration((unsigned int)(m_config.default_acceleration.value + 0.5)); - if (m_wipe.enable) - m_wipe.path = paths.front().polyline; // TODO: don't limit wipe to last path + if (m_wipe.enable) { + m_wipe.path = paths.front().polyline; + + for (auto it = std::next(paths.begin()); it != paths.end(); ++it) { + if (is_bridge(it->role())) + break; // Don't perform a wipe on bridges. + + assert(it->polyline.points.size() >= 2); + assert(m_wipe.path.points.back() == it->polyline.first_point()); + if (m_wipe.path.points.back() != it->polyline.first_point()) + break; // ExtrusionLoop is interrupted in some place. + + m_wipe.path.points.insert(m_wipe.path.points.end(), it->polyline.points.begin() + 1, it->polyline.points.end()); + } + } // make a little move inwards before leaving loop if (paths.back().role() == erExternalPerimeter && m_layer != NULL && m_config.perimeters.value > 1 && paths.front().size() >= 2 && paths.back().polyline.points.size() >= 3) { @@ -2709,6 +2718,10 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, const std::string_view descr std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, const std::string_view description, double speed) { + for (auto it = std::next(multipath.paths.begin()); it != multipath.paths.end(); ++it) { + assert(it->polyline.points.size() >= 2); + assert(std::prev(it)->polyline.last_point() == it->polyline.first_point()); + } // extrude along the path std::string gcode; for (ExtrusionPath path : multipath.paths) { @@ -2716,8 +2729,20 @@ std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, const std::s gcode += this->_extrude(path, description, speed); } if (m_wipe.enable) { - m_wipe.path = std::move(multipath.paths.back().polyline); // TODO: don't limit wipe to last path + m_wipe.path = std::move(multipath.paths.back().polyline); m_wipe.path.reverse(); + + for (auto it = std::next(multipath.paths.rbegin()); it != multipath.paths.rend(); ++it) { + if (is_bridge(it->role())) + break; // Do not perform a wipe on bridges. + + assert(it->polyline.points.size() >= 2); + assert(m_wipe.path.points.back() == it->polyline.last_point()); + if (m_wipe.path.points.back() != it->polyline.last_point()) + break; // ExtrusionMultiPath is interrupted in some place. + + m_wipe.path.points.insert(m_wipe.path.points.end(), it->polyline.points.rbegin() + 1, it->polyline.points.rend()); + } } // reset acceleration gcode += m_writer.set_acceleration((unsigned int)floor(m_config.default_acceleration.value + 0.5)); @@ -3134,15 +3159,26 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role) return false; } - if (role == erSupportMaterial) { - const SupportLayer* support_layer = dynamic_cast(m_layer); - //FIXME support_layer->support_islands.contains should use some search structure! - if (support_layer != NULL && diff_pl(travel, support_layer->support_islands).empty()) - // skip retraction if this is a travel move inside a support material island - //FIXME not retracting over a long path may cause oozing, which in turn may result in missing material - // at the end of the extrusion path! - return false; - } + if (role == erSupportMaterial) + if (const SupportLayer *support_layer = dynamic_cast(m_layer); + support_layer != nullptr && ! support_layer->support_islands_bboxes.empty()) { + BoundingBox bbox_travel = get_extents(travel); + Polylines trimmed; + bool trimmed_initialized = false; + for (const BoundingBox &bbox : support_layer->support_islands_bboxes) + if (bbox.overlap(bbox_travel)) { + const auto &island = support_layer->support_islands[&bbox - support_layer->support_islands_bboxes.data()]; + trimmed = trimmed_initialized ? diff_pl(trimmed, island) : diff_pl(travel, island); + trimmed_initialized = true; + if (trimmed.empty()) + // skip retraction if this is a travel move inside a support material island + //FIXME not retracting over a long path may cause oozing, which in turn may result in missing material + // at the end of the extrusion path! + return false; + // Not sure whether updating the boudning box isn't too expensive. + //bbox_travel = get_extents(trimmed); + } + } if (m_config.only_retract_when_crossing_perimeters && m_layer != nullptr && m_config.fill_density.value > 0 && m_layer->any_internal_region_slice_contains(travel)) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index e1341fa98..3868c87df 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -2683,7 +2683,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) if (m_height == 0.0f) m_height = DEFAULT_TOOLPATH_HEIGHT; - if (m_end_position[Z] == 0.0f) + if (m_end_position[Z] == 0.0f || (m_extrusion_role == erCustom && m_layer_id == 0)) m_end_position[Z] = m_height; #if ENABLE_PROCESS_G2_G3_LINES @@ -2913,7 +2913,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) } if (m_spiral_vase_active && !m_result.spiral_vase_layers.empty()) { - if (m_result.spiral_vase_layers.back().first == FLT_MAX && delta_pos[Z] > 0.0) + if (m_result.spiral_vase_layers.back().first == FLT_MAX && delta_pos[Z] >= 0.0) // replace layer height placeholder with correct value m_result.spiral_vase_layers.back().first = static_cast(m_end_position[Z]); if (!m_result.moves.empty()) diff --git a/src/libslic3r/GCode/PostProcessor.cpp b/src/libslic3r/GCode/PostProcessor.cpp index ced74e3eb..de1807dbb 100644 --- a/src/libslic3r/GCode/PostProcessor.cpp +++ b/src/libslic3r/GCode/PostProcessor.cpp @@ -153,7 +153,7 @@ static int run_script(const std::string &script, const std::string &gcode, std:: { // Try to obtain user's default shell const char *shell = ::getenv("SHELL"); - if (shell == nullptr) { shell = "sh"; } + if (shell == nullptr) { shell = "/bin/sh"; } // Quote and escape the gcode path argument std::string command { script }; diff --git a/src/libslic3r/GCode/PressureEqualizer.cpp b/src/libslic3r/GCode/PressureEqualizer.cpp index b173cb684..7135c0a5c 100644 --- a/src/libslic3r/GCode/PressureEqualizer.cpp +++ b/src/libslic3r/GCode/PressureEqualizer.cpp @@ -669,17 +669,32 @@ inline void PressureEqualizer::push_to_output(const char *text, const size_t len output_buffer[output_buffer_length] = 0; } -inline bool PressureEqualizer::is_just_feedrate_provided(const GCodeLine &line) +inline bool is_just_line_with_extrude_set_speed_tag(const std::string &line) { - return line.pos_provided[4] && !line.pos_provided[0] && !line.pos_provided[1] && !line.pos_provided[2] && !line.pos_provided[3]; + if (line.empty() && !boost::starts_with(line, "G1 ") && !boost::ends_with(line, EXTRUDE_SET_SPEED_TAG)) + return false; + + const char *p_line = line.data() + 3; + const char *const line_end = line.data() + line.length() - 1; + while (!is_eol(*p_line)) { + if (toupper(*p_line++) == 'F') + break; + else + return false; + } + parse_float(p_line, line_end - p_line); + eatws(p_line); + p_line += EXTRUDE_SET_SPEED_TAG.length(); + return p_line <= line_end && is_eol(*p_line); } void PressureEqualizer::push_line_to_output(const size_t line_idx, const float new_feedrate, const char *comment) { const GCodeLine &line = this->m_gcode_lines[line_idx]; - if (line_idx > 0) { - const GCodeLine &prev_line = this->m_gcode_lines[line_idx - 1]; - if (prev_line.extrude_set_speed_tag && this->is_just_feedrate_provided(prev_line)) + if (line_idx > 0 && output_buffer_length > 0) { + const std::string prev_line_str = std::string(output_buffer.begin() + int(this->output_buffer_prev_length), + output_buffer.begin() + int(this->output_buffer_length) + 1); + if (is_just_line_with_extrude_set_speed_tag(prev_line_str)) this->output_buffer_length = this->output_buffer_prev_length; // Remove the last line because it only sets the speed for an empty block of g-code lines, so it is useless. else push_to_output(EXTRUDE_END_TAG.data(), EXTRUDE_END_TAG.length(), true); diff --git a/src/libslic3r/GCode/PressureEqualizer.hpp b/src/libslic3r/GCode/PressureEqualizer.hpp index 068b01df4..d6b7f2a4f 100644 --- a/src/libslic3r/GCode/PressureEqualizer.hpp +++ b/src/libslic3r/GCode/PressureEqualizer.hpp @@ -193,8 +193,6 @@ private: // Push a G-code line to the output. void push_line_to_output(size_t line_idx, float new_feedrate, const char *comment); - inline bool is_just_feedrate_provided(const GCodeLine &line); - public: std::queue m_layer_results; diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 5e86e902a..88d73277c 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -455,6 +455,7 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const } Polygon polygon = orig_polygon; bool was_clockwise = polygon.make_counter_clockwise(); + float angle_arm_len = region != nullptr ? region->flow(FlowRole::frExternalPerimeter).nozzle_diameter() : 0.5f; std::vector lengths { }; for (size_t point_idx = 0; point_idx < polygon.size() - 1; ++point_idx) { @@ -462,7 +463,7 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const } lengths.push_back(std::max((unscale(polygon[0]) - unscale(polygon[polygon.size() - 1])).norm(), 0.1)); std::vector polygon_angles = calculate_polygon_angles_at_vertices(polygon, lengths, - SeamPlacer::polygon_local_angles_arm_distance); + angle_arm_len); result.perimeters.push_back( { }); Perimeter &perimeter = result.perimeters.back(); @@ -949,6 +950,10 @@ void pick_random_seam_point(const std::vector &perimeter_points, }; std::vector viables; + const Vec3f pseudornd_seed = perimeter_points[viable_example_index].position; + float rand = std::abs(sin(pseudornd_seed.dot(Vec3f(12.9898f,78.233f, 133.3333f))) * 43758.5453f); + rand = rand - (int) rand; + for (size_t index = start_index; index < end_index; ++index) { if (comparator.are_similar(perimeter_points[index], perimeter_points[viable_example_index])) { // index ok, push info into viables @@ -976,7 +981,7 @@ void pick_random_seam_point(const std::vector &perimeter_points, float len_sum = std::accumulate(viables.begin(), viables.end(), 0.0f, [](const float acc, const Viable &v) { return acc + v.edge_length; }); - float picked_len = len_sum * (rand() / (float(RAND_MAX) + 1)); + float picked_len = len_sum * rand; size_t point_idx = 0; while (picked_len - viables[point_idx].edge_length > 0) { @@ -997,11 +1002,7 @@ class PerimeterDistancer { public: PerimeterDistancer(const Layer *layer) { - static const float eps = float(scale_(layer->object()->config().slice_closing_radius.value)); - // merge with offset - ExPolygons merged = layer->merged(eps); - // ofsset back - ExPolygons layer_outline = offset_ex(merged, -eps); + ExPolygons layer_outline = layer->lslices; for (const ExPolygon &island : layer_outline) { assert(island.contour.is_counter_clockwise()); for (const auto &line : island.contour.lines()) { @@ -1017,8 +1018,8 @@ public: tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(lines); } - float distance_from_perimeter(const Point &point) const { - Vec2d p = unscale(point); + float distance_from_perimeter(const Vec2f &point) const { + Vec2d p = point.cast(); size_t hit_idx_out { }; Vec2d hit_point_out = Vec2d::Zero(); auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, hit_idx_out, hit_point_out); @@ -1110,20 +1111,19 @@ void SeamPlacer::calculate_overhangs_and_layer_embedding(const PrintObject *po) std::unique_ptr current_layer_distancer = std::make_unique(po->layers()[layer_idx]); for (SeamCandidate &perimeter_point : layers[layer_idx].points) { - Point point = Point::new_scale(Vec2f { perimeter_point.position.head<2>() }); + Vec2f point = Vec2f { perimeter_point.position.head<2>() }; if (prev_layer_distancer.get() != nullptr) { - perimeter_point.overhang = (prev_layer_distancer->distance_from_perimeter(point) - + 0.5f * perimeter_point.perimeter.flow_width + perimeter_point.overhang = prev_layer_distancer->distance_from_perimeter(point) + + 0.6f * perimeter_point.perimeter.flow_width - tan(SeamPlacer::overhang_angle_threshold) - * po->layers()[layer_idx]->height) - / (3.0f * perimeter_point.perimeter.flow_width); - //NOTE disables the feature to place seams on slowly decreasing areas. Remove the following line to enable. - perimeter_point.overhang = perimeter_point.overhang < 0.0f ? 0.0f : perimeter_point.overhang; + * po->layers()[layer_idx]->height; + perimeter_point.overhang = + perimeter_point.overhang < 0.0f ? 0.0f : perimeter_point.overhang; } if (should_compute_layer_embedding) { // search for embedded perimeter points (points hidden inside the print ,e.g. multimaterial join, best position for seam) perimeter_point.embedded_distance = current_layer_distancer->distance_from_perimeter(point) - + 0.5f * perimeter_point.perimeter.flow_width; + + 0.6f * perimeter_point.perimeter.flow_width; } } @@ -1381,7 +1381,7 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: observations[index] = current.position.head<2>(); observation_points[index] = current.position.z(); weights[index] = angle_weight(current.local_ccw_angle); - float sign = layer_angle > 2.0 * std::abs(current.local_ccw_angle) ? -1.0f : 1.0f; + float sign = layer_angle > 2.0 * std::abs(current.local_ccw_angle) ? -0.8f : 1.0f; if (current.type == EnforcedBlockedSeamPoint::Enforced) { sign = 1.0f; weights[index] += 3.0f; @@ -1399,10 +1399,10 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: // Perimeter structure of the point; also set flag aligned to true for (size_t index = 0; index < seam_string.size(); ++index) { const auto &pair = seam_string[index]; - float t = std::min(1.0f, std::abs(layers[pair.first].points[pair.second].local_ccw_angle) - / SeamPlacer::sharp_angle_snapping_threshold); + float t = std::min(1.0f, std::pow(std::abs(layers[pair.first].points[pair.second].local_ccw_angle) + / SeamPlacer::sharp_angle_snapping_threshold, 3.0f)); if (layers[pair.first].points[pair.second].type == EnforcedBlockedSeamPoint::Enforced){ - t = std::max(0.7f, t); + t = std::max(0.4f, t); } Vec3f current_pos = layers[pair.first].points[pair.second].position; @@ -1533,16 +1533,40 @@ void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool extern const size_t layer_index = layer->id() - po->slicing_parameters().raft_layers(); const double unscaled_z = layer->slice_z; + auto get_next_loop_point = [&loop](ExtrusionLoop::ClosestPathPoint current) { + current.segment_idx += 1; + if (current.segment_idx >= loop.paths[current.path_idx].polyline.points.size()) { + current.path_idx = next_idx_modulo(current.path_idx, loop.paths.size()); + current.segment_idx = 0; + } + current.foot_pt = loop.paths[current.path_idx].polyline.points[current.segment_idx]; + return current; + }; + const PrintObjectSeamData::LayerSeams &layer_perimeters = m_seam_per_object.find(layer->object())->second.layers[layer_index]; - // Find the closest perimeter in the SeamPlacer to the first point of this loop. - size_t closest_perimeter_point_index; - { - const Point &fp = loop.first_point(); - Vec2f unscaled_p = unscaled(fp); - closest_perimeter_point_index = find_closest_point(*layer_perimeters.points_tree.get(), - to_3d(unscaled_p, float(unscaled_z))); + // Find the closest perimeter in the SeamPlacer to this loop. + // Repeat search until two consecutive points of the loop are found, that result in the same closest_perimeter + // This is beacuse with arachne, T-Junctions may exist and sometimes the wrong perimeter was chosen + size_t closest_perimeter_point_index = 0; + { // local space for the closest_perimeter_point_index + Perimeter *closest_perimeter = nullptr; + ExtrusionLoop::ClosestPathPoint closest_point{0,0,loop.paths[0].polyline.points[0]}; + size_t points_count = std::accumulate(loop.paths.begin(), loop.paths.end(), 0, [](size_t acc,const ExtrusionPath& p) { + return acc + p.polyline.points.size(); + }); + for (size_t _ = 0; _ < points_count; ++_) { + Vec2f unscaled_p = unscaled(closest_point.foot_pt); + closest_perimeter_point_index = find_closest_point(*layer_perimeters.points_tree.get(), + to_3d(unscaled_p, float(unscaled_z))); + if (closest_perimeter != &layer_perimeters.points[closest_perimeter_point_index].perimeter) { + closest_perimeter = &layer_perimeters.points[closest_perimeter_point_index].perimeter; + closest_point = get_next_loop_point(closest_point); + } else { + break; + } + } } Vec3f seam_position; diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index 2f67f6616..8d52fd79f 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -113,12 +113,10 @@ public: //square of number of rays per sample point static constexpr size_t sqr_rays_per_sample_point = 5; - // arm length used during angles computation - static constexpr float polygon_local_angles_arm_distance = 0.3f; // snapping angle - angles larger than this value will be snapped to during seam painting static constexpr float sharp_angle_snapping_threshold = 55.0f * float(PI) / 180.0f; // overhang angle for seam placement that still yields good results, in degrees, measured from vertical direction - static constexpr float overhang_angle_threshold = 45.0f * float(PI) / 180.0f; + static constexpr float overhang_angle_threshold = 50.0f * float(PI) / 180.0f; // determines angle importance compared to visibility ( neutral value is 1.0f. ) static constexpr float angle_importance_aligned = 0.6f; diff --git a/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp index 44b0b89f6..cd6cedf51 100644 --- a/src/libslic3r/GCodeReader.cpp +++ b/src/libslic3r/GCodeReader.cpp @@ -10,7 +10,6 @@ #include "LocalesUtils.hpp" -#include #include namespace Slic3r { @@ -37,14 +36,11 @@ void GCodeReader::apply_config(const DynamicPrintConfig &config) const char* GCodeReader::parse_line_internal(const char *ptr, const char *end, GCodeLine &gline, std::pair &command) { - PROFILE_FUNC(); - assert(is_decimal_separator_point()); // command and args const char *c = ptr; { - PROFILE_BLOCK(command_and_args); // Skip the whitespaces. command.first = skip_whitespaces(c); // Skip the command. @@ -98,10 +94,8 @@ const char* GCodeReader::parse_line_internal(const char *ptr, const char *end, G for (; ! is_end_of_line(*c); ++ c); // Copy the raw string including the comment, without the trailing newlines. - if (c > ptr) { - PROFILE_BLOCK(copy_raw_string); + if (c > ptr) gline.m_raw.assign(ptr, c); - } // Skip the trailing newlines. if (*c == '\r') @@ -117,7 +111,6 @@ const char* GCodeReader::parse_line_internal(const char *ptr, const char *end, G void GCodeReader::update_coordinates(GCodeLine &gline, std::pair &command) { - PROFILE_FUNC(); if (*command.first == 'G') { int cmd_len = int(command.second - command.first); if ((cmd_len == 2 && (command.first[1] == '0' || command.first[1] == '1')) || diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index c5279c0f5..c2caf5766 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -23,8 +23,8 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config) bool use_mach_limits = print_config.gcode_flavor.value == gcfMarlinLegacy || print_config.gcode_flavor.value == gcfMarlinFirmware || print_config.gcode_flavor.value == gcfRepRapFirmware; - m_max_acceleration = std::lrint((use_mach_limits && print_config.machine_limits_usage.value == MachineLimitsUsage::EmitToGCode) ? - print_config.machine_max_acceleration_extruding.values.front() : 0); + m_max_acceleration = static_cast(std::round((use_mach_limits && print_config.machine_limits_usage.value == MachineLimitsUsage::EmitToGCode) ? + print_config.machine_max_acceleration_extruding.values.front() : 0)); } void GCodeWriter::set_extruders(std::vector extruder_ids) diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 3070dbe42..13878e9ec 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -577,7 +577,6 @@ void Transformation::set_scaling_factor(const Vec3d& scaling_factor) void Transformation::set_scaling_factor(Axis axis, double scaling_factor) { #if ENABLE_WORLD_COORDINATE - assert(scaling_factor > 0.0); auto [rotation, scale] = extract_rotation_scale(m_matrix); scale(axis, axis) = scaling_factor; diff --git a/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp b/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp index caaf1ee9c..062a3b397 100644 --- a/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp +++ b/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp @@ -3,6 +3,7 @@ #include #include "libslic3r/Geometry/Voronoi.hpp" +#include "libslic3r/Arachne/utils/VoronoiUtils.hpp" #include "VoronoiUtilsCgal.hpp" @@ -28,7 +29,8 @@ bool VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(const VD &voronoi_ if (edge.color() != 0) continue; - if (edge.is_finite() && edge.is_linear()) { + if (edge.is_finite() && edge.is_linear() && edge.vertex0() != nullptr && edge.vertex1() != nullptr && + Arachne::VoronoiUtils::is_finite(*edge.vertex0()) && Arachne::VoronoiUtils::is_finite(*edge.vertex1())) { segments.emplace_back(to_cgal_point(*edge.vertex0()), to_cgal_point(*edge.vertex1())); edge.color(1); assert(edge.twin() != nullptr); @@ -73,7 +75,8 @@ bool VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(const VoronoiDiagram &vor do { // FIXME Lukas H.: Also process parabolic segments. - if (edge->is_finite() && edge->is_linear()) + if (edge->is_finite() && edge->is_linear() && edge->vertex0() != nullptr && edge->vertex1() != nullptr && + Arachne::VoronoiUtils::is_finite(*edge->vertex0()) && Arachne::VoronoiUtils::is_finite(*edge->vertex1())) edges.emplace_back(edge); edge = edge->rot_next(); diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 21de030cd..e11c740e6 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -197,6 +197,8 @@ public: // Polygons covered by the supports: base, interface and contact areas. // Used to suppress retraction if moving for a support extrusion over these support_islands. ExPolygons support_islands; + // Slightly inflated bounding boxes of the above, for faster intersection query. + std::vector support_islands_bboxes; // Extrusion paths for the support base and for the support interface and contacts. ExtrusionEntityCollection support_fills; diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index dfae5f188..1996a58b5 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -101,7 +101,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec g.overhang_flow = this->bridging_flow(frPerimeter); g.solid_infill_flow = this->flow(frSolidInfill); - if (this->layer()->object()->config().perimeter_generator.value == PerimeterGeneratorType::Arachne) + if (this->layer()->object()->config().perimeter_generator.value == PerimeterGeneratorType::Arachne && !spiral_vase) g.process_arachne(); else g.process_classic(); @@ -386,10 +386,12 @@ void LayerRegion::prepare_fill_surfaces() bool spiral_vase = this->layer()->object()->print()->config().spiral_vase; // if no solid layers are requested, turn top/bottom surfaces to internal + // For Lightning infill, infill_only_where_needed is ignored because both + // do a similar thing, and their combination doesn't make much sense. if (! spiral_vase && this->region().config().top_solid_layers == 0) { for (Surface &surface : this->fill_surfaces.surfaces) if (surface.is_top()) - surface.surface_type = this->layer()->object()->config().infill_only_where_needed ? stInternalVoid : stInternal; + surface.surface_type = this->layer()->object()->config().infill_only_where_needed && this->region().config().fill_pattern != ipLightning ? stInternalVoid : stInternal; } if (this->region().config().bottom_solid_layers == 0) { for (Surface &surface : this->fill_surfaces.surfaces) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 830d48571..e61f9097d 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -21,12 +21,12 @@ namespace Slic3r { -ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, const float tolerance, const float merge_tolerance) +ExtrusionMultiPath thick_polyline_to_multi_path(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, const float tolerance, const float merge_tolerance) { - ExtrusionPaths paths; - ExtrusionPath path(role); - ThickLines lines = thick_polyline.thicklines(); - + ExtrusionMultiPath multi_path; + ExtrusionPath path(role); + ThickLines lines = thick_polyline.thicklines(); + for (int i = 0; i < (int)lines.size(); ++i) { const ThickLine& line = lines[i]; assert(line.a_width >= SCALED_EPSILON && line.b_width >= SCALED_EPSILON); @@ -38,8 +38,8 @@ ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_poly path.polyline.points.back() = line.b; // If the variable path is non-empty, connect this tiny line to it. else if (i + 1 < (int)lines.size()) // If there is at least one following line, connect this tiny line to it. lines[i + 1].a = line.a; - else if (!paths.empty()) - paths.back().polyline.points.back() = line.b; // Connect this tiny line to the last finished path. + else if (!multi_path.paths.empty()) + multi_path.paths.back().polyline.points.back() = line.b; // Connect this tiny line to the last finished path. // If any of the above isn't satisfied, then remove this tiny line. continue; @@ -103,40 +103,38 @@ ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_poly path.polyline.append(line.b); } else { // we need to initialize a new line - paths.emplace_back(std::move(path)); + multi_path.paths.emplace_back(std::move(path)); path = ExtrusionPath(role); -- i; } } } if (path.polyline.is_valid()) - paths.emplace_back(std::move(path)); - return paths; + multi_path.paths.emplace_back(std::move(path)); + return multi_path; } -static void variable_width(const ThickPolylines& polylines, ExtrusionRole role, const Flow &flow, std::vector &out) +static void variable_width(const ThickPolylines &polylines, ExtrusionRole role, const Flow &flow, std::vector &out) { - // This value determines granularity of adaptive width, as G-code does not allow - // variable extrusion within a single move; this value shall only affect the amount - // of segments, and any pruning shall be performed before we apply this tolerance. - const auto tolerance = float(scale_(0.05)); - for (const ThickPolyline &p : polylines) { - ExtrusionPaths paths = thick_polyline_to_extrusion_paths(p, role, flow, tolerance, tolerance); - // Append paths to collection. - if (!paths.empty()) { - for (auto it = std::next(paths.begin()); it != paths.end(); ++it) { + // This value determines granularity of adaptive width, as G-code does not allow + // variable extrusion within a single move; this value shall only affect the amount + // of segments, and any pruning shall be performed before we apply this tolerance. + const auto tolerance = float(scale_(0.05)); + for (const ThickPolyline &p : polylines) { + ExtrusionMultiPath multi_path = thick_polyline_to_multi_path(p, role, flow, tolerance, tolerance); + // Append paths to collection. + if (!multi_path.paths.empty()) { + for (auto it = std::next(multi_path.paths.begin()); it != multi_path.paths.end(); ++it) { assert(it->polyline.points.size() >= 2); assert(std::prev(it)->polyline.last_point() == it->polyline.first_point()); } - if (paths.front().first_point() == paths.back().last_point()) { - out.emplace_back(new ExtrusionLoop(std::move(paths))); - } else { - for (ExtrusionPath &path : paths) - out.emplace_back(new ExtrusionPath(std::move(path))); - } - } - } + if (multi_path.paths.front().first_point() == multi_path.paths.back().last_point()) + out.emplace_back(new ExtrusionLoop(std::move(multi_path.paths))); + else + out.emplace_back(new ExtrusionMultiPath(std::move(multi_path))); + } + } } // Hierarchy of perimeters. @@ -534,10 +532,35 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator &p else extrusion_loop.make_clockwise(); + for (auto it = std::next(extrusion_loop.paths.begin()); it != extrusion_loop.paths.end(); ++it) { + assert(it->polyline.points.size() >= 2); + assert(std::prev(it)->polyline.last_point() == it->polyline.first_point()); + } + assert(extrusion_loop.paths.front().first_point() == extrusion_loop.paths.back().last_point()); + extrusion_coll.append(std::move(extrusion_loop)); - } else - for (ExtrusionPath &path : paths) - extrusion_coll.append(ExtrusionPath(std::move(path))); + } else { + // Because we are processing one ExtrusionLine all ExtrusionPaths should form one connected path. + // But there is possibility that due to numerical issue there is poss + assert([&paths = std::as_const(paths)]() -> bool { + for (auto it = std::next(paths.begin()); it != paths.end(); ++it) + if (std::prev(it)->polyline.last_point() != it->polyline.first_point()) + return false; + return true; + }()); + ExtrusionMultiPath multi_path; + multi_path.paths.emplace_back(std::move(paths.front())); + + for (auto it_path = std::next(paths.begin()); it_path != paths.end(); ++it_path) { + if (multi_path.paths.back().last_point() != it_path->first_point()) { + extrusion_coll.append(ExtrusionMultiPath(std::move(multi_path))); + multi_path = ExtrusionMultiPath(); + } + multi_path.paths.emplace_back(std::move(*it_path)); + } + + extrusion_coll.append(ExtrusionMultiPath(std::move(multi_path))); + } } } diff --git a/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp index 2e478e107..ecf09c593 100644 --- a/src/libslic3r/PerimeterGenerator.hpp +++ b/src/libslic3r/PerimeterGenerator.hpp @@ -72,7 +72,7 @@ private: Polygons m_lower_slices_polygons; }; -ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, float tolerance, float merge_tolerance); +ExtrusionMultiPath thick_polyline_to_multi_path(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, float tolerance, float merge_tolerance); } diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index e84ddc503..109c949cd 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -40,11 +40,11 @@ #include #include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include #include diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index ee318d3ae..da558e438 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -142,9 +142,9 @@ public: 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)) {} - Point(double x, double y) : Vec2crd(coord_t(lrint(x)), coord_t(lrint(y))) {} + Point(double x, double y) : Vec2crd(coord_t(std::round(x)), coord_t(std::round(y))) {} Point(const Point &rhs) { *this = rhs; } - explicit Point(const Vec2d& rhs) : Vec2crd(coord_t(lrint(rhs.x())), coord_t(lrint(rhs.y()))) {} + explicit Point(const Vec2d& rhs) : Vec2crd(coord_t(std::round(rhs.x())), coord_t(std::round(rhs.y()))) {} // This constructor allows you to construct Point from Eigen expressions template Point(const Eigen::MatrixBase &other) : Vec2crd(other) {} @@ -282,7 +282,7 @@ namespace int128 { // To be used by std::unordered_map, std::unordered_multimap and friends. struct PointHash { - size_t operator()(const Vec2crd &pt) const { + size_t operator()(const Vec2crd &pt) const noexcept { return coord_t((89 * 31 + int64_t(pt.x())) * 31 + pt.y()); } }; diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index 09f1c393d..bf0abd4fa 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -90,28 +90,24 @@ void Polygon::douglas_peucker(double tolerance) // 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 &p) const { // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html bool result = false; Points::const_iterator i = this->points.begin(); Points::const_iterator j = this->points.end() - 1; - for (; i != this->points.end(); j = i++) { - //FIXME this test is not numerically robust. Particularly, it does not handle horizontal segments at y == point(1) well. - // Does the ray with y == point(1) intersect this line segment? + for (; i != this->points.end(); j = i ++) + if (i->y() > p.y() != j->y() > p.y()) #if 1 - if ( (((*i)(1) > point(1)) != ((*j)(1) > point(1))) - && ((double)point(0) < (double)((*j)(0) - (*i)(0)) * (double)(point(1) - (*i)(1)) / (double)((*j)(1) - (*i)(1)) + (double)(*i)(0)) ) - result = !result; + if (Vec2d v = (*j - *i).cast(); + // p.x() is below the line + p.x() - i->x() < double(p.y() - i->y()) * v.x() / v.y()) #else - if (((*i)(1) > point(1)) != ((*j)(1) > point(1))) { // Orientation predicated relative to i-th point. - double orient = (double)(point(0) - (*i)(0)) * (double)((*j)(1) - (*i)(1)) - (double)(point(1) - (*i)(1)) * (double)((*j)(0) - (*i)(0)); - if (((*i)(1) > (*j)(1)) ? (orient > 0.) : (orient < 0.)) - result = !result; - } + if (double orient = (double)(p.x() - i->x()) * (double)(j->y() - i->y()) - (double)(p.y() - i->y()) * (double)(j->x() - i->x()); + (i->y() > j->y()) ? (orient > 0.) : (orient < 0.)) #endif - } + result = !result; return result; } @@ -520,4 +516,35 @@ void remove_collinear(Polygons &polys) remove_collinear(poly); } +bool contains(const Polygons &polygons, const Point &p, bool border_result) +{ + int poly_count_inside = 0; + for (const Polygon &poly : polygons) { + const int is_inside_this_poly = ClipperLib::PointInPolygon(p, poly.points); + if (is_inside_this_poly == -1) + return border_result; + poly_count_inside += is_inside_this_poly; + } + return (poly_count_inside % 2) == 1; +} + +Polygon make_circle(double radius, double error) +{ + double angle = 2. * acos(1. - error / radius); + size_t num_segments = size_t(ceil(2. * M_PI / angle)); + return make_circle_num_segments(radius, num_segments); +} + +Polygon make_circle_num_segments(double radius, size_t num_segments) +{ + Polygon out; + out.points.reserve(num_segments); + double angle_inc = 2.0 * M_PI / num_segments; + for (size_t i = 0; i < num_segments; ++ i) { + const double angle = angle_inc * i; + out.points.emplace_back(coord_t(cos(angle) * radius), coord_t(sin(angle) * radius)); + } + return out; +} + } diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index 77f3020be..12d457c37 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -250,6 +250,12 @@ inline Polygons to_polygons(std::vector &&paths) return out; } +// Returns true if inside. Returns border_result if on boundary. +bool contains(const Polygons& polygons, const Point& p, bool border_result = true); + +Polygon make_circle(double radius, double error); +Polygon make_circle_num_segments(double radius, size_t num_segments); + } // Slic3r // start Boost diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index 5282f6c77..547664d4f 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -61,6 +61,9 @@ public: } } + Point& operator[](Points::size_type idx) { return this->points[idx]; } + const Point& operator[](Points::size_type idx) const { return this->points[idx]; } + const Point& last_point() const override { return this->points.back(); } const Point& leftmost_point() const; Lines lines() const override; diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 72cf59527..f740c874c 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -748,7 +748,7 @@ std::pair PresetCollection::load_external_preset( { // Load the preset over a default preset, so that the missing fields are filled in from the default preset. DynamicPrintConfig cfg(this->default_preset_for(combined_config).config); - const auto &keys = cfg.keys(); + t_config_option_keys keys = std::move(cfg.keys()); cfg.apply_only(combined_config, keys, true); std::string &inherits = Preset::inherits(cfg); if (select == LoadAndSelect::Never) { @@ -792,6 +792,13 @@ std::pair PresetCollection::load_external_preset( // the differences will be shown in the preset editor against the referenced profile. this->select_preset(it - m_presets.begin()); // The source config may contain keys from many possible preset types. Just copy those that relate to this preset. + + // Following keys are not used neither by the UI nor by the slicing core, therefore they are not important + // Erase them from config appl to avoid redundant "dirty" parameter in loaded preset. + for (const char* key : { "print_settings_id", "filament_settings_id", "sla_print_settings_id", "sla_material_settings_id", "printer_settings_id", + "printer_model", "printer_variant", "default_print_profile", "default_filament_profile", "default_sla_print_profile", "default_sla_material_profile" }) + keys.erase(std::remove(keys.begin(), keys.end(), key), keys.end()); + this->get_edited_preset().config.apply_only(combined_config, keys, true); this->update_dirty(); // Don't save the newly loaded project as a "saved into project" state. @@ -1869,13 +1876,23 @@ bool PhysicalPrinterCollection::delete_preset_from_printers( const std::string& return true; } +void PhysicalPrinterCollection::rename_preset_in_printers(const std::string& old_preset_name, const std::string& new_preset_name) +{ + for (PhysicalPrinter& printer : m_printers) + if (printer.delete_preset(old_preset_name)) { + printer.add_preset(new_preset_name); + printer.update_preset_names_in_config(); + printer.save(); + } +} + // Get list of printers which have more than one preset and "preset_names" preset is one of them -std::vector PhysicalPrinterCollection::get_printers_with_preset(const std::string& preset_name) +std::vector PhysicalPrinterCollection::get_printers_with_preset(const std::string& preset_name, bool respect_only_preset /*= true*/) { std::vector printers; for (auto printer : m_printers) { - if (printer.preset_names.size() == 1) + if (!respect_only_preset && printer.preset_names.size() == 1) continue; if (printer.preset_names.find(preset_name) != printer.preset_names.end()) printers.emplace_back(printer.name); diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index 58ab57cfd..1e2ab9e24 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -734,9 +734,9 @@ public: // If there is last preset for the printer and first_check== false, then delete this printer // returns true if all presets were deleted successfully. bool delete_preset_from_printers(const std::string& preset_name); - + void rename_preset_in_printers(const std::string& old_name, const std::string& new_name); // Get list of printers which have more than one preset and "preset_names" preset is one of them - std::vector get_printers_with_preset( const std::string &preset_name); + std::vector get_printers_with_preset( const std::string &preset_name, bool respect_only_preset = true); // Get list of printers which has only "preset_names" preset std::vector get_printers_with_only_preset( const std::string &preset_name); diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index 2a5b14c7e..c3792779c 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -626,7 +626,8 @@ PrintObjectRegions::BoundingBox find_modifier_volume_extents(const PrintObjectRe const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[parent_region_id]; const PrintObjectRegions::BoundingBox *parent_extents = find_volume_extents(layer_range, *parent_region.model_volume); assert(parent_extents); - out.extend(*parent_extents); + out.clamp(*parent_extents); + assert(! out.isEmpty()); if (parent_region.model_volume->is_model_part()) break; parent_region_id = parent_region.parent; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 2a51a2855..efb92d3d3 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -137,7 +137,8 @@ CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SupportMaterialPattern) static const t_config_enum_values s_keys_map_SupportMaterialStyle { { "grid", smsGrid }, - { "snug", smsSnug } + { "snug", smsSnug }, + { "tree", smsTree } }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SupportMaterialStyle) @@ -1050,6 +1051,7 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("HIPS"); def->enum_values.push_back("EDGE"); def->enum_values.push_back("NGEN"); + def->enum_values.push_back("PA"); def->enum_values.push_back("NYLON"); def->enum_values.push_back("PVA"); def->enum_values.push_back("PC"); @@ -2776,8 +2778,10 @@ void PrintConfigDef::init_fff_params() def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); def->enum_values.push_back("grid"); def->enum_values.push_back("snug"); + def->enum_values.push_back("tree"); def->enum_labels.push_back(L("Grid")); def->enum_labels.push_back(L("Snug")); + def->enum_labels.push_back(L("Tree")); def->mode = comAdvanced; def->set_default_value(new ConfigOptionEnum(smsGrid)); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index f6bb1572b..5cc5cc59c 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -60,7 +60,7 @@ enum InfillPattern : int { ipRectilinear, ipMonotonic, ipAlignedRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipSupportBase, ipLightning, -ipCount, + ipCount, }; enum class IroningType { @@ -85,7 +85,7 @@ enum SupportMaterialPattern { }; enum SupportMaterialStyle { - smsGrid, smsSnug, + smsGrid, smsSnug, smsTree, }; enum SupportMaterialInterfacePattern { diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index c96e23fe0..71ec15e07 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -8,6 +8,7 @@ #include "Layer.hpp" #include "MutablePolygon.hpp" #include "SupportMaterial.hpp" +#include "TreeSupport.hpp" #include "Surface.hpp" #include "Slicing.hpp" #include "Tesselate.hpp" @@ -28,8 +29,6 @@ #include -#include - using namespace std::literals; //! macro used to mark string used at localization, @@ -689,12 +688,21 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "bottom_fill_pattern" || opt_key == "external_fill_link_max_length" || opt_key == "fill_angle" - || opt_key == "fill_pattern" || opt_key == "infill_anchor" || opt_key == "infill_anchor_max" || opt_key == "top_infill_extrusion_width" || opt_key == "first_layer_extrusion_width") { steps.emplace_back(posInfill); + } else if (opt_key == "fill_pattern") { + steps.emplace_back(posInfill); + + const auto *old_fill_pattern = old_config.option>(opt_key); + const auto *new_fill_pattern = new_config.option>(opt_key); + assert(old_fill_pattern && new_fill_pattern); + // We need to recalculate infill surfaces when infill_only_where_needed is enabled, and we are switching from + // the Lightning infill to another infill or vice versa. + if (m_config.infill_only_where_needed && (new_fill_pattern->value == ipLightning || old_fill_pattern->value == ipLightning)) + steps.emplace_back(posPrepareInfill); } else if (opt_key == "fill_density") { // One likely wants to reslice only when switching between zero infill to simulate boolean difference (subtracting volumes), // normal infill and 100% (solid) infill. @@ -1098,8 +1106,6 @@ void PrintObject::process_external_surfaces() void PrintObject::discover_vertical_shells() { - PROFILE_FUNC(); - BOOST_LOG_TRIVIAL(info) << "Discovering vertical shells..." << log_memory_info(); struct DiscoverVerticalShellsCacheEntry @@ -1210,8 +1216,6 @@ void PrintObject::discover_vertical_shells() } for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { - PROFILE_BLOCK(discover_vertical_shells_region); - const PrintRegion ®ion = this->printing_region(region_id); if (! region.config().ensure_vertical_shell_thickness.value) // This region will be handled by discover_horizontal_shells(). @@ -1261,7 +1265,6 @@ void PrintObject::discover_vertical_shells() (const tbb::blocked_range& range) { // printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end()); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - PROFILE_BLOCK(discover_vertical_shells_region_layer); m_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING static size_t debug_idx = 0; @@ -1286,88 +1289,82 @@ void PrintObject::discover_vertical_shells() ExPolygons shell_ex; #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ float min_perimeter_infill_spacing = float(infill_line_spacing) * 1.05f; - { - PROFILE_BLOCK(discover_vertical_shells_region_layer_collect); #if 0 // #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - { - Slic3r::SVG svg_cummulative(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d.svg", debug_idx), this->bounding_box()); - for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n) { - if (n < 0 || n >= (int)m_layers.size()) - continue; - ExPolygons &expolys = m_layers[n]->perimeter_expolygons; - for (size_t i = 0; i < expolys.size(); ++ i) { - Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d-layer%d-expoly%d.svg", debug_idx, n, i), get_extents(expolys[i])); - svg.draw(expolys[i]); - svg.draw_outline(expolys[i].contour, "black", scale_(0.05)); - svg.draw_outline(expolys[i].holes, "blue", scale_(0.05)); - svg.Close(); + { + Slic3r::SVG svg_cummulative(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d.svg", debug_idx), this->bounding_box()); + for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n) { + if (n < 0 || n >= (int)m_layers.size()) + continue; + ExPolygons &expolys = m_layers[n]->perimeter_expolygons; + for (size_t i = 0; i < expolys.size(); ++ i) { + Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d-layer%d-expoly%d.svg", debug_idx, n, i), get_extents(expolys[i])); + svg.draw(expolys[i]); + svg.draw_outline(expolys[i].contour, "black", scale_(0.05)); + svg.draw_outline(expolys[i].holes, "blue", scale_(0.05)); + svg.Close(); - svg_cummulative.draw(expolys[i]); - svg_cummulative.draw_outline(expolys[i].contour, "black", scale_(0.05)); - svg_cummulative.draw_outline(expolys[i].holes, "blue", scale_(0.05)); - } + svg_cummulative.draw(expolys[i]); + svg_cummulative.draw_outline(expolys[i].contour, "black", scale_(0.05)); + svg_cummulative.draw_outline(expolys[i].holes, "blue", scale_(0.05)); } } + } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - polygons_append(holes, cache_top_botom_regions[idx_layer].holes); - if (int n_top_layers = region_config.top_solid_layers.value; n_top_layers > 0) { - // Gather top regions projected to this layer. - coordf_t print_z = layer->print_z; - for (int i = int(idx_layer) + 1; - i < int(cache_top_botom_regions.size()) && - (i < int(idx_layer) + n_top_layers || - m_layers[i]->print_z - print_z < region_config.top_solid_min_thickness - EPSILON); - ++ i) { - const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; - if (! holes.empty()) - holes = intersection(holes, cache.holes); - if (! cache.top_surfaces.empty()) { - polygons_append(shell, cache.top_surfaces); - // Running the union_ using the Clipper library piece by piece is cheaper - // than running the union_ all at once. - shell = union_(shell); - } + polygons_append(holes, cache_top_botom_regions[idx_layer].holes); + if (int n_top_layers = region_config.top_solid_layers.value; n_top_layers > 0) { + // Gather top regions projected to this layer. + coordf_t print_z = layer->print_z; + for (int i = int(idx_layer) + 1; + i < int(cache_top_botom_regions.size()) && + (i < int(idx_layer) + n_top_layers || + m_layers[i]->print_z - print_z < region_config.top_solid_min_thickness - EPSILON); + ++ i) { + const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; + if (! holes.empty()) + holes = intersection(holes, cache.holes); + if (! cache.top_surfaces.empty()) { + polygons_append(shell, cache.top_surfaces); + // Running the union_ using the Clipper library piece by piece is cheaper + // than running the union_ all at once. + shell = union_(shell); } } - if (int n_bottom_layers = region_config.bottom_solid_layers.value; n_bottom_layers > 0) { - // Gather bottom regions projected to this layer. - coordf_t bottom_z = layer->bottom_z(); - for (int i = int(idx_layer) - 1; - i >= 0 && - (i > int(idx_layer) - n_bottom_layers || - bottom_z - m_layers[i]->bottom_z() < region_config.bottom_solid_min_thickness - EPSILON); - -- i) { - const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; - if (! holes.empty()) - holes = intersection(holes, cache.holes); - if (! cache.bottom_surfaces.empty()) { - polygons_append(shell, cache.bottom_surfaces); - // Running the union_ using the Clipper library piece by piece is cheaper - // than running the union_ all at once. - shell = union_(shell); - } - } + } + if (int n_bottom_layers = region_config.bottom_solid_layers.value; n_bottom_layers > 0) { + // Gather bottom regions projected to this layer. + coordf_t bottom_z = layer->bottom_z(); + for (int i = int(idx_layer) - 1; + i >= 0 && + (i > int(idx_layer) - n_bottom_layers || + bottom_z - m_layers[i]->bottom_z() < region_config.bottom_solid_min_thickness - EPSILON); + -- i) { + const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; + if (! holes.empty()) + holes = intersection(holes, cache.holes); + if (! cache.bottom_surfaces.empty()) { + polygons_append(shell, cache.bottom_surfaces); + // Running the union_ using the Clipper library piece by piece is cheaper + // than running the union_ all at once. + shell = union_(shell); + } } + } #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - { - Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell)); - svg.draw(shell); - svg.draw_outline(shell, "black", scale_(0.05)); - svg.Close(); - } + { + Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell)); + svg.draw(shell); + svg.draw_outline(shell, "black", scale_(0.05)); + svg.Close(); + } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #if 0 - { - PROFILE_BLOCK(discover_vertical_shells_region_layer_shell_); - // shell = union_(shell, true); - shell = union_(shell, false); - } +// shell = union_(shell, true); + shell = union_(shell, false); #endif #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - shell_ex = union_safety_offset_ex(shell); + shell_ex = union_safety_offset_ex(shell); #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - } //if (shell.empty()) // continue; @@ -1494,62 +1491,66 @@ void PrintObject::discover_vertical_shells() } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ } // for each region - - // Write the profiler measurements to file -// PROFILE_UPDATE(); -// PROFILE_OUTPUT(debug_out_path("discover_vertical_shells-profile.txt").c_str()); } -/* This method applies bridge flow to the first internal solid layer above - sparse infill */ +// This method applies bridge flow to the first internal solid layer above sparse infill. void PrintObject::bridge_over_infill() { BOOST_LOG_TRIVIAL(info) << "Bridge over infill..." << log_memory_info(); - for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { - const PrintRegion ®ion = this->printing_region(region_id); - - // skip bridging in case there are no voids - if (region.config().fill_density.value == 100) - continue; + std::vector sparse_infill_regions; + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) + if (const PrintRegion ®ion = this->printing_region(region_id); region.config().fill_density.value < 100) + sparse_infill_regions.emplace_back(region_id); + if (this->layer_count() < 2 || sparse_infill_regions.empty()) + return; - for (LayerPtrs::iterator layer_it = m_layers.begin(); layer_it != m_layers.end(); ++ layer_it) { - // skip first layer - if (layer_it == m_layers.begin()) - continue; - - Layer *layer = *layer_it; + // Collect sum of all internal (sparse infill) regions, because + // 1) layerm->fill_surfaces.will be modified in parallel. + // 2) the parallel loop works on a sum of surfaces over regions anyways, thus collecting the sparse infill surfaces + // up front is an optimization. + std::vector internals; + internals.reserve(this->layer_count()); + for (Layer *layer : m_layers) { + Polygons sum; + for (const LayerRegion *layerm : layer->m_regions) + layerm->fill_surfaces.filter_by_type(stInternal, &sum); + internals.emplace_back(std::move(sum)); + } + + // Process all regions and layers in parallel. + tbb::parallel_for(tbb::blocked_range(0, sparse_infill_regions.size() * (this->layer_count() - 1), sparse_infill_regions.size()), + [this, &sparse_infill_regions, &internals] + (const tbb::blocked_range &range) { + for (size_t task_id = range.begin(); task_id != range.end(); ++ task_id) { + const size_t layer_id = (task_id / sparse_infill_regions.size()) + 1; + const size_t region_id = sparse_infill_regions[task_id % sparse_infill_regions.size()]; + Layer *layer = this->get_layer(layer_id); LayerRegion *layerm = layer->m_regions[region_id]; Flow bridge_flow = layerm->bridging_flow(frSolidInfill); - // extract the stInternalSolid surfaces that might be transformed into bridges - Polygons internal_solid; - layerm->fill_surfaces.filter_by_type(stInternalSolid, &internal_solid); - + // Extract the stInternalSolid surfaces that might be transformed into bridges. + ExPolygons internal_solid; + layerm->fill_surfaces.remove_type(stInternalSolid, &internal_solid); + if (internal_solid.empty()) + // No internal solid -> no new bridges for this layer region. + continue; + // check whether the lower area is deep enough for absorbing the extra flow // (for obvious physical reasons but also for preventing the bridge extrudates // from overflowing in 3D preview) ExPolygons to_bridge; { - Polygons to_bridge_pp = internal_solid; - - // iterate through lower layers spanned by bridge_flow + Polygons to_bridge_pp = to_polygons(internal_solid); + // Iterate through lower layers spanned by bridge_flow. double bottom_z = layer->print_z - bridge_flow.height() - EPSILON; - for (int i = int(layer_it - m_layers.begin()) - 1; i >= 0; --i) { - const Layer* lower_layer = m_layers[i]; - - // stop iterating if layer is lower than bottom_z - if (lower_layer->print_z < bottom_z) break; - - // iterate through regions and collect internal surfaces - Polygons lower_internal; - for (LayerRegion *lower_layerm : lower_layer->m_regions) - lower_layerm->fill_surfaces.filter_by_type(stInternal, &lower_internal); - - // intersect such lower internal surfaces with the candidate solid surfaces - to_bridge_pp = intersection(to_bridge_pp, lower_internal); + for (auto i = int(layer_id) - 1; i >= 0; -- i) { + // Stop iterating if layer is lower than bottom_z. + if (m_layers[i]->print_z < bottom_z) + break; + // Intersect lower sparse infills with the candidate solid surfaces. + to_bridge_pp = intersection(to_bridge_pp, internals[i]); } - // there's no point in bridging too thin/short regions //FIXME Vojtech: The offset2 function is not a geometric offset, // therefore it may create 1) gaps, and 2) sharp corners, which are outside the original contour. @@ -1559,12 +1560,16 @@ void PrintObject::bridge_over_infill() to_bridge_pp = opening(to_bridge_pp, min_width); } - if (to_bridge_pp.empty()) continue; - + if (to_bridge_pp.empty()) { + // Restore internal_solid surfaces. + for (ExPolygon &ex : internal_solid) + layerm->fill_surfaces.surfaces.push_back(Surface(stInternalSolid, std::move(ex))); + continue; + } // convert into ExPolygons to_bridge = union_ex(to_bridge_pp); } - + #ifdef SLIC3R_DEBUG printf("Bridging %zu internal areas at layer %zu\n", to_bridge.size(), layer->id()); #endif @@ -1573,11 +1578,10 @@ void PrintObject::bridge_over_infill() ExPolygons not_to_bridge = diff_ex(internal_solid, to_bridge, ApplySafetyOffset::Yes); to_bridge = intersection_ex(to_bridge, internal_solid, ApplySafetyOffset::Yes); // build the new collection of fill_surfaces - layerm->fill_surfaces.remove_type(stInternalSolid); for (ExPolygon &ex : to_bridge) - layerm->fill_surfaces.surfaces.push_back(Surface(stInternalBridge, ex)); + layerm->fill_surfaces.surfaces.push_back(Surface(stInternalBridge, std::move(ex))); for (ExPolygon &ex : not_to_bridge) - layerm->fill_surfaces.surfaces.push_back(Surface(stInternalSolid, ex)); + layerm->fill_surfaces.surfaces.push_back(Surface(stInternalSolid, std::move(ex))); /* # exclude infill from the layers below if needed # see discussion at https://github.com/alexrj/Slic3r/issues/240 @@ -1611,14 +1615,13 @@ void PrintObject::bridge_over_infill() } } */ - #ifdef SLIC3R_DEBUG_SLICE_PROCESSING layerm->export_region_slices_to_svg_debug("7_bridge_over_infill"); layerm->export_region_fill_surfaces_to_svg_debug("7_bridge_over_infill"); #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ m_print->throw_if_canceled(); } - } + }); } static void clamp_exturder_to_default(ConfigOptionInt &opt, size_t num_extruders) @@ -1795,7 +1798,14 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c // fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries. void PrintObject::clip_fill_surfaces() { - if (! m_config.infill_only_where_needed.value) + bool has_lightning_infill = false; + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) + if (const PrintRegionConfig &config = this->printing_region(region_id).config(); config.fill_density > 0 && config.fill_pattern == ipLightning) + has_lightning_infill = true; + + // For Lightning infill, infill_only_where_needed is ignored because both + // do a similar thing, and their combination doesn't make much sense. + if (! m_config.infill_only_where_needed.value || has_lightning_infill) return; bool has_infill = false; for (size_t i = 0; i < this->num_printing_regions(); ++ i) @@ -2187,8 +2197,13 @@ void PrintObject::combine_infill() void PrintObject::_generate_support_material() { - PrintObjectSupportMaterial support_material(this, m_slicing_params); - support_material.generate(*this); + if (m_config.support_material_style == smsTree) { + TreeSupport tree_support; + tree_support.generateSupportAreas(*this); + } else { + PrintObjectSupportMaterial support_material(this, m_slicing_params); + support_material.generate(*this); + } } static void project_triangles_to_slabs(ConstLayerPtrsAdaptor layers, const indexed_triangle_set &custom_facets, const Transform3f &tr, bool seam, std::vector &out) diff --git a/src/libslic3r/SLA/Pad.cpp b/src/libslic3r/SLA/Pad.cpp index b04c3f219..ec9e216f5 100644 --- a/src/libslic3r/SLA/Pad.cpp +++ b/src/libslic3r/SLA/Pad.cpp @@ -204,7 +204,7 @@ public: void add(const ExPolygon &ep) { m_polys.emplace_back(ep); - m_index.insert(BoundingBox{ep}, unsigned(m_index.size())); + m_index.insert(get_extents(ep), unsigned(m_index.size())); } // Check an arbitrary polygon for intersection with the indexed polygons diff --git a/src/libslic3r/SVG.cpp b/src/libslic3r/SVG.cpp index d68301e74..03c8823d3 100644 --- a/src/libslic3r/SVG.cpp +++ b/src/libslic3r/SVG.cpp @@ -88,10 +88,8 @@ void SVG::draw(const ExPolygon &expolygon, std::string fill, const float fill_op this->fill = fill; std::string d; - Polygons pp = expolygon; - for (Polygons::const_iterator p = pp.begin(); p != pp.end(); ++p) { - d += this->get_path_d(*p, true) + " "; - } + for (const Polygon &p : to_polygons(expolygon)) + d += this->get_path_d(p, true) + " "; this->path(d, true, 0, fill_opacity); } @@ -359,7 +357,7 @@ 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.draw(to_points(expoly), exp_with_attr.second.color_points, exp_with_attr.second.radius_points); // Export legend. // 1st row diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index e2a1f0cf2..e8ee782f4 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -8,6 +8,8 @@ #include "Point.hpp" #include "MutablePolygon.hpp" +#include + #include #include #include @@ -61,19 +63,19 @@ namespace Slic3r { //#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5 #define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0. -#ifdef SLIC3R_DEBUG -const char* support_surface_type_to_color_name(const PrintObjectSupportMaterial::SupporLayerType surface_type) +#if 1 //#ifdef SLIC3R_DEBUG +const char* support_surface_type_to_color_name(const SupporLayerType surface_type) { switch (surface_type) { - case PrintObjectSupportMaterial::sltTopContact: return "rgb(255,0,0)"; // "red"; - case PrintObjectSupportMaterial::sltTopInterface: return "rgb(0,255,0)"; // "green"; - case PrintObjectSupportMaterial::sltBase: return "rgb(0,0,255)"; // "blue"; - case PrintObjectSupportMaterial::sltBottomInterface:return "rgb(255,255,128)"; // yellow - case PrintObjectSupportMaterial::sltBottomContact: return "rgb(255,0,255)"; // magenta - case PrintObjectSupportMaterial::sltRaftInterface: return "rgb(0,255,255)"; - case PrintObjectSupportMaterial::sltRaftBase: return "rgb(128,128,128)"; - case PrintObjectSupportMaterial::sltUnknown: return "rgb(128,0,0)"; // maroon - default: return "rgb(64,64,64)"; + case SupporLayerType::TopContact: return "rgb(255,0,0)"; // "red"; + case SupporLayerType::TopInterface: return "rgb(0,255,0)"; // "green"; + case SupporLayerType::Base: return "rgb(0,0,255)"; // "blue"; + case SupporLayerType::BottomInterface:return "rgb(255,255,128)"; // yellow + case SupporLayerType::BottomContact: return "rgb(255,0,255)"; // magenta + case SupporLayerType::RaftInterface: return "rgb(0,255,255)"; + case SupporLayerType::RaftBase: return "rgb(128,128,128)"; + case SupporLayerType::Unknown: return "rgb(128,0,0)"; // maroon + default: return "rgb(64,64,64)"; }; } @@ -89,28 +91,28 @@ void export_support_surface_type_legend_to_svg(SVG &svg, const Point &pos) coord_t pos_x = pos_x0; coord_t pos_y = pos(1) + scale_(1.5); coord_t step_x = scale_(10.); - svg.draw_legend(Point(pos_x, pos_y), "top contact" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltTopContact)); + svg.draw_legend(Point(pos_x, pos_y), "top contact" , support_surface_type_to_color_name(SupporLayerType::TopContact)); pos_x += step_x; - svg.draw_legend(Point(pos_x, pos_y), "top iface" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltTopInterface)); + svg.draw_legend(Point(pos_x, pos_y), "top iface" , support_surface_type_to_color_name(SupporLayerType::TopInterface)); pos_x += step_x; - svg.draw_legend(Point(pos_x, pos_y), "base" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltBase)); + svg.draw_legend(Point(pos_x, pos_y), "base" , support_surface_type_to_color_name(SupporLayerType::Base)); pos_x += step_x; - svg.draw_legend(Point(pos_x, pos_y), "bottom iface" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltBottomInterface)); + svg.draw_legend(Point(pos_x, pos_y), "bottom iface" , support_surface_type_to_color_name(SupporLayerType::BottomInterface)); pos_x += step_x; - svg.draw_legend(Point(pos_x, pos_y), "bottom contact" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltBottomContact)); + svg.draw_legend(Point(pos_x, pos_y), "bottom contact" , support_surface_type_to_color_name(SupporLayerType::BottomContact)); // 2nd row pos_x = pos_x0; pos_y = pos(1)+scale_(2.8); - svg.draw_legend(Point(pos_x, pos_y), "raft interface" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltRaftInterface)); + svg.draw_legend(Point(pos_x, pos_y), "raft interface" , support_surface_type_to_color_name(SupporLayerType::RaftInterface)); pos_x += step_x; - svg.draw_legend(Point(pos_x, pos_y), "raft base" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltRaftBase)); + svg.draw_legend(Point(pos_x, pos_y), "raft base" , support_surface_type_to_color_name(SupporLayerType::RaftBase)); pos_x += step_x; - svg.draw_legend(Point(pos_x, pos_y), "unknown" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltUnknown)); + svg.draw_legend(Point(pos_x, pos_y), "unknown" , support_surface_type_to_color_name(SupporLayerType::Unknown)); pos_x += step_x; - svg.draw_legend(Point(pos_x, pos_y), "intermediate" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltIntermediate)); + svg.draw_legend(Point(pos_x, pos_y), "intermediate" , support_surface_type_to_color_name(SupporLayerType::Intermediate)); } -void export_print_z_polygons_to_svg(const char *path, PrintObjectSupportMaterial::MyLayer ** const layers, size_t n_layers) +void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer ** const layers, int n_layers) { BoundingBox bbox; for (int i = 0; i < n_layers; ++ i) @@ -129,10 +131,10 @@ void export_print_z_polygons_to_svg(const char *path, PrintObjectSupportMaterial } void export_print_z_polygons_and_extrusions_to_svg( - const char *path, - PrintObjectSupportMaterial::MyLayer ** const layers, - size_t n_layers, - SupportLayer &support_layer) + const char *path, + SupportGeneratorLayer ** const layers, + int n_layers, + SupportLayer &support_layer) { BoundingBox bbox; for (int i = 0; i < n_layers; ++ i) @@ -321,112 +323,119 @@ static Polygons contours_simplified(const Vec2i &grid_size, const double pixel_s } #endif // SUPPORT_USE_AGG_RASTERIZER -PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params) : - m_object (object), - m_print_config (&object->print()->config()), - m_object_config (&object->config()), - m_slicing_params (slicing_params) +SupportParameters::SupportParameters(const PrintObject &object) { - m_support_params.first_layer_flow = support_material_1st_layer_flow(object, float(slicing_params.first_print_layer_height)); - m_support_params.support_material_flow = support_material_flow(object, float(slicing_params.layer_height)); - m_support_params.support_material_interface_flow = support_material_interface_flow(object, float(slicing_params.layer_height)); - m_support_params.support_layer_height_min = 0.01; + const PrintConfig &print_config = object.print()->config(); + const PrintObjectConfig &object_config = object.config(); + const SlicingParameters &slicing_params = object.slicing_parameters(); + + this->first_layer_flow = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height)); + this->support_material_flow = Slic3r::support_material_flow(&object, float(slicing_params.layer_height)); + this->support_material_interface_flow = Slic3r::support_material_interface_flow(&object, float(slicing_params.layer_height)); // Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um. - m_support_params.support_layer_height_min = 1000000.; - for (auto lh : m_print_config->min_layer_height.values) - m_support_params.support_layer_height_min = std::min(m_support_params.support_layer_height_min, std::max(0.01, lh)); - for (auto layer : m_object->layers()) - m_support_params.support_layer_height_min = std::min(m_support_params.support_layer_height_min, std::max(0.01, layer->height)); + this->support_layer_height_min = scaled(0.01); + for (auto lh : print_config.min_layer_height.values) + this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, lh)); + for (auto layer : object.layers()) + this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, layer->height)); - if (m_object_config->support_material_interface_layers.value == 0) { + if (object_config.support_material_interface_layers.value == 0) { // No interface layers allowed, print everything with the base support pattern. - m_support_params.support_material_interface_flow = m_support_params.support_material_flow; + this->support_material_interface_flow = this->support_material_flow; } // Evaluate the XY gap between the object outer perimeters and the support structures. // Evaluate the XY gap between the object outer perimeters and the support structures. coordf_t external_perimeter_width = 0.; coordf_t bridge_flow_ratio = 0; - for (size_t region_id = 0; region_id < object->num_printing_regions(); ++ region_id) { - const PrintRegion ®ion = object->printing_region(region_id); - external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(*object, frExternalPerimeter, slicing_params.layer_height).width())); + for (size_t region_id = 0; region_id < object.num_printing_regions(); ++ region_id) { + const PrintRegion ®ion = object.printing_region(region_id); + external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(object, frExternalPerimeter, slicing_params.layer_height).width())); bridge_flow_ratio += region.config().bridge_flow_ratio; } - m_support_params.gap_xy = m_object_config->support_material_xy_spacing.get_abs_value(external_perimeter_width); - bridge_flow_ratio /= object->num_printing_regions(); + this->gap_xy = object_config.support_material_xy_spacing.get_abs_value(external_perimeter_width); + bridge_flow_ratio /= object.num_printing_regions(); - m_support_params.support_material_bottom_interface_flow = m_slicing_params.soluble_interface || ! m_object_config->thick_bridges ? - m_support_params.support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) : - Flow::bridging_flow(bridge_flow_ratio * m_support_params.support_material_interface_flow.nozzle_diameter(), m_support_params.support_material_interface_flow.nozzle_diameter()); + this->support_material_bottom_interface_flow = slicing_params.soluble_interface || ! object_config.thick_bridges ? + this->support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) : + Flow::bridging_flow(bridge_flow_ratio * this->support_material_interface_flow.nozzle_diameter(), this->support_material_interface_flow.nozzle_diameter()); - m_support_params.can_merge_support_regions = m_object_config->support_material_extruder.value == m_object_config->support_material_interface_extruder.value; - if (!m_support_params.can_merge_support_regions && (m_object_config->support_material_extruder.value == 0 || m_object_config->support_material_interface_extruder.value == 0)) { + this->can_merge_support_regions = object_config.support_material_extruder.value == object_config.support_material_interface_extruder.value; + if (!this->can_merge_support_regions && (object_config.support_material_extruder.value == 0 || object_config.support_material_interface_extruder.value == 0)) { // One of the support extruders is of "don't care" type. - auto object_extruders = m_object->object_extruders(); + auto object_extruders = object.object_extruders(); if (object_extruders.size() == 1 && - *object_extruders.begin() == std::max(m_object_config->support_material_extruder.value, m_object_config->support_material_interface_extruder.value)) + *object_extruders.begin() == std::max(object_config.support_material_extruder.value, object_config.support_material_interface_extruder.value)) // Object is printed with the same extruder as the support. - m_support_params.can_merge_support_regions = true; + this->can_merge_support_regions = true; } - m_support_params.base_angle = Geometry::deg2rad(float(m_object_config->support_material_angle.value)); - m_support_params.interface_angle = Geometry::deg2rad(float(m_object_config->support_material_angle.value + 90.)); - m_support_params.interface_spacing = m_object_config->support_material_interface_spacing.value + m_support_params.support_material_interface_flow.spacing(); - m_support_params.interface_density = std::min(1., m_support_params.support_material_interface_flow.spacing() / m_support_params.interface_spacing); - m_support_params.support_spacing = m_object_config->support_material_spacing.value + m_support_params.support_material_flow.spacing(); - m_support_params.support_density = std::min(1., m_support_params.support_material_flow.spacing() / m_support_params.support_spacing); - if (m_object_config->support_material_interface_layers.value == 0) { + this->base_angle = Geometry::deg2rad(float(object_config.support_material_angle.value)); + this->interface_angle = Geometry::deg2rad(float(object_config.support_material_angle.value + 90.)); + this->interface_spacing = object_config.support_material_interface_spacing.value + this->support_material_interface_flow.spacing(); + this->interface_density = std::min(1., this->support_material_interface_flow.spacing() / this->interface_spacing); + this->support_spacing = object_config.support_material_spacing.value + this->support_material_flow.spacing(); + this->support_density = std::min(1., this->support_material_flow.spacing() / this->support_spacing); + if (object_config.support_material_interface_layers.value == 0) { // No interface layers allowed, print everything with the base support pattern. - m_support_params.interface_spacing = m_support_params.support_spacing; - m_support_params.interface_density = m_support_params.support_density; + this->interface_spacing = this->support_spacing; + this->interface_density = this->support_density; } - SupportMaterialPattern support_pattern = m_object_config->support_material_pattern; - m_support_params.with_sheath = m_object_config->support_material_with_sheath; - m_support_params.base_fill_pattern = + SupportMaterialPattern support_pattern = object_config.support_material_pattern; + this->with_sheath = object_config.support_material_with_sheath; + this->base_fill_pattern = support_pattern == smpHoneycomb ? ipHoneycomb : - m_support_params.support_density > 0.95 || m_support_params.with_sheath ? ipRectilinear : ipSupportBase; - m_support_params.interface_fill_pattern = (m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase); - m_support_params.contact_fill_pattern = - (m_object_config->support_material_interface_pattern == smipAuto && m_slicing_params.soluble_interface) || - m_object_config->support_material_interface_pattern == smipConcentric ? + this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase; + this->interface_fill_pattern = (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase); + this->contact_fill_pattern = + (object_config.support_material_interface_pattern == smipAuto && slicing_params.soluble_interface) || + object_config.support_material_interface_pattern == smipConcentric ? ipConcentric : - (m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase); + (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase); +} + +PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params) : + m_print_config (&object->print()->config()), + m_object_config (&object->config()), + m_slicing_params (slicing_params), + m_support_params (*object) +{ } // Using the std::deque as an allocator. -inline PrintObjectSupportMaterial::MyLayer& layer_allocate( - std::deque &layer_storage, - PrintObjectSupportMaterial::SupporLayerType layer_type) +inline SupportGeneratorLayer& layer_allocate( + std::deque &layer_storage, + SupporLayerType layer_type) { - layer_storage.push_back(PrintObjectSupportMaterial::MyLayer()); + layer_storage.push_back(SupportGeneratorLayer()); layer_storage.back().layer_type = layer_type; return layer_storage.back(); } -inline PrintObjectSupportMaterial::MyLayer& layer_allocate( - std::deque &layer_storage, +inline SupportGeneratorLayer& layer_allocate( + std::deque &layer_storage, tbb::spin_mutex &layer_storage_mutex, - PrintObjectSupportMaterial::SupporLayerType layer_type) + SupporLayerType layer_type) { layer_storage_mutex.lock(); - layer_storage.push_back(PrintObjectSupportMaterial::MyLayer()); - PrintObjectSupportMaterial::MyLayer *layer_new = &layer_storage.back(); + layer_storage.push_back(SupportGeneratorLayer()); + SupportGeneratorLayer *layer_new = &layer_storage.back(); layer_storage_mutex.unlock(); layer_new->layer_type = layer_type; return *layer_new; } -inline void layers_append(PrintObjectSupportMaterial::MyLayersPtr &dst, const PrintObjectSupportMaterial::MyLayersPtr &src) +inline void layers_append(SupportGeneratorLayersPtr &dst, const SupportGeneratorLayersPtr &src) { dst.insert(dst.end(), src.begin(), src.end()); } // Support layer that is covered by some form of dense interface. -static constexpr const std::initializer_list support_types_interface { - PrintObjectSupportMaterial::sltRaftInterface, PrintObjectSupportMaterial::sltBottomContact, PrintObjectSupportMaterial::sltBottomInterface, PrintObjectSupportMaterial::sltTopContact, PrintObjectSupportMaterial::sltTopInterface +static constexpr const std::initializer_list support_types_interface { + SupporLayerType::RaftInterface, SupporLayerType::BottomContact, SupporLayerType::BottomInterface, SupporLayerType::TopContact, SupporLayerType::TopInterface }; void PrintObjectSupportMaterial::generate(PrintObject &object) @@ -439,7 +448,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // Layer instances will be allocated by std::deque and they will be kept until the end of this function call. // The layers will be referenced by various LayersPtr (of type std::vector) - MyLayerStorage layer_storage; + SupportGeneratorLayerStorage layer_storage; BOOST_LOG_TRIVIAL(info) << "Support generator - Creating top contacts"; @@ -452,7 +461,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // should the support material expose to the object in order to guarantee // that it will be effective, regardless of how it's built below. // If raft is to be generated, the 1st top_contact layer will contain the 1st object layer silhouette without holes. - MyLayersPtr top_contacts = this->top_contact_layers(object, buildplate_covered, layer_storage); + SupportGeneratorLayersPtr top_contacts = this->top_contact_layers(object, buildplate_covered, layer_storage); if (top_contacts.empty()) // Nothing is supported, no supports are generated. return; @@ -460,7 +469,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) #ifdef SLIC3R_DEBUG static int iRun = 0; iRun ++; - for (const MyLayer *layer : top_contacts) + for (const SupportGeneratorLayer *layer : top_contacts) Slic3r::SVG::export_expolygons( debug_out_path("support-top-contacts-%d-%lf.svg", iRun, layer->print_z), union_ex(layer->polygons)); @@ -473,7 +482,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // layer_support_areas contains the per object layer support areas. These per object layer support areas // may get merged and trimmed by this->generate_base_layers() if the support layers are not synchronized with object layers. std::vector layer_support_areas; - MyLayersPtr bottom_contacts = this->bottom_contact_layers_and_layer_support_areas( + SupportGeneratorLayersPtr bottom_contacts = this->bottom_contact_layers_and_layer_support_areas( object, top_contacts, buildplate_covered, layer_storage, layer_support_areas); @@ -491,13 +500,13 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // The layers may or may not be synchronized with the object layers, depending on the configuration. // For example, a single nozzle multi material printing will need to generate a waste tower, which in turn // wastes less material, if there are as little tool changes as possible. - MyLayersPtr intermediate_layers = this->raft_and_intermediate_support_layers( + SupportGeneratorLayersPtr intermediate_layers = this->raft_and_intermediate_support_layers( object, bottom_contacts, top_contacts, layer_storage); this->trim_support_layers_by_object(object, top_contacts, m_slicing_params.gap_support_object, m_slicing_params.gap_object_support, m_support_params.gap_xy); #ifdef SLIC3R_DEBUG - for (const MyLayer *layer : top_contacts) + for (const SupportGeneratorLayer *layer : top_contacts) Slic3r::SVG::export_expolygons( debug_out_path("support-top-contacts-trimmed-by-object-%d-%lf.svg", iRun, layer->print_z), union_ex(layer->polygons)); @@ -509,7 +518,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) this->generate_base_layers(object, bottom_contacts, top_contacts, intermediate_layers, layer_support_areas); #ifdef SLIC3R_DEBUG - for (MyLayersPtr::const_iterator it = intermediate_layers.begin(); it != intermediate_layers.end(); ++ it) + for (SupportGeneratorLayersPtr::const_iterator it = intermediate_layers.begin(); it != intermediate_layers.end(); ++ it) Slic3r::SVG::export_expolygons( debug_out_path("support-base-layers-%d-%lf.svg", iRun, (*it)->print_z), union_ex((*it)->polygons)); @@ -535,14 +544,14 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // If raft is to be generated, the 1st top_contact layer will contain the 1st object layer silhouette with holes filled. // There is also a 1st intermediate layer containing bases of support columns. // Inflate the bases of the support columns and create the raft base under the object. - MyLayersPtr raft_layers = this->generate_raft_base(object, top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage); + SupportGeneratorLayersPtr raft_layers = generate_raft_base(object, m_support_params, m_slicing_params, top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage); #ifdef SLIC3R_DEBUG - for (const MyLayer *l : interface_layers) + for (const SupportGeneratorLayer *l : interface_layers) Slic3r::SVG::export_expolygons( debug_out_path("support-interface-layers-%d-%lf.svg", iRun, l->print_z), union_ex(l->polygons)); - for (const MyLayer *l : base_interface_layers) + for (const SupportGeneratorLayer *l : base_interface_layers) Slic3r::SVG::export_expolygons( debug_out_path("support-base-interface-layers-%d-%lf.svg", iRun, l->print_z), union_ex(l->polygons)); @@ -565,71 +574,10 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // intermediate_layers.clear(); // interface_layers.clear(); - // Install support layers into the object. - // A support layer installed on a PrintObject has a unique print_z. - MyLayersPtr layers_sorted; - layers_sorted.reserve(raft_layers.size() + bottom_contacts.size() + top_contacts.size() + intermediate_layers.size() + interface_layers.size() + base_interface_layers.size()); - layers_append(layers_sorted, raft_layers); - layers_append(layers_sorted, bottom_contacts); - layers_append(layers_sorted, top_contacts); - layers_append(layers_sorted, intermediate_layers); - layers_append(layers_sorted, interface_layers); - layers_append(layers_sorted, base_interface_layers); - // Sort the layers lexicographically by a raising print_z and a decreasing height. - std::sort(layers_sorted.begin(), layers_sorted.end(), [](auto *l1, auto *l2) { return *l1 < *l2; }); - int layer_id = 0; - int layer_id_interface = 0; - assert(object.support_layers().empty()); - for (size_t i = 0; i < layers_sorted.size();) { - // Find the last layer with roughly the same print_z, find the minimum layer height of all. - // Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should. - size_t j = i + 1; - coordf_t zmax = layers_sorted[i]->print_z + EPSILON; - for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j) ; - // Assign an average print_z to the set of layers with nearly equal print_z. - coordf_t zavg = 0.5 * (layers_sorted[i]->print_z + layers_sorted[j - 1]->print_z); - coordf_t height_min = layers_sorted[i]->height; - bool empty = true; - // For snug supports, layers where the direction of the support interface shall change are accounted for. - size_t num_interfaces = 0; - size_t num_top_contacts = 0; - double top_contact_bottom_z = 0; - for (size_t u = i; u < j; ++u) { - MyLayer &layer = *layers_sorted[u]; - if (! layer.polygons.empty()) { - empty = false; - num_interfaces += one_of(layer.layer_type, support_types_interface); - if (layer.layer_type == sltTopContact) { - ++ num_top_contacts; - assert(num_top_contacts <= 1); - // All top contact layers sharing this print_z shall also share bottom_z. - //assert(num_top_contacts == 1 || (top_contact_bottom_z - layer.bottom_z) < EPSILON); - top_contact_bottom_z = layer.bottom_z; - } - } - layer.print_z = zavg; - height_min = std::min(height_min, layer.height); - } - if (! empty) { - // Here the upper_layer and lower_layer pointers are left to null at the support layers, - // as they are never used. These pointers are candidates for removal. - bool this_layer_contacts_only = num_top_contacts > 0 && num_top_contacts == num_interfaces; - size_t this_layer_id_interface = layer_id_interface; - if (this_layer_contacts_only) { - // Find a supporting layer for its interface ID. - for (auto it = object.support_layers().rbegin(); it != object.support_layers().rend(); ++ it) - if (const SupportLayer &other_layer = **it; std::abs(other_layer.print_z - top_contact_bottom_z) < EPSILON) { - // other_layer supports this top contact layer. Assign a different support interface direction to this layer - // from the layer that supports it. - this_layer_id_interface = other_layer.interface_id() + 1; - } - } - object.add_support_layer(layer_id ++, this_layer_id_interface, height_min, zavg); - if (num_interfaces && ! this_layer_contacts_only) - ++ layer_id_interface; - } - i = j; - } +#ifdef SLIC3R_DEBUG + SupportGeneratorLayersPtr layers_sorted = +#endif // SLIC3R_DEBUG + generate_support_layers(object, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers); BOOST_LOG_TRIVIAL(info) << "Support generator - Generating tool paths"; @@ -641,7 +589,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should. int j = i + 1; coordf_t zmax = layers_sorted[i]->print_z + EPSILON; - bool empty = true; + bool empty = layers_sorted[i]->polygons.empty(); for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j) if (!layers_sorted[j]->polygons.empty()) empty = false; @@ -661,7 +609,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) #endif /* SLIC3R_DEBUG */ // Generate the actual toolpaths and save them into each layer. - this->generate_toolpaths(object.support_layers(), raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers); + generate_support_toolpaths(object.support_layers(), *m_object_config, m_support_params, m_slicing_params, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers); #ifdef SLIC3R_DEBUG { @@ -671,7 +619,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should. int j = i + 1; coordf_t zmax = layers_sorted[i]->print_z + EPSILON; - bool empty = true; + bool empty = layers_sorted[i]->polygons.empty(); for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j) if (! layers_sorted[j]->polygons.empty()) empty = false; @@ -848,6 +796,9 @@ public: ) { switch (m_style) { + case smsTree: + assert(false); + [[fallthrough]]; case smsGrid: { #ifdef SUPPORT_USE_AGG_RASTERIZER @@ -1681,17 +1632,17 @@ static inline std::tuple detect_overhangs( // Allocate one, possibly two support contact layers. // For "thick" overhangs, one support layer will be generated to support normal extrusions, the other to support the "thick" extrusions. -static inline std::pair new_contact_layer( +static inline std::pair new_contact_layer( const PrintConfig &print_config, const PrintObjectConfig &object_config, const SlicingParameters &slicing_params, const coordf_t support_layer_height_min, const Layer &layer, - std::deque &layer_storage, + std::deque &layer_storage, tbb::spin_mutex &layer_storage_mutex) { double print_z, bottom_z, height; - PrintObjectSupportMaterial::MyLayer* bridging_layer = nullptr; + SupportGeneratorLayer* bridging_layer = nullptr; assert(layer.id() >= slicing_params.raft_layers()); size_t layer_id = layer.id() - slicing_params.raft_layers(); @@ -1717,7 +1668,7 @@ static inline std::pair(nullptr, nullptr); + return std::pair(nullptr, nullptr); } const bool has_raft = slicing_params.raft_layers() > 1; const coordf_t min_print_z = has_raft ? slicing_params.raft_contact_top_z : slicing_params.first_print_layer_height; @@ -1748,7 +1699,7 @@ static inline std::pairidx_object_layer_above = layer_id; bridging_layer->print_z = bridging_print_z; if (bridging_print_z == slicing_params.first_print_layer_height) { @@ -1764,7 +1715,7 @@ static inline std::pairprint_z < l2->print_z; }); + std::sort(layers.begin(), layers.end(), [](const SupportGeneratorLayer *l1, const SupportGeneratorLayer *l2) { return l1->print_z < l2->print_z; }); int i = 0; int k = 0; @@ -1938,7 +1889,7 @@ static void merge_contact_layers(const SlicingParameters &slicing_params, double for (; j < (int)layers.size() && layers[j]->print_z < slicing_params.first_print_layer_height + support_layer_height_min - EPSILON; ++ j); if (j > 0) { // Merge the layers layers (0) to (j - 1) into the layers[0]. - PrintObjectSupportMaterial::MyLayer &dst = *layers.front(); + SupportGeneratorLayer &dst = *layers.front(); for (int u = 1; u < j; ++ u) dst.merge(std::move(*layers[u])); // Snap the first layer to the 1st layer height. @@ -1956,7 +1907,7 @@ static void merge_contact_layers(const SlicingParameters &slicing_params, double for (; j < (int)layers.size() && layers[j]->print_z < zmax; ++ j) ; if (i + 1 < j) { // Merge the layers layers (i + 1) to (j - 1) into the layers[i]. - PrintObjectSupportMaterial::MyLayer &dst = *layers[i]; + SupportGeneratorLayer &dst = *layers[i]; for (int u = i + 1; u < j; ++ u) dst.merge(std::move(*layers[u])); } @@ -1971,8 +1922,8 @@ static void merge_contact_layers(const SlicingParameters &slicing_params, double // Generate top contact layers supporting overhangs. // For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined. // If supports over bed surface only are requested, don't generate contact layers over an object. -PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_layers( - const PrintObject &object, const std::vector &buildplate_covered, MyLayerStorage &layer_storage) const +SupportGeneratorLayersPtr PrintObjectSupportMaterial::top_contact_layers( + const PrintObject &object, const std::vector &buildplate_covered, SupportGeneratorLayerStorage &layer_storage) const { #ifdef SLIC3R_DEBUG static int iRun = 0; @@ -1984,7 +1935,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ SupportAnnotations annotations(object, buildplate_covered); // Output layers, sorted by top Z. - MyLayersPtr contact_out; + SupportGeneratorLayersPtr contact_out; BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::top_contact_layers() in parallel - start"; // Determine top contact areas. @@ -2056,17 +2007,17 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ } // Find the bottom contact layers above the top surfaces of this layer. -static inline PrintObjectSupportMaterial::MyLayer* detect_bottom_contacts( +static inline SupportGeneratorLayer* detect_bottom_contacts( const SlicingParameters &slicing_params, - const PrintObjectSupportMaterial::SupportParams &support_params, + const SupportParameters &support_params, const PrintObject &object, const Layer &layer, // Existing top contact layers, to which this newly created bottom contact layer will be snapped to guarantee a minimum layer height. - const PrintObjectSupportMaterial::MyLayersPtr &top_contacts, + const SupportGeneratorLayersPtr &top_contacts, // First top contact layer index overlapping with this new bottom interface layer. size_t contact_idx, // To allocate a new layer from. - std::deque &layer_storage, + std::deque &layer_storage, // To trim the support areas above this bottom interface layer with this newly created bottom interface layer. std::vector &layer_support_areas, // Support areas projected from top to bottom, starting with top support interfaces. @@ -2101,7 +2052,7 @@ static inline PrintObjectSupportMaterial::MyLayer* detect_bottom_contacts( size_t layer_id = layer.id() - slicing_params.raft_layers(); // Allocate a new bottom contact layer. - PrintObjectSupportMaterial::MyLayer &layer_new = layer_allocate(layer_storage, PrintObjectSupportMaterial::sltBottomContact); + SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, SupporLayerType::BottomContact); // Grow top surfaces so that interface and support generation are generated // with some spacing from object - it looks we don't need the actual // top shapes so this can be done here @@ -2263,12 +2214,12 @@ static inline std::pair project_support_to_grid(const Layer // Generate bottom contact layers supporting the top contact layers. // For a soluble interface material synchronize the layer heights with the object, // otherwise set the layer height to a bridging flow of a support interface nozzle. -PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_contact_layers_and_layer_support_areas( - const PrintObject &object, const MyLayersPtr &top_contacts, std::vector &buildplate_covered, - MyLayerStorage &layer_storage, std::vector &layer_support_areas) const +SupportGeneratorLayersPtr PrintObjectSupportMaterial::bottom_contact_layers_and_layer_support_areas( + const PrintObject &object, const SupportGeneratorLayersPtr &top_contacts, std::vector &buildplate_covered, + SupportGeneratorLayerStorage &layer_storage, std::vector &layer_support_areas) const { if (top_contacts.empty()) - return MyLayersPtr(); + return SupportGeneratorLayersPtr(); #ifdef SLIC3R_DEBUG static size_t s_iRun = 0; @@ -2285,7 +2236,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta // find object top surfaces // we'll use them to clip our support and detect where does it stick - MyLayersPtr bottom_contacts; + SupportGeneratorLayersPtr bottom_contacts; // There is some support to be built, if there are non-empty top surfaces detected. // Sum of unsupported contact areas above the current layer.print_z. @@ -2304,7 +2255,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta Polygons enforcers_new; #endif // SLIC3R_DEBUG for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z > layer.print_z - EPSILON; -- contact_idx) { - MyLayer &top_contact = *top_contacts[contact_idx]; + SupportGeneratorLayer &top_contact = *top_contacts[contact_idx]; #ifndef SLIC3R_DEBUG Polygons polygons_new; Polygons enforcers_new; @@ -2346,7 +2297,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta #endif // SLIC3R_DEBUG ] { // Find the bottom contact layers above the top surfaces of this layer. - MyLayer *layer_new = detect_bottom_contacts( + SupportGeneratorLayer *layer_new = detect_bottom_contacts( m_slicing_params, m_support_params, object, layer, top_contacts, contact_idx, layer_storage, layer_support_areas, overhangs_for_bottom_contacts #ifdef SLIC3R_DEBUG , iRun, polygons_new @@ -2481,19 +2432,19 @@ int idx_lower_or_equal(const std::vector &vec, int idx, FN_LOWER_EQUAL fn_lo // Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them. void PrintObjectSupportMaterial::trim_top_contacts_by_bottom_contacts( - const PrintObject &object, const MyLayersPtr &bottom_contacts, MyLayersPtr &top_contacts) const + const PrintObject &object, const SupportGeneratorLayersPtr &bottom_contacts, SupportGeneratorLayersPtr &top_contacts) const { tbb::parallel_for(tbb::blocked_range(0, int(top_contacts.size())), [&bottom_contacts, &top_contacts](const tbb::blocked_range& range) { int idx_bottom_overlapping_first = -2; // For all top contact layers, counting downwards due to the way idx_higher_or_equal caches the last index to avoid repeated binary search. for (int idx_top = range.end() - 1; idx_top >= range.begin(); -- idx_top) { - MyLayer &layer_top = *top_contacts[idx_top]; + SupportGeneratorLayer &layer_top = *top_contacts[idx_top]; // Find the first bottom layer overlapping with layer_top. - idx_bottom_overlapping_first = idx_lower_or_equal(bottom_contacts, idx_bottom_overlapping_first, [&layer_top](const MyLayer *layer_bottom){ return layer_bottom->bottom_print_z() - EPSILON <= layer_top.bottom_z; }); + idx_bottom_overlapping_first = idx_lower_or_equal(bottom_contacts, idx_bottom_overlapping_first, [&layer_top](const SupportGeneratorLayer *layer_bottom){ return layer_bottom->bottom_print_z() - EPSILON <= layer_top.bottom_z; }); // For all top contact layers overlapping with the thick bottom contact layer: for (int idx_bottom_overlapping = idx_bottom_overlapping_first; idx_bottom_overlapping >= 0; -- idx_bottom_overlapping) { - const MyLayer &layer_bottom = *bottom_contacts[idx_bottom_overlapping]; + const SupportGeneratorLayer &layer_bottom = *bottom_contacts[idx_bottom_overlapping]; assert(layer_bottom.bottom_print_z() - EPSILON <= layer_top.bottom_z); if (layer_top.print_z < layer_bottom.print_z + EPSILON) { // Layers overlap. Trim layer_top with layer_bottom. @@ -2505,16 +2456,16 @@ void PrintObjectSupportMaterial::trim_top_contacts_by_bottom_contacts( }); } -PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_support_layers( +SupportGeneratorLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_support_layers( const PrintObject &object, - const MyLayersPtr &bottom_contacts, - const MyLayersPtr &top_contacts, - MyLayerStorage &layer_storage) const + const SupportGeneratorLayersPtr &bottom_contacts, + const SupportGeneratorLayersPtr &top_contacts, + SupportGeneratorLayerStorage &layer_storage) const { - MyLayersPtr intermediate_layers; + SupportGeneratorLayersPtr intermediate_layers; // Collect and sort the extremes (bottoms of the top contacts and tops of the bottom contacts). - MyLayersPtr extremes; + SupportGeneratorLayersPtr extremes; extremes.reserve(top_contacts.size() + bottom_contacts.size()); for (size_t i = 0; i < top_contacts.size(); ++ i) // Bottoms of the top contact layers. In case of non-soluble supports, @@ -2526,18 +2477,18 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int if (extremes.empty()) return intermediate_layers; - auto layer_extreme_lower = [](const MyLayer *l1, const MyLayer *l2) { + auto layer_extreme_lower = [](const SupportGeneratorLayer *l1, const SupportGeneratorLayer *l2) { coordf_t z1 = l1->extreme_z(); coordf_t z2 = l2->extreme_z(); // If the layers are aligned, return the top contact surface first. - return z1 < z2 || (z1 == z2 && l1->layer_type == PrintObjectSupportMaterial::sltTopContact && l2->layer_type == PrintObjectSupportMaterial::sltBottomContact); + return z1 < z2 || (z1 == z2 && l1->layer_type == SupporLayerType::TopContact && l2->layer_type == SupporLayerType::BottomContact); }; std::sort(extremes.begin(), extremes.end(), layer_extreme_lower); assert(extremes.empty() || (extremes.front()->extreme_z() > m_slicing_params.raft_interface_top_z - EPSILON && (m_slicing_params.raft_layers() == 1 || // only raft contact layer - extremes.front()->layer_type == sltTopContact || // first extreme is a top contact layer + extremes.front()->layer_type == SupporLayerType::TopContact || // first extreme is a top contact layer extremes.front()->extreme_z() > m_slicing_params.first_print_layer_height - EPSILON))); bool synchronize = this->synchronize_layers(); @@ -2549,7 +2500,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int extremes[i]->extreme_z() - extremes[i-1]->extreme_z() > m_support_params.support_layer_height_min - EPSILON); assert(extremes[i]->extreme_z() - extremes[i-1]->extreme_z() > 0. || extremes[i]->layer_type == extremes[i-1]->layer_type || - (extremes[i]->layer_type == sltBottomContact && extremes[i - 1]->layer_type == sltTopContact)); + (extremes[i]->layer_type == SupporLayerType::BottomContact && extremes[i - 1]->layer_type == SupporLayerType::TopContact)); } #endif @@ -2562,19 +2513,19 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int if (! extremes.empty() && std::abs(extremes.front()->extreme_z() - m_slicing_params.raft_interface_top_z) < EPSILON) { // This is a raft contact layer, its height has been decided in this->top_contact_layers(). // Ignore this layer when calculating the intermediate support layers. - assert(extremes.front()->layer_type == sltTopContact); + assert(extremes.front()->layer_type == SupporLayerType::TopContact); ++ idx_extreme_first; } for (size_t idx_extreme = idx_extreme_first; idx_extreme < extremes.size(); ++ idx_extreme) { - MyLayer *extr2 = extremes[idx_extreme]; + SupportGeneratorLayer *extr2 = extremes[idx_extreme]; coordf_t extr2z = extr2->extreme_z(); if (std::abs(extr2z - m_slicing_params.first_print_layer_height) < EPSILON) { // This is a bottom of a synchronized (or soluble) top contact layer, its height has been decided in this->top_contact_layers(). - assert(extr2->layer_type == sltTopContact); + assert(extr2->layer_type == SupporLayerType::TopContact); assert(extr2->bottom_z == m_slicing_params.first_print_layer_height); assert(extr2->print_z >= m_slicing_params.first_print_layer_height + m_support_params.support_layer_height_min - EPSILON); if (intermediate_layers.empty() || intermediate_layers.back()->print_z < m_slicing_params.first_print_layer_height) { - MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate); + SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, SupporLayerType::Intermediate); layer_new.bottom_z = 0.; layer_new.print_z = m_slicing_params.first_print_layer_height; layer_new.height = m_slicing_params.first_print_layer_height; @@ -2584,11 +2535,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int } assert(extr2z >= m_slicing_params.raft_interface_top_z + EPSILON); assert(extr2z >= m_slicing_params.first_print_layer_height + EPSILON); - MyLayer *extr1 = (idx_extreme == idx_extreme_first) ? nullptr : extremes[idx_extreme - 1]; + SupportGeneratorLayer *extr1 = (idx_extreme == idx_extreme_first) ? nullptr : extremes[idx_extreme - 1]; // Fuse a support layer firmly to the raft top interface (not to the raft contacts). coordf_t extr1z = (extr1 == nullptr) ? m_slicing_params.raft_interface_top_z : extr1->extreme_z(); assert(extr2z >= extr1z); - assert(extr2z > extr1z || (extr1 != nullptr && extr2->layer_type == sltBottomContact)); + assert(extr2z > extr1z || (extr1 != nullptr && extr2->layer_type == SupporLayerType::BottomContact)); if (std::abs(extr1z) < EPSILON) { // This layer interval starts with the 1st layer. Print the 1st layer using the prescribed 1st layer thickness. // assert(! m_slicing_params.has_raft()); RaftingEdition: unclear where the issue is: assert fails with 1-layer raft & base supports @@ -2596,7 +2547,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int // At this point only layers above first_print_layer_heigth + EPSILON are expected as the other cases were captured earlier. assert(extr2z >= m_slicing_params.first_print_layer_height + EPSILON); // Generate a new intermediate layer. - MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate); + SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, SupporLayerType::Intermediate); layer_new.bottom_z = 0.; layer_new.print_z = extr1z = m_slicing_params.first_print_layer_height; layer_new.height = extr1z; @@ -2616,7 +2567,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int ++ idx_layer_object; if (idx_layer_object == 0 && extr1z == m_slicing_params.raft_interface_top_z) { // Insert one base support layer below the object. - MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate); + SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, SupporLayerType::Intermediate); layer_new.print_z = m_slicing_params.object_print_z_min; layer_new.bottom_z = m_slicing_params.raft_interface_top_z; layer_new.height = layer_new.print_z - layer_new.bottom_z; @@ -2624,7 +2575,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int } // Emit all intermediate support layers synchronized with object layers up to extr2z. for (; idx_layer_object < object.layers().size() && object.layers()[idx_layer_object]->print_z < extr2z + EPSILON; ++ idx_layer_object) { - MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate); + SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, SupporLayerType::Intermediate); layer_new.print_z = object.layers()[idx_layer_object]->print_z; layer_new.height = object.layers()[idx_layer_object]->height; layer_new.bottom_z = (idx_layer_object > 0) ? object.layers()[idx_layer_object - 1]->print_z : (layer_new.print_z - layer_new.height); @@ -2636,13 +2587,13 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int size_t n_layers_extra = size_t(ceil(dist / m_slicing_params.max_suport_layer_height)); assert(n_layers_extra > 0); coordf_t step = dist / coordf_t(n_layers_extra); - if (extr1 != nullptr && extr1->layer_type == sltTopContact && + if (extr1 != nullptr && extr1->layer_type == SupporLayerType::TopContact && extr1->print_z + m_support_params.support_layer_height_min > extr1->bottom_z + step) { // The bottom extreme is a bottom of a top surface. Ensure that the gap // between the 1st intermediate layer print_z and extr1->print_z is not too small. assert(extr1->bottom_z + m_support_params.support_layer_height_min < extr1->print_z + EPSILON); // Generate the first intermediate layer. - MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate); + SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, SupporLayerType::Intermediate); layer_new.bottom_z = extr1->bottom_z; layer_new.print_z = extr1z = extr1->print_z; layer_new.height = extr1->height; @@ -2654,7 +2605,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int // Continue printing the other layers up to extr2z. step = dist / coordf_t(n_layers_extra); } - if (! m_slicing_params.soluble_interface && extr2->layer_type == sltTopContact) { + if (! m_slicing_params.soluble_interface && extr2->layer_type == SupporLayerType::TopContact) { // This is a top interface layer, which does not have a height assigned yet. Do it now. assert(extr2->height == 0.); assert(extr1z > m_slicing_params.first_print_layer_height - EPSILON); @@ -2666,7 +2617,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int coordf_t extr2z_large_steps = extr2z; // Take the largest allowed step in the Z axis until extr2z_large_steps is reached. for (size_t i = 0; i < n_layers_extra; ++ i) { - MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate); + SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, SupporLayerType::Intermediate); if (i + 1 == n_layers_extra) { // Last intermediate layer added. Align the last entered layer with extr2z_large_steps exactly. layer_new.bottom_z = (i == 0) ? extr1z : intermediate_layers.back()->print_z; @@ -2697,9 +2648,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int // Also the bottom/top_contacts shall have a layer thickness assigned already. void PrintObjectSupportMaterial::generate_base_layers( const PrintObject &object, - const MyLayersPtr &bottom_contacts, - const MyLayersPtr &top_contacts, - MyLayersPtr &intermediate_layers, + const SupportGeneratorLayersPtr &bottom_contacts, + const SupportGeneratorLayersPtr &top_contacts, + SupportGeneratorLayersPtr &intermediate_layers, const std::vector &layer_support_areas) const { #ifdef SLIC3R_DEBUG @@ -2723,7 +2674,7 @@ void PrintObjectSupportMaterial::generate_base_layers( { BOOST_LOG_TRIVIAL(trace) << "Support generator - generate_base_layers - creating layer " << idx_intermediate << " of " << intermediate_layers.size(); - MyLayer &layer_intermediate = *intermediate_layers[idx_intermediate]; + SupportGeneratorLayer &layer_intermediate = *intermediate_layers[idx_intermediate]; // Layers must be sorted by print_z. assert(idx_intermediate == 0 || layer_intermediate.print_z >= intermediate_layers[idx_intermediate - 1]->print_z); @@ -2746,10 +2697,10 @@ void PrintObjectSupportMaterial::generate_base_layers( // 4) base.print_z > top.bottom_z && base.bottom_z < top.bottom_z -> Base overlaps with top.bottom_z. This must not happen. // 5) base.print_z <= top.print_z && base.bottom_z >= top.bottom_z -> Base is fully inside top. Trim base by top. idx_top_contact_above = idx_lower_or_equal(top_contacts, idx_top_contact_above, - [&layer_intermediate](const MyLayer *layer){ return layer->bottom_z <= layer_intermediate.print_z - EPSILON; }); + [&layer_intermediate](const SupportGeneratorLayer *layer){ return layer->bottom_z <= layer_intermediate.print_z - EPSILON; }); // Collect all the top_contact layer intersecting with this layer. for (int idx_top_contact_overlapping = idx_top_contact_above; idx_top_contact_overlapping >= 0; -- idx_top_contact_overlapping) { - MyLayer &layer_top_overlapping = *top_contacts[idx_top_contact_overlapping]; + SupportGeneratorLayer &layer_top_overlapping = *top_contacts[idx_top_contact_overlapping]; if (layer_top_overlapping.print_z < layer_intermediate.bottom_z + EPSILON) break; // Base must not overlap with top.bottom_z. @@ -2766,7 +2717,7 @@ void PrintObjectSupportMaterial::generate_base_layers( polygons_new = layer_support_areas.front(); double first_layer_z = object.layers().front()->print_z; for (int i = idx_top_contact_above + 1; i < int(top_contacts.size()); ++ i) { - MyLayer &contacts = *top_contacts[i]; + SupportGeneratorLayer &contacts = *top_contacts[i]; if (contacts.print_z > first_layer_z + EPSILON) break; assert(contacts.bottom_z > layer_intermediate.print_z - EPSILON); @@ -2783,10 +2734,10 @@ void PrintObjectSupportMaterial::generate_base_layers( // 4) base.print_z > bottom.print_z && base.bottom_z >= bottom.print_z -> Base overlaps with bottom.print_z. This must not happen. // 5) base.print_z <= bottom.print_z && base.bottom_z >= bottom.bottom_z -> Base is fully inside top. Trim base by top. idx_bottom_contact_overlapping = idx_lower_or_equal(bottom_contacts, idx_bottom_contact_overlapping, - [&layer_intermediate](const MyLayer *layer){ return layer->bottom_print_z() <= layer_intermediate.print_z - EPSILON; }); + [&layer_intermediate](const SupportGeneratorLayer *layer){ return layer->bottom_print_z() <= layer_intermediate.print_z - EPSILON; }); // Collect all the bottom_contacts layer intersecting with this layer. for (int i = idx_bottom_contact_overlapping; i >= 0; -- i) { - MyLayer &layer_bottom_overlapping = *bottom_contacts[i]; + SupportGeneratorLayer &layer_bottom_overlapping = *bottom_contacts[i]; if (layer_bottom_overlapping.print_z < layer_intermediate.bottom_print_z() + EPSILON) break; // Base must not overlap with bottom.top_z. @@ -2816,7 +2767,7 @@ void PrintObjectSupportMaterial::generate_base_layers( polygons_new, polygons_trimming, ApplySafetyOffset::Yes); // safety offset to merge the touching source polygons - layer_intermediate.layer_type = sltBase; + layer_intermediate.layer_type = SupporLayerType::Base; #if 0 // coordf_t fillet_radius_scaled = scale_(m_object_config->support_material_spacing); @@ -2838,7 +2789,7 @@ void PrintObjectSupportMaterial::generate_base_layers( BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_base_layers() in parallel - end"; #ifdef SLIC3R_DEBUG - for (MyLayersPtr::const_iterator it = intermediate_layers.begin(); it != intermediate_layers.end(); ++it) + for (SupportGeneratorLayersPtr::const_iterator it = intermediate_layers.begin(); it != intermediate_layers.end(); ++it) ::Slic3r::SVG::export_expolygons( debug_out_path("support-intermediate-layers-untrimmed-%d-%lf.svg", iRun, (*it)->print_z), union_ex((*it)->polygons)); @@ -2850,7 +2801,7 @@ void PrintObjectSupportMaterial::generate_base_layers( void PrintObjectSupportMaterial::trim_support_layers_by_object( const PrintObject &object, - MyLayersPtr &support_layers, + SupportGeneratorLayersPtr &support_layers, const coordf_t gap_extra_above, const coordf_t gap_extra_below, const coordf_t gap_xy) const @@ -2859,10 +2810,10 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( // Collect non-empty layers to be processed in parallel. // This is a good idea as pulling a thread from a thread pool for an empty task is expensive. - MyLayersPtr nonempty_layers; + SupportGeneratorLayersPtr nonempty_layers; nonempty_layers.reserve(support_layers.size()); for (size_t idx_layer = 0; idx_layer < support_layers.size(); ++ idx_layer) { - MyLayer *support_layer = support_layers[idx_layer]; + SupportGeneratorLayer *support_layer = support_layers[idx_layer]; if (! support_layer->polygons.empty() && support_layer->print_z >= m_slicing_params.raft_contact_top_z + EPSILON) // Non-empty support layer and not a raft layer. nonempty_layers.push_back(support_layer); @@ -2875,7 +2826,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( [this, &object, &nonempty_layers, gap_extra_above, gap_extra_below, gap_xy_scaled](const tbb::blocked_range& range) { size_t idx_object_layer_overlapping = size_t(-1); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - MyLayer &support_layer = *nonempty_layers[idx_layer]; + SupportGeneratorLayer &support_layer = *nonempty_layers[idx_layer]; // BOOST_LOG_TRIVIAL(trace) << "Support generator - trim_support_layers_by_object - trimmming non-empty layer " << idx_layer << " of " << nonempty_layers.size(); assert(! support_layer.polygons.empty() && support_layer.print_z >= m_slicing_params.raft_contact_top_z + EPSILON); // Find the overlapping object layers including the extra above / below gap. @@ -2922,13 +2873,15 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::trim_support_layers_by_object() in parallel - end"; } -PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raft_base( - const PrintObject &object, - const MyLayersPtr &top_contacts, - const MyLayersPtr &interface_layers, - const MyLayersPtr &base_interface_layers, - const MyLayersPtr &base_layers, - MyLayerStorage &layer_storage) const +SupportGeneratorLayersPtr generate_raft_base( + const PrintObject &object, + const SupportParameters &support_params, + const SlicingParameters &slicing_params, + const SupportGeneratorLayersPtr &top_contacts, + const SupportGeneratorLayersPtr &interface_layers, + const SupportGeneratorLayersPtr &base_interface_layers, + const SupportGeneratorLayersPtr &base_layers, + SupportGeneratorLayerStorage &layer_storage) { // If there is brim to be generated, calculate the trimming regions. Polygons brim; @@ -2960,22 +2913,22 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf } // How much to inflate the support columns to be stable. This also applies to the 1st layer, if no raft layers are to be printed. - const float inflate_factor_fine = float(scale_((m_slicing_params.raft_layers() > 1) ? 0.5 : EPSILON)); + const float inflate_factor_fine = float(scale_((slicing_params.raft_layers() > 1) ? 0.5 : EPSILON)); const float inflate_factor_1st_layer = std::max(0.f, float(scale_(object.config().raft_first_layer_expansion)) - inflate_factor_fine); - MyLayer *contacts = top_contacts .empty() ? nullptr : top_contacts .front(); - MyLayer *interfaces = interface_layers .empty() ? nullptr : interface_layers .front(); - MyLayer *base_interfaces = base_interface_layers.empty() ? nullptr : base_interface_layers.front(); - MyLayer *columns_base = base_layers .empty() ? nullptr : base_layers .front(); - if (contacts != nullptr && contacts->print_z > std::max(m_slicing_params.first_print_layer_height, m_slicing_params.raft_contact_top_z) + EPSILON) + SupportGeneratorLayer *contacts = top_contacts .empty() ? nullptr : top_contacts .front(); + SupportGeneratorLayer *interfaces = interface_layers .empty() ? nullptr : interface_layers .front(); + SupportGeneratorLayer *base_interfaces = base_interface_layers.empty() ? nullptr : base_interface_layers.front(); + SupportGeneratorLayer *columns_base = base_layers .empty() ? nullptr : base_layers .front(); + if (contacts != nullptr && contacts->print_z > std::max(slicing_params.first_print_layer_height, slicing_params.raft_contact_top_z) + EPSILON) // This is not the raft contact layer. contacts = nullptr; - if (interfaces != nullptr && interfaces->bottom_print_z() > m_slicing_params.raft_interface_top_z + EPSILON) + if (interfaces != nullptr && interfaces->bottom_print_z() > slicing_params.raft_interface_top_z + EPSILON) // This is not the raft column base layer. interfaces = nullptr; - if (base_interfaces != nullptr && base_interfaces->bottom_print_z() > m_slicing_params.raft_interface_top_z + EPSILON) + if (base_interfaces != nullptr && base_interfaces->bottom_print_z() > slicing_params.raft_interface_top_z + EPSILON) // This is not the raft column base layer. base_interfaces = nullptr; - if (columns_base != nullptr && columns_base->bottom_print_z() > m_slicing_params.raft_interface_top_z + EPSILON) + if (columns_base != nullptr && columns_base->bottom_print_z() > slicing_params.raft_interface_top_z + EPSILON) // This is not the raft interface layer. columns_base = nullptr; @@ -2988,9 +2941,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf polygons_append(interface_polygons, expand(base_interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS)); // Output vector. - MyLayersPtr raft_layers; + SupportGeneratorLayersPtr raft_layers; - if (m_slicing_params.raft_layers() > 1) { + if (slicing_params.raft_layers() > 1) { Polygons base; Polygons columns; if (columns_base != nullptr) { @@ -3007,30 +2960,30 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf // Do not add the raft contact layer, only add the raft layers below the contact layer. // Insert the 1st layer. { - MyLayer &new_layer = layer_allocate(layer_storage, (m_slicing_params.base_raft_layers > 0) ? sltRaftBase : sltRaftInterface); + SupportGeneratorLayer &new_layer = layer_allocate(layer_storage, (slicing_params.base_raft_layers > 0) ? SupporLayerType::RaftBase : SupporLayerType::RaftInterface); raft_layers.push_back(&new_layer); - new_layer.print_z = m_slicing_params.first_print_layer_height; - new_layer.height = m_slicing_params.first_print_layer_height; + new_layer.print_z = slicing_params.first_print_layer_height; + new_layer.height = slicing_params.first_print_layer_height; new_layer.bottom_z = 0.; new_layer.polygons = inflate_factor_1st_layer > 0 ? expand(base, inflate_factor_1st_layer) : base; } // Insert the base layers. - for (size_t i = 1; i < m_slicing_params.base_raft_layers; ++ i) { + for (size_t i = 1; i < slicing_params.base_raft_layers; ++ i) { coordf_t print_z = raft_layers.back()->print_z; - MyLayer &new_layer = layer_allocate(layer_storage, sltRaftBase); + SupportGeneratorLayer &new_layer = layer_allocate(layer_storage, SupporLayerType::RaftBase); raft_layers.push_back(&new_layer); - new_layer.print_z = print_z + m_slicing_params.base_raft_layer_height; - new_layer.height = m_slicing_params.base_raft_layer_height; + new_layer.print_z = print_z + slicing_params.base_raft_layer_height; + new_layer.height = slicing_params.base_raft_layer_height; new_layer.bottom_z = print_z; new_layer.polygons = base; } // Insert the interface layers. - for (size_t i = 1; i < m_slicing_params.interface_raft_layers; ++ i) { + for (size_t i = 1; i < slicing_params.interface_raft_layers; ++ i) { coordf_t print_z = raft_layers.back()->print_z; - MyLayer &new_layer = layer_allocate(layer_storage, sltRaftInterface); + SupportGeneratorLayer &new_layer = layer_allocate(layer_storage, SupporLayerType::RaftInterface); raft_layers.push_back(&new_layer); - new_layer.print_z = print_z + m_slicing_params.interface_raft_layer_height; - new_layer.height = m_slicing_params.interface_raft_layer_height; + new_layer.print_z = print_z + slicing_params.interface_raft_layer_height; + new_layer.height = slicing_params.interface_raft_layer_height; new_layer.bottom_z = print_z; new_layer.polygons = interface_polygons; //FIXME misusing contact_polygons for support columns. @@ -3038,12 +2991,12 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf } } else { if (columns_base != nullptr) { - // Expand the bases of the support columns in the 1st layer. + // Expand the bases of the support columns in the 1st layer. Polygons &raft = columns_base->polygons; - Polygons trimming = offset(m_object->layers().front()->lslices, (float)scale_(m_support_params.gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS); + Polygons trimming = offset(object.layers().front()->lslices, (float)scale_(support_params.gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS); if (inflate_factor_1st_layer > SCALED_EPSILON) { // Inflate in multiple steps to avoid leaking of the support 1st layer through object walls. - auto nsteps = std::max(5, int(ceil(inflate_factor_1st_layer / m_support_params.first_layer_flow.scaled_width()))); + auto nsteps = std::max(5, int(ceil(inflate_factor_1st_layer / support_params.first_layer_flow.scaled_width()))); float step = inflate_factor_1st_layer / nsteps; for (int i = 0; i < nsteps; ++ i) raft = diff(expand(raft, step), trimming); @@ -3068,17 +3021,17 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf } // Convert some of the intermediate layers into top/bottom interface layers as well as base interface layers. -std::pair PrintObjectSupportMaterial::generate_interface_layers( - const MyLayersPtr &bottom_contacts, - const MyLayersPtr &top_contacts, - MyLayersPtr &intermediate_layers, - MyLayerStorage &layer_storage) const +std::pair PrintObjectSupportMaterial::generate_interface_layers( + const SupportGeneratorLayersPtr &bottom_contacts, + const SupportGeneratorLayersPtr &top_contacts, + SupportGeneratorLayersPtr &intermediate_layers, + SupportGeneratorLayerStorage &layer_storage) const { // my $area_threshold = $self->interface_flow->scaled_spacing ** 2; - std::pair base_and_interface_layers; - MyLayersPtr &interface_layers = base_and_interface_layers.first; - MyLayersPtr &base_interface_layers = base_and_interface_layers.second; + std::pair base_and_interface_layers; + SupportGeneratorLayersPtr &interface_layers = base_and_interface_layers.first; + SupportGeneratorLayersPtr &base_interface_layers = base_and_interface_layers.second; // distinguish between interface and base interface layers // Contact layer is considered an interface layer, therefore run the following block only if support_material_interface_layers > 1. @@ -3115,7 +3068,7 @@ std::pair MyLayer* { + SupportGeneratorLayer &intermediate_layer, Polygons &bottom, Polygons &&top, const Polygons *subtract, SupporLayerType type) -> SupportGeneratorLayer* { assert(! bottom.empty() || ! top.empty()); // Merge top into bottom, unite them with a safety offset. append(bottom, std::move(top)); @@ -3129,7 +3082,7 @@ std::pair::max() : intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + num_interface_layers_only_top - 1)]->print_z; // Move idx_top_contact_first up until above the current print_z. - idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const MyLayer *layer){ return layer->print_z >= intermediate_layer.print_z; }); // - EPSILON + idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const SupportGeneratorLayer *layer){ return layer->print_z >= intermediate_layer.print_z; }); // - EPSILON // Collect the top contact areas above this intermediate layer, below top_z. for (int idx_top_contact = idx_top_contact_first; idx_top_contact < int(top_contacts.size()); ++ idx_top_contact) { - const MyLayer &top_contact_layer = *top_contacts[idx_top_contact]; + const SupportGeneratorLayer &top_contact_layer = *top_contacts[idx_top_contact]; //FIXME maybe this adds one interface layer in excess? if (top_contact_layer.bottom_z - EPSILON > top_z) break; @@ -3199,26 +3152,26 @@ std::pair::max() : intermediate_layers[std::max(0, idx_intermediate_layer - num_interface_layers_only_bottom)]->bottom_z; // Move idx_bottom_contact_first up until touching bottom_z. - idx_bottom_contact_first = idx_higher_or_equal(bottom_contacts, idx_bottom_contact_first, [bottom_z](const MyLayer *layer){ return layer->print_z >= bottom_z - EPSILON; }); + idx_bottom_contact_first = idx_higher_or_equal(bottom_contacts, idx_bottom_contact_first, [bottom_z](const SupportGeneratorLayer *layer){ return layer->print_z >= bottom_z - EPSILON; }); // Collect the top contact areas above this intermediate layer, below top_z. for (int idx_bottom_contact = idx_bottom_contact_first; idx_bottom_contact < int(bottom_contacts.size()); ++ idx_bottom_contact) { - const MyLayer &bottom_contact_layer = *bottom_contacts[idx_bottom_contact]; + const SupportGeneratorLayer &bottom_contact_layer = *bottom_contacts[idx_bottom_contact]; if (bottom_contact_layer.print_z - EPSILON > intermediate_layer.bottom_z) break; polygons_append(bottom_contact_layer.print_z - EPSILON > bottom_interface_z ? polygons_bottom_contact_projected_interface : polygons_bottom_contact_projected_base, bottom_contact_layer.polygons); } } - MyLayer *interface_layer = nullptr; + SupportGeneratorLayer *interface_layer = nullptr; if (! polygons_bottom_contact_projected_interface.empty() || ! polygons_top_contact_projected_interface.empty()) { interface_layer = insert_layer( intermediate_layer, polygons_bottom_contact_projected_interface, std::move(polygons_top_contact_projected_interface), nullptr, - polygons_top_contact_projected_interface.empty() ? sltBottomInterface : sltTopInterface); + polygons_top_contact_projected_interface.empty() ? SupporLayerType::BottomInterface : SupporLayerType::TopInterface); interface_layers[idx_intermediate_layer] = interface_layer; } if (! polygons_bottom_contact_projected_base.empty() || ! polygons_top_contact_projected_base.empty()) base_interface_layers[idx_intermediate_layer] = insert_layer( intermediate_layer, polygons_bottom_contact_projected_base, std::move(polygons_top_contact_projected_base), - interface_layer ? &interface_layer->polygons : nullptr, sltBase); + interface_layer ? &interface_layer->polygons : nullptr, SupporLayerType::Base); } }); @@ -3281,6 +3234,207 @@ static inline void fill_expolygons_generate_paths( fill_expolygons_generate_paths(dst, std::move(expolygons), filler, fill_params, density, role, flow); } +static inline void tree_supports_generate_paths( + ExtrusionEntitiesPtr &dst, + const Polygons &polygons, + const Flow &flow) +{ + // Offset expolygon inside, returns number of expolygons collected (0 or 1). + // Vertices of output paths are marked with Z = source contour index of the expoly. + // Vertices at the intersection of source contours are marked with Z = -1. + auto shrink_expolygon_with_contour_idx = [](const Slic3r::ExPolygon &expoly, const float delta, ClipperLib::JoinType joinType, double miterLimit, ClipperLib_Z::Paths &out) -> int + { + assert(delta > 0); + auto append_paths_with_z = [](ClipperLib::Paths &src, coord_t contour_idx, ClipperLib_Z::Paths &dst) { + dst.reserve(next_highest_power_of_2(dst.size() + src.size())); + for (const ClipperLib::Path &contour : src) { + ClipperLib_Z::Path tmp; + tmp.reserve(contour.size()); + for (const Point &p : contour) + tmp.emplace_back(p.x(), p.y(), contour_idx); + dst.emplace_back(std::move(tmp)); + } + }; + + // 1) Offset the outer contour. + ClipperLib_Z::Paths contours; + { + ClipperLib::ClipperOffset co; + if (joinType == jtRound) + co.ArcTolerance = miterLimit; + else + co.MiterLimit = miterLimit; + co.ShortestEdgeLength = double(delta * 0.005); + co.AddPath(expoly.contour.points, joinType, ClipperLib::etClosedPolygon); + ClipperLib::Paths contours_raw; + co.Execute(contours_raw, - delta); + if (contours_raw.empty()) + // No need to try to offset the holes. + return 0; + append_paths_with_z(contours_raw, 0, contours); + } + + if (expoly.holes.empty()) { + // No need to subtract holes from the offsetted expolygon, we are done. + append(out, std::move(contours)); + } else { + // 2) Offset the holes one by one, collect the offsetted holes. + ClipperLib_Z::Paths holes; + { + for (const Polygon &hole : expoly.holes) { + ClipperLib::ClipperOffset co; + if (joinType == jtRound) + co.ArcTolerance = miterLimit; + else + co.MiterLimit = miterLimit; + co.ShortestEdgeLength = double(delta * 0.005); + co.AddPath(hole.points, joinType, ClipperLib::etClosedPolygon); + ClipperLib::Paths out2; + // Execute reorients the contours so that the outer most contour has a positive area. Thus the output + // contours will be CCW oriented even though the input paths are CW oriented. + // Offset is applied after contour reorientation, thus the signum of the offset value is reversed. + co.Execute(out2, delta); + append_paths_with_z(out2, 1 + (&hole - expoly.holes.data()), holes); + } + } + + // 3) Subtract holes from the contours. + if (holes.empty()) { + // No hole remaining after an offset. Just copy the outer contour. + append(out, std::move(contours)); + } else { + // Negative offset. There is a chance, that the offsetted hole intersects the outer contour. + // Subtract the offsetted holes from the offsetted contours. + ClipperLib_Z::Clipper clipper; + clipper.ZFillFunction([](const ClipperLib_Z::IntPoint &e1bot, const ClipperLib_Z::IntPoint &e1top, const ClipperLib_Z::IntPoint &e2bot, const ClipperLib_Z::IntPoint &e2top, ClipperLib_Z::IntPoint &pt) { + //pt.z() = std::max(std::max(e1bot.z(), e1top.z()), std::max(e2bot.z(), e2top.z())); + // Just mark the intersection. + pt.z() = -1; + }); + clipper.AddPaths(contours, ClipperLib_Z::ptSubject, true); + clipper.AddPaths(holes, ClipperLib_Z::ptClip, true); + ClipperLib_Z::Paths output; + clipper.Execute(ClipperLib_Z::ctDifference, output, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero); + if (! output.empty()) { + append(out, std::move(output)); + } else { + // The offsetted holes have eaten up the offsetted outer contour. + return 0; + } + } + } + + return 1; + }; + + const double spacing = flow.scaled_spacing(); + // Clip the sheath path to avoid the extruder to get exactly on the first point of the loop. + const double clip_length = spacing * 0.15; + const double anchor_length = spacing * 6.; + ClipperLib_Z::Paths anchor_candidates; + for (ExPolygon &expoly : closing_ex(polygons, float(SCALED_EPSILON), float(SCALED_EPSILON + 0.5*flow.scaled_width()))) { + // Try to produce one more perimeter to place the seam anchor. + // First genrate a 2nd perimeter loop as a source for anchor candidates. + // The anchor candidate points are annotated with an index of the source contour or with -1 if on intersection. + anchor_candidates.clear(); + shrink_expolygon_with_contour_idx(expoly, flow.scaled_width(), DefaultJoinType, 1.2, anchor_candidates); + // Orient all contours CCW. + for (auto &path : anchor_candidates) + if (ClipperLib_Z::Area(path) < 0) + std::reverse(path.begin(), path.end()); + + // Draw the perimeters. + Polylines polylines; + polylines.reserve(expoly.holes.size() + 1); + for (size_t idx_loop = 0; idx_loop <= expoly.holes.size(); ++ idx_loop) { + // Open the loop with a seam. + const Polygon &loop = idx_loop == 0 ? expoly.contour : expoly.holes[idx_loop - 1]; + Polyline pl(loop.points); + // Orient all contours CCW. + if (loop.area() < 0) + pl.reverse(); + pl.points.emplace_back(pl.points.front()); + pl.clip_end(clip_length); + if (pl.size() < 2) + continue; + // Find the foot of the seam point on anchor_candidates. Only pick an anchor point that was created by offsetting the source contour. + ClipperLib_Z::Path *closest_contour = nullptr; + Vec2d closest_point; + int closest_point_idx = -1; + double closest_point_t; + double d2min = std::numeric_limits::max(); + Vec2d seam_pt = pl.back().cast(); + for (ClipperLib_Z::Path &path : anchor_candidates) + for (int i = 0; i < path.size(); ++ i) { + int j = next_idx_modulo(i, path); + if (path[i].z() == idx_loop || path[j].z() == idx_loop) { + Vec2d pi(path[i].x(), path[i].y()); + Vec2d pj(path[j].x(), path[j].y()); + Vec2d v = pj - pi; + Vec2d w = seam_pt - pi; + auto l2 = v.squaredNorm(); + auto t = std::clamp((l2 == 0) ? 0 : v.dot(w) / l2, 0., 1.); + if ((path[i].z() == idx_loop || t > EPSILON) && (path[j].z() == idx_loop || t < 1. - EPSILON)) { + // Closest point. + Vec2d fp = pi + v * t; + double d2 = (fp - seam_pt).squaredNorm(); + if (d2 < d2min) { + d2min = d2; + closest_contour = &path; + closest_point = fp; + closest_point_idx = i; + closest_point_t = t; + } + } + } + } + if (d2min < sqr(flow.scaled_width() * 3.)) { + // Try to cut an anchor from the closest_contour. + // Both closest_contour and pl are CCW oriented. + pl.points.emplace_back(closest_point.cast()); + const ClipperLib_Z::Path &path = *closest_contour; + double remaining_length = anchor_length - (seam_pt - closest_point).norm(); + int i = closest_point_idx; + int j = next_idx_modulo(i, *closest_contour); + Vec2d pi(path[i].x(), path[i].y()); + Vec2d pj(path[j].x(), path[j].y()); + Vec2d v = pj - pi; + double l = v.norm(); + if (remaining_length < (1. - closest_point_t) * l) { + // Just trim the current line. + pl.points.emplace_back((closest_point + v * (remaining_length / l)).cast()); + } else { + // Take the rest of the current line, continue with the other lines. + pl.points.emplace_back(path[j].x(), path[j].y()); + pi = pj; + for (i = j; path[i].z() == idx_loop && remaining_length > 0; i = j, pi = pj) { + j = next_idx_modulo(i, path); + pj = Vec2d(path[j].x(), path[j].y()); + v = pj - pi; + l = v.norm(); + if (i == closest_point_idx) { + // Back at the first segment. Most likely this should not happen and we may end the anchor. + break; + } + if (remaining_length <= l) { + pl.points.emplace_back((pi + v * (remaining_length / l)).cast()); + break; + } + pl.points.emplace_back(path[j].x(), path[j].y()); + remaining_length -= l; + } + } + } + // Start with the anchor. + pl.reverse(); + polylines.emplace_back(std::move(pl)); + } + extrusion_entities_append_paths(dst, polylines, erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), + // Disable reversal of the path, always start with the anchor, always print CCW. + false); + } +} + static inline void fill_expolygons_with_sheath_generate_paths( ExtrusionEntitiesPtr &dst, const Polygons &polygons, @@ -3294,7 +3448,12 @@ static inline void fill_expolygons_with_sheath_generate_paths( if (polygons.empty()) return; - if (! with_sheath) { + if (with_sheath) { + if (density == 0) { + tree_supports_generate_paths(dst, polygons, flow); + return; + } + } else { fill_expolygons_generate_paths(dst, closing_ex(polygons, float(SCALED_EPSILON)), filler, density, role, flow); return; } @@ -3333,9 +3492,9 @@ static inline void fill_expolygons_with_sheath_generate_paths( } // Support layers, partially processed. -struct MyLayerExtruded +struct SupportGeneratorLayerExtruded { - MyLayerExtruded& operator=(MyLayerExtruded &&rhs) { + SupportGeneratorLayerExtruded& operator=(SupportGeneratorLayerExtruded &&rhs) { this->layer = rhs.layer; this->extrusions = std::move(rhs.extrusions); m_polygons_to_extrude = std::move(rhs.m_polygons_to_extrude); @@ -3356,14 +3515,14 @@ struct MyLayerExtruded Polygons& polygons_to_extrude() { return (m_polygons_to_extrude == nullptr) ? layer->polygons : *m_polygons_to_extrude; } const Polygons& polygons_to_extrude() const { return (m_polygons_to_extrude == nullptr) ? layer->polygons : *m_polygons_to_extrude; } - bool could_merge(const MyLayerExtruded &other) const { + bool could_merge(const SupportGeneratorLayerExtruded &other) const { return ! this->empty() && ! other.empty() && std::abs(this->layer->height - other.layer->height) < EPSILON && this->layer->bridging == other.layer->bridging; } // Merge regions, perform boolean union over the merged polygons. - void merge(MyLayerExtruded &&other) { + void merge(SupportGeneratorLayerExtruded &&other) { assert(this->could_merge(other)); // 1) Merge the rest polygons to extrude, if there are any. if (other.m_polygons_to_extrude != nullptr) { @@ -3397,7 +3556,7 @@ struct MyLayerExtruded } // The source layer. It carries the height and extrusion type (bridging / non bridging, extrusion height). - PrintObjectSupportMaterial::MyLayer *layer { nullptr }; + SupportGeneratorLayer *layer { nullptr }; // Collect extrusions. They will be exported sorted by the bottom height. ExtrusionEntitiesPtr extrusions; @@ -3407,7 +3566,7 @@ private: std::unique_ptr m_polygons_to_extrude; }; -typedef std::vector MyLayerExtrudedPtrs; +typedef std::vector SupportGeneratorLayerExtrudedPtrs; struct LoopInterfaceProcessor { @@ -3426,7 +3585,7 @@ struct LoopInterfaceProcessor // Generate loop contacts at the top_contact_layer, // trim the top_contact_layer->polygons with the areas covered by the loops. - void generate(MyLayerExtruded &top_contact_layer, const Flow &interface_flow_src) const; + void generate(SupportGeneratorLayerExtruded &top_contact_layer, const Flow &interface_flow_src) const; int n_contact_loops; coordf_t circle_radius; @@ -3434,7 +3593,7 @@ struct LoopInterfaceProcessor Polygon circle; }; -void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const Flow &interface_flow_src) const +void LoopInterfaceProcessor::generate(SupportGeneratorLayerExtruded &top_contact_layer, const Flow &interface_flow_src) const { if (n_contact_loops == 0 || top_contact_layer.empty()) return; @@ -3665,9 +3824,9 @@ static std::string dbg_index_to_color(int idx) void modulate_extrusion_by_overlapping_layers( // Extrusions generated for this_layer. ExtrusionEntitiesPtr &extrusions_in_out, - const PrintObjectSupportMaterial::MyLayer &this_layer, + const SupportGeneratorLayer &this_layer, // Multiple layers overlapping with this_layer, sorted bottom up. - const PrintObjectSupportMaterial::MyLayersPtr &overlapping_layers) + const SupportGeneratorLayersPtr &overlapping_layers) { size_t n_overlapping_layers = overlapping_layers.size(); if (n_overlapping_layers == 0 || extrusions_in_out.empty()) @@ -3704,7 +3863,7 @@ void modulate_extrusion_by_overlapping_layers( ++ iRun; BoundingBox bbox; for (size_t i_overlapping_layer = 0; i_overlapping_layer < n_overlapping_layers; ++ i_overlapping_layer) { - const PrintObjectSupportMaterial::MyLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; + const SupportGeneratorLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; bbox.merge(get_extents(overlapping_layer.polygons)); } for (ExtrusionEntitiesPtr::const_iterator it = extrusions_in_out.begin(); it != extrusions_in_out.end(); ++ it) { @@ -3717,13 +3876,13 @@ void modulate_extrusion_by_overlapping_layers( // Filled polygons for the overlapping regions. svg.draw(union_ex(this_layer.polygons), dbg_index_to_color(-1), transparency); for (size_t i_overlapping_layer = 0; i_overlapping_layer < n_overlapping_layers; ++ i_overlapping_layer) { - const PrintObjectSupportMaterial::MyLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; + const SupportGeneratorLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; svg.draw(union_ex(overlapping_layer.polygons), dbg_index_to_color(int(i_overlapping_layer)), transparency); } // Contours of the overlapping regions. svg.draw(to_polylines(this_layer.polygons), dbg_index_to_color(-1), scale_(0.2)); for (size_t i_overlapping_layer = 0; i_overlapping_layer < n_overlapping_layers; ++ i_overlapping_layer) { - const PrintObjectSupportMaterial::MyLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; + const SupportGeneratorLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; svg.draw(to_polylines(overlapping_layer.polygons), dbg_index_to_color(int(i_overlapping_layer)), scale_(0.1)); } // Fill extrusion, the source. @@ -3765,7 +3924,7 @@ void modulate_extrusion_by_overlapping_layers( // Fragment the path segments by overlapping layers. The overlapping layers are sorted by an increasing print_z. // Trim by the highest overlapping layer first. for (int i_overlapping_layer = int(n_overlapping_layers) - 1; i_overlapping_layer >= 0; -- i_overlapping_layer) { - const PrintObjectSupportMaterial::MyLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; + const SupportGeneratorLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; ExtrusionPathFragment &frag = path_fragments[i_overlapping_layer]; Polygons polygons_trimming = offset(union_ex(overlapping_layer.polygons), float(scale_(0.5*extrusion_width))); frag.polylines = intersection_pl(path_fragments.back().polylines, polygons_trimming); @@ -3856,7 +4015,7 @@ void modulate_extrusion_by_overlapping_layers( ExtrusionPath *path = multipath.paths.empty() ? nullptr : &multipath.paths.back(); if (path != nullptr) { // Verify whether the path is compatible with the current fragment. - assert(this_layer.layer_type == PrintObjectSupportMaterial::sltBottomContact || path->height != frag.height || path->mm3_per_mm != frag.mm3_per_mm); + assert(this_layer.layer_type == SupporLayerType::BottomContact || path->height != frag.height || path->mm3_per_mm != frag.mm3_per_mm); if (path->height != frag.height || path->mm3_per_mm != frag.mm3_per_mm) { path = nullptr; } @@ -3903,22 +4062,102 @@ void modulate_extrusion_by_overlapping_layers( extrusion_entities_append_paths(extrusions_in_out, std::move(it_fragment->polylines), extrusion_role, it_fragment->mm3_per_mm, it_fragment->width, it_fragment->height); } -void PrintObjectSupportMaterial::generate_toolpaths( - SupportLayerPtrs &support_layers, - const MyLayersPtr &raft_layers, - const MyLayersPtr &bottom_contacts, - const MyLayersPtr &top_contacts, - const MyLayersPtr &intermediate_layers, - const MyLayersPtr &interface_layers, - const MyLayersPtr &base_interface_layers) const +SupportGeneratorLayersPtr generate_support_layers( + PrintObject &object, + const SupportGeneratorLayersPtr &raft_layers, + const SupportGeneratorLayersPtr &bottom_contacts, + const SupportGeneratorLayersPtr &top_contacts, + const SupportGeneratorLayersPtr &intermediate_layers, + const SupportGeneratorLayersPtr &interface_layers, + const SupportGeneratorLayersPtr &base_interface_layers) +{ + // Install support layers into the object. + // A support layer installed on a PrintObject has a unique print_z. + SupportGeneratorLayersPtr layers_sorted; + layers_sorted.reserve(raft_layers.size() + bottom_contacts.size() + top_contacts.size() + intermediate_layers.size() + interface_layers.size() + base_interface_layers.size()); + layers_append(layers_sorted, raft_layers); + layers_append(layers_sorted, bottom_contacts); + layers_append(layers_sorted, top_contacts); + layers_append(layers_sorted, intermediate_layers); + layers_append(layers_sorted, interface_layers); + layers_append(layers_sorted, base_interface_layers); + // Sort the layers lexicographically by a raising print_z and a decreasing height. + std::sort(layers_sorted.begin(), layers_sorted.end(), [](auto *l1, auto *l2) { return *l1 < *l2; }); + int layer_id = 0; + int layer_id_interface = 0; + assert(object.support_layers().empty()); + for (size_t i = 0; i < layers_sorted.size();) { + // Find the last layer with roughly the same print_z, find the minimum layer height of all. + // Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should. + size_t j = i + 1; + coordf_t zmax = layers_sorted[i]->print_z + EPSILON; + for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j) ; + // Assign an average print_z to the set of layers with nearly equal print_z. + coordf_t zavg = 0.5 * (layers_sorted[i]->print_z + layers_sorted[j - 1]->print_z); + coordf_t height_min = layers_sorted[i]->height; + bool empty = true; + // For snug supports, layers where the direction of the support interface shall change are accounted for. + size_t num_interfaces = 0; + size_t num_top_contacts = 0; + double top_contact_bottom_z = 0; + for (size_t u = i; u < j; ++u) { + SupportGeneratorLayer &layer = *layers_sorted[u]; + if (! layer.polygons.empty()) { + empty = false; + num_interfaces += one_of(layer.layer_type, support_types_interface); + if (layer.layer_type == SupporLayerType::TopContact) { + ++ num_top_contacts; + assert(num_top_contacts <= 1); + // All top contact layers sharing this print_z shall also share bottom_z. + //assert(num_top_contacts == 1 || (top_contact_bottom_z - layer.bottom_z) < EPSILON); + top_contact_bottom_z = layer.bottom_z; + } + } + layer.print_z = zavg; + height_min = std::min(height_min, layer.height); + } + if (! empty) { + // Here the upper_layer and lower_layer pointers are left to null at the support layers, + // as they are never used. These pointers are candidates for removal. + bool this_layer_contacts_only = num_top_contacts > 0 && num_top_contacts == num_interfaces; + size_t this_layer_id_interface = layer_id_interface; + if (this_layer_contacts_only) { + // Find a supporting layer for its interface ID. + for (auto it = object.support_layers().rbegin(); it != object.support_layers().rend(); ++ it) + if (const SupportLayer &other_layer = **it; std::abs(other_layer.print_z - top_contact_bottom_z) < EPSILON) { + // other_layer supports this top contact layer. Assign a different support interface direction to this layer + // from the layer that supports it. + this_layer_id_interface = other_layer.interface_id() + 1; + } + } + object.add_support_layer(layer_id ++, this_layer_id_interface, height_min, zavg); + if (num_interfaces && ! this_layer_contacts_only) + ++ layer_id_interface; + } + i = j; + } + return layers_sorted; +} + +void generate_support_toolpaths( + SupportLayerPtrs &support_layers, + const PrintObjectConfig &config, + const SupportParameters &support_params, + const SlicingParameters &slicing_params, + const SupportGeneratorLayersPtr &raft_layers, + const SupportGeneratorLayersPtr &bottom_contacts, + const SupportGeneratorLayersPtr &top_contacts, + const SupportGeneratorLayersPtr &intermediate_layers, + const SupportGeneratorLayersPtr &interface_layers, + const SupportGeneratorLayersPtr &base_interface_layers) { // loop_interface_processor with a given circle radius. - LoopInterfaceProcessor loop_interface_processor(1.5 * m_support_params.support_material_interface_flow.scaled_width()); - loop_interface_processor.n_contact_loops = this->has_contact_loops() ? 1 : 0; + LoopInterfaceProcessor loop_interface_processor(1.5 * support_params.support_material_interface_flow.scaled_width()); + loop_interface_processor.n_contact_loops = config.support_material_interface_contact_loops ? 1 : 0; - std::vector angles { m_support_params.base_angle }; - if (m_object_config->support_material_pattern == smpRectilinearGrid) - angles.push_back(m_support_params.interface_angle); + std::vector angles { support_params.base_angle }; + if (config.support_material_pattern == smpRectilinearGrid) + angles.push_back(support_params.interface_angle); BoundingBox bbox_object(Point(-scale_(1.), -scale_(1.0)), Point(scale_(1.), scale_(1.))); @@ -3928,34 +4167,34 @@ void PrintObjectSupportMaterial::generate_toolpaths( float raft_angle_1st_layer = 0.f; float raft_angle_base = 0.f; float raft_angle_interface = 0.f; - if (m_slicing_params.base_raft_layers > 1) { + if (slicing_params.base_raft_layers > 1) { // There are all raft layer types (1st layer, base, interface & contact layers) available. - raft_angle_1st_layer = m_support_params.interface_angle; - raft_angle_base = m_support_params.base_angle; - raft_angle_interface = m_support_params.interface_angle; - } else if (m_slicing_params.base_raft_layers == 1 || m_slicing_params.interface_raft_layers > 1) { + raft_angle_1st_layer = support_params.interface_angle; + raft_angle_base = support_params.base_angle; + raft_angle_interface = support_params.interface_angle; + } else if (slicing_params.base_raft_layers == 1 || slicing_params.interface_raft_layers > 1) { // 1st layer, interface & contact layers available. - raft_angle_1st_layer = m_support_params.base_angle; - if (this->has_support()) + raft_angle_1st_layer = support_params.base_angle; + if (config.support_material || config.support_material_enforce_layers > 0) // Print 1st layer at 45 degrees from both the interface and base angles as both can land on the 1st layer. raft_angle_1st_layer += 0.7854f; - raft_angle_interface = m_support_params.interface_angle; - } else if (m_slicing_params.interface_raft_layers == 1) { + raft_angle_interface = support_params.interface_angle; + } else if (slicing_params.interface_raft_layers == 1) { // Only the contact raft layer is non-empty, which will be printed as the 1st layer. - assert(m_slicing_params.base_raft_layers == 0); - assert(m_slicing_params.interface_raft_layers == 1); - assert(m_slicing_params.raft_layers() == 1 && raft_layers.size() == 0); + assert(slicing_params.base_raft_layers == 0); + assert(slicing_params.interface_raft_layers == 1); + assert(slicing_params.raft_layers() == 1 && raft_layers.size() == 0); } else { // No raft. - assert(m_slicing_params.base_raft_layers == 0); - assert(m_slicing_params.interface_raft_layers == 0); - assert(m_slicing_params.raft_layers() == 0 && raft_layers.size() == 0); + assert(slicing_params.base_raft_layers == 0); + assert(slicing_params.interface_raft_layers == 0); + assert(slicing_params.raft_layers() == 0 && raft_layers.size() == 0); } // Insert the raft base layers. - size_t n_raft_layers = size_t(std::max(0, int(m_slicing_params.raft_layers()) - 1)); + size_t n_raft_layers = size_t(std::max(0, int(slicing_params.raft_layers()) - 1)); tbb::parallel_for(tbb::blocked_range(0, n_raft_layers), - [this, &support_layers, &raft_layers, + [&support_layers, &raft_layers, &config, &support_params, &slicing_params, &bbox_object, raft_angle_1st_layer, raft_angle_base, raft_angle_interface, link_max_length_factor] (const tbb::blocked_range& range) { for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) @@ -3963,55 +4202,55 @@ void PrintObjectSupportMaterial::generate_toolpaths( assert(support_layer_id < raft_layers.size()); SupportLayer &support_layer = *support_layers[support_layer_id]; assert(support_layer.support_fills.entities.empty()); - MyLayer &raft_layer = *raft_layers[support_layer_id]; + SupportGeneratorLayer &raft_layer = *raft_layers[support_layer_id]; - std::unique_ptr filler_interface = std::unique_ptr(Fill::new_from_type(m_support_params.interface_fill_pattern)); - std::unique_ptr filler_support = std::unique_ptr(Fill::new_from_type(m_support_params.base_fill_pattern)); + std::unique_ptr filler_interface = std::unique_ptr(Fill::new_from_type(support_params.interface_fill_pattern)); + std::unique_ptr filler_support = std::unique_ptr(Fill::new_from_type(support_params.base_fill_pattern)); filler_interface->set_bounding_box(bbox_object); filler_support->set_bounding_box(bbox_object); // Print the support base below the support columns, or the support base for the support columns plus the contacts. if (support_layer_id > 0) { - const Polygons &to_infill_polygons = (support_layer_id < m_slicing_params.base_raft_layers) ? + const Polygons &to_infill_polygons = (support_layer_id < slicing_params.base_raft_layers) ? raft_layer.polygons : //FIXME misusing contact_polygons for support columns. ((raft_layer.contact_polygons == nullptr) ? Polygons() : *raft_layer.contact_polygons); if (! to_infill_polygons.empty()) { assert(! raft_layer.bridging); - Flow flow(float(m_support_params.support_material_flow.width()), float(raft_layer.height), m_support_params.support_material_flow.nozzle_diameter()); + Flow flow(float(support_params.support_material_flow.width()), float(raft_layer.height), support_params.support_material_flow.nozzle_diameter()); Fill * filler = filler_support.get(); filler->angle = raft_angle_base; - filler->spacing = m_support_params.support_material_flow.spacing(); - filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / m_support_params.support_density)); + filler->spacing = support_params.support_material_flow.spacing(); + filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_params.support_density)); fill_expolygons_with_sheath_generate_paths( // Destination support_layer.support_fills.entities, // Regions to fill to_infill_polygons, // Filler and its parameters - filler, float(m_support_params.support_density), + filler, float(support_params.support_density), // Extrusion parameters erSupportMaterial, flow, - m_support_params.with_sheath, false); + support_params.with_sheath, false); } } Fill *filler = filler_interface.get(); - Flow flow = m_support_params.first_layer_flow; + Flow flow = support_params.first_layer_flow; float density = 0.f; if (support_layer_id == 0) { // Base flange. filler->angle = raft_angle_1st_layer; - filler->spacing = m_support_params.first_layer_flow.spacing(); - density = float(m_object_config->raft_first_layer_density.value * 0.01); - } else if (support_layer_id >= m_slicing_params.base_raft_layers) { + filler->spacing = support_params.first_layer_flow.spacing(); + density = float(config.raft_first_layer_density.value * 0.01); + } else if (support_layer_id >= slicing_params.base_raft_layers) { filler->angle = raft_angle_interface; // We don't use $base_flow->spacing because we need a constant spacing // value that guarantees that all layers are correctly aligned. - filler->spacing = m_support_params.support_material_flow.spacing(); + filler->spacing = support_params.support_material_flow.spacing(); assert(! raft_layer.bridging); - flow = Flow(float(m_support_params.support_material_interface_flow.width()), float(raft_layer.height), m_support_params.support_material_flow.nozzle_diameter()); - density = float(m_support_params.interface_density); + flow = Flow(float(support_params.support_material_interface_flow.width()), float(raft_layer.height), support_params.support_material_flow.nozzle_diameter()); + density = float(support_params.interface_density); } else continue; filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); @@ -4023,27 +4262,27 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Filler and its parameters filler, density, // Extrusion parameters - (support_layer_id < m_slicing_params.base_raft_layers) ? erSupportMaterial : erSupportMaterialInterface, flow, + (support_layer_id < slicing_params.base_raft_layers) ? erSupportMaterial : erSupportMaterialInterface, flow, // sheath at first layer support_layer_id == 0, support_layer_id == 0); } }); struct LayerCacheItem { - LayerCacheItem(MyLayerExtruded *layer_extruded = nullptr) : layer_extruded(layer_extruded) {} - MyLayerExtruded *layer_extruded; - std::vector overlapping; + LayerCacheItem(SupportGeneratorLayerExtruded *layer_extruded = nullptr) : layer_extruded(layer_extruded) {} + SupportGeneratorLayerExtruded *layer_extruded; + std::vector overlapping; }; struct LayerCache { - MyLayerExtruded bottom_contact_layer; - MyLayerExtruded top_contact_layer; - MyLayerExtruded base_layer; - MyLayerExtruded interface_layer; - MyLayerExtruded base_interface_layer; + SupportGeneratorLayerExtruded bottom_contact_layer; + SupportGeneratorLayerExtruded top_contact_layer; + SupportGeneratorLayerExtruded base_layer; + SupportGeneratorLayerExtruded interface_layer; + SupportGeneratorLayerExtruded base_interface_layer; boost::container::static_vector nonempty; void add_nonempty_and_sort() { - for (MyLayerExtruded *item : { &bottom_contact_layer, &top_contact_layer, &interface_layer, &base_interface_layer, &base_layer }) + for (SupportGeneratorLayerExtruded *item : { &bottom_contact_layer, &top_contact_layer, &interface_layer, &base_interface_layer, &base_layer }) if (! item->empty()) this->nonempty.emplace_back(item); // Sort the layers with the same print_z coordinate by their heights, thickest first. @@ -4053,7 +4292,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( std::vector layer_caches(support_layers.size()); tbb::parallel_for(tbb::blocked_range(n_raft_layers, support_layers.size()), - [this, &support_layers, &bottom_contacts, &top_contacts, &intermediate_layers, &interface_layers, &base_interface_layers, &layer_caches, &loop_interface_processor, + [&config, &support_params, &support_layers, &bottom_contacts, &top_contacts, &intermediate_layers, &interface_layers, &base_interface_layers, &layer_caches, &loop_interface_processor, &bbox_object, &angles, link_max_length_factor] (const tbb::blocked_range& range) { // Indices of the 1st layer in their respective container at the support layer height. @@ -4063,15 +4302,15 @@ void PrintObjectSupportMaterial::generate_toolpaths( size_t idx_layer_interface = size_t(-1); size_t idx_layer_base_interface = size_t(-1); const auto fill_type_first_layer = ipRectilinear; - auto filler_interface = std::unique_ptr(Fill::new_from_type(m_support_params.contact_fill_pattern)); + auto filler_interface = std::unique_ptr(Fill::new_from_type(support_params.contact_fill_pattern)); // Filler for the 1st layer interface, if different from filler_interface. - auto filler_first_layer_ptr = std::unique_ptr(range.begin() == 0 && m_support_params.contact_fill_pattern != fill_type_first_layer ? Fill::new_from_type(fill_type_first_layer) : nullptr); + auto filler_first_layer_ptr = std::unique_ptr(range.begin() == 0 && support_params.contact_fill_pattern != fill_type_first_layer ? Fill::new_from_type(fill_type_first_layer) : nullptr); // Pointer to the 1st layer interface filler. auto filler_first_layer = filler_first_layer_ptr ? filler_first_layer_ptr.get() : filler_interface.get(); // Filler for the base interface (to be used for soluble interface / non soluble base, to produce non soluble interface layer below soluble interface layer). auto filler_base_interface = std::unique_ptr(base_interface_layers.empty() ? nullptr : - Fill::new_from_type(m_support_params.interface_density > 0.95 || m_support_params.with_sheath ? ipRectilinear : ipSupportBase)); - auto filler_support = std::unique_ptr(Fill::new_from_type(m_support_params.base_fill_pattern)); + Fill::new_from_type(support_params.interface_density > 0.95 || support_params.with_sheath ? ipRectilinear : ipSupportBase)); + auto filler_support = std::unique_ptr(Fill::new_from_type(support_params.base_fill_pattern)); filler_interface->set_bounding_box(bbox_object); if (filler_first_layer_ptr) filler_first_layer_ptr->set_bounding_box(bbox_object); @@ -4082,19 +4321,19 @@ void PrintObjectSupportMaterial::generate_toolpaths( { SupportLayer &support_layer = *support_layers[support_layer_id]; LayerCache &layer_cache = layer_caches[support_layer_id]; - float interface_angle_delta = m_object_config->support_material_style.value == smsSnug ? + float interface_angle_delta = config.support_material_style.value == smsSnug || config.support_material_style.value == smsTree ? (support_layer.interface_id() & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.) : 0; // Find polygons with the same print_z. - MyLayerExtruded &bottom_contact_layer = layer_cache.bottom_contact_layer; - MyLayerExtruded &top_contact_layer = layer_cache.top_contact_layer; - MyLayerExtruded &base_layer = layer_cache.base_layer; - MyLayerExtruded &interface_layer = layer_cache.interface_layer; - MyLayerExtruded &base_interface_layer = layer_cache.base_interface_layer; + SupportGeneratorLayerExtruded &bottom_contact_layer = layer_cache.bottom_contact_layer; + SupportGeneratorLayerExtruded &top_contact_layer = layer_cache.top_contact_layer; + SupportGeneratorLayerExtruded &base_layer = layer_cache.base_layer; + SupportGeneratorLayerExtruded &interface_layer = layer_cache.interface_layer; + SupportGeneratorLayerExtruded &base_interface_layer = layer_cache.base_interface_layer; // Increment the layer indices to find a layer at support_layer.print_z. { - auto fun = [&support_layer](const MyLayer *l){ return l->print_z >= support_layer.print_z - EPSILON; }; + auto fun = [&support_layer](const SupportGeneratorLayer *l){ return l->print_z >= support_layer.print_z - EPSILON; }; idx_layer_bottom_contact = idx_higher_or_equal(bottom_contacts, idx_layer_bottom_contact, fun); idx_layer_top_contact = idx_higher_or_equal(top_contacts, idx_layer_top_contact, fun); idx_layer_intermediate = idx_higher_or_equal(intermediate_layers, idx_layer_intermediate, fun); @@ -4113,23 +4352,23 @@ void PrintObjectSupportMaterial::generate_toolpaths( if (idx_layer_intermediate < intermediate_layers.size() && intermediate_layers[idx_layer_intermediate]->print_z < support_layer.print_z + EPSILON) base_layer.layer = intermediate_layers[idx_layer_intermediate]; - if (m_object_config->support_material_interface_layers == 0) { + if (config.support_material_interface_layers == 0) { // If no top interface layers were requested, we treat the contact layer exactly as a generic base layer. - if (m_support_params.can_merge_support_regions) { + if (support_params.can_merge_support_regions) { if (base_layer.could_merge(top_contact_layer)) base_layer.merge(std::move(top_contact_layer)); else if (base_layer.empty()) base_layer = std::move(top_contact_layer); } } else { - loop_interface_processor.generate(top_contact_layer, m_support_params.support_material_interface_flow); + loop_interface_processor.generate(top_contact_layer, support_params.support_material_interface_flow); // If no loops are allowed, we treat the contact layer exactly as a generic interface layer. // Merge interface_layer into top_contact_layer, as the top_contact_layer is not synchronized and therefore it will be used // to trim other layers. if (top_contact_layer.could_merge(interface_layer)) top_contact_layer.merge(std::move(interface_layer)); } - if ((m_object_config->support_material_interface_layers == 0 || m_object_config->support_material_bottom_interface_layers == 0) && m_support_params.can_merge_support_regions) { + if ((config.support_material_interface_layers == 0 || config.support_material_bottom_interface_layers == 0) && support_params.can_merge_support_regions) { if (base_layer.could_merge(bottom_contact_layer)) base_layer.merge(std::move(bottom_contact_layer)); else if (base_layer.empty() && ! bottom_contact_layer.empty() && ! bottom_contact_layer.layer->bridging) @@ -4153,23 +4392,23 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Top and bottom contacts, interface layers. for (size_t i = 0; i < 3; ++ i) { - MyLayerExtruded &layer_ex = (i == 0) ? top_contact_layer : (i == 1 ? bottom_contact_layer : interface_layer); + SupportGeneratorLayerExtruded &layer_ex = (i == 0) ? top_contact_layer : (i == 1 ? bottom_contact_layer : interface_layer); if (layer_ex.empty() || layer_ex.polygons_to_extrude().empty()) continue; - bool interface_as_base = m_object_config->support_material_interface_layers.value == 0 || - (m_object_config->support_material_bottom_interface_layers == 0 && &layer_ex == &bottom_contact_layer); + bool interface_as_base = config.support_material_interface_layers.value == 0 || + (config.support_material_bottom_interface_layers == 0 && &layer_ex == &bottom_contact_layer); //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b) auto interface_flow = layer_ex.layer->bridging ? - Flow::bridging_flow(layer_ex.layer->height, m_support_params.support_material_bottom_interface_flow.nozzle_diameter()) : - (interface_as_base ? &m_support_params.support_material_flow : &m_support_params.support_material_interface_flow)->with_height(float(layer_ex.layer->height)); + Flow::bridging_flow(layer_ex.layer->height, support_params.support_material_bottom_interface_flow.nozzle_diameter()) : + (interface_as_base ? &support_params.support_material_flow : &support_params.support_material_interface_flow)->with_height(float(layer_ex.layer->height)); filler_interface->angle = interface_as_base ? // If zero interface layers are configured, use the same angle as for the base layers. angles[support_layer_id % angles.size()] : // Use interface angle for the interface layers. - m_support_params.interface_angle + interface_angle_delta; - double density = interface_as_base ? m_support_params.support_density : m_support_params.interface_density; - filler_interface->spacing = interface_as_base ? m_support_params.support_material_flow.spacing() : m_support_params.support_material_interface_flow.spacing(); + support_params.interface_angle + interface_angle_delta; + double density = interface_as_base ? support_params.support_density : support_params.interface_density; + filler_interface->spacing = interface_as_base ? support_params.support_material_flow.spacing() : support_params.support_material_interface_flow.spacing(); filler_interface->link_max_length = coord_t(scale_(filler_interface->spacing * link_max_length_factor / density)); fill_expolygons_generate_paths( // Destination @@ -4188,10 +4427,10 @@ void PrintObjectSupportMaterial::generate_toolpaths( //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b) assert(! base_interface_layer.layer->bridging); - Flow interface_flow = m_support_params.support_material_flow.with_height(float(base_interface_layer.layer->height)); - filler->angle = m_support_params.interface_angle + interface_angle_delta; - filler->spacing = m_support_params.support_material_interface_flow.spacing(); - filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / m_support_params.interface_density)); + Flow interface_flow = support_params.support_material_flow.with_height(float(base_interface_layer.layer->height)); + filler->angle = support_params.interface_angle + interface_angle_delta; + filler->spacing = support_params.support_material_interface_flow.spacing(); + filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_params.interface_density)); fill_expolygons_generate_paths( // Destination base_interface_layer.extrusions, @@ -4199,7 +4438,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Regions to fill union_safety_offset_ex(base_interface_layer.polygons_to_extrude()), // Filler and its parameters - filler, float(m_support_params.interface_density), + filler, float(support_params.interface_density), // Extrusion parameters erSupportMaterial, interface_flow); } @@ -4211,18 +4450,18 @@ void PrintObjectSupportMaterial::generate_toolpaths( // We don't use $base_flow->spacing because we need a constant spacing // value that guarantees that all layers are correctly aligned. assert(! base_layer.layer->bridging); - auto flow = m_support_params.support_material_flow.with_height(float(base_layer.layer->height)); - filler->spacing = m_support_params.support_material_flow.spacing(); - filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / m_support_params.support_density)); - float density = float(m_support_params.support_density); - bool sheath = m_support_params.with_sheath; + auto flow = support_params.support_material_flow.with_height(float(base_layer.layer->height)); + filler->spacing = support_params.support_material_flow.spacing(); + filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_params.support_density)); + float density = float(support_params.support_density); + bool sheath = support_params.with_sheath; bool no_sort = false; if (base_layer.layer->bottom_z < EPSILON) { // Base flange (the 1st layer). filler = filler_first_layer; - filler->angle = Geometry::deg2rad(float(m_object_config->support_material_angle.value + 90.)); - density = float(m_object_config->raft_first_layer_density.value * 0.01); - flow = m_support_params.first_layer_flow; + filler->angle = Geometry::deg2rad(float(config.support_material_angle.value + 90.)); + density = float(config.raft_first_layer_density.value * 0.01); + flow = support_params.first_layer_flow; // use the proper spacing for first layer as we don't need to align // its pattern to the other layers //FIXME When paralellizing, each thread shall have its own copy of the fillers. @@ -4269,12 +4508,12 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Collect overlapping top/bottom surfaces. layer_cache_item.overlapping.reserve(20); coordf_t bottom_z = layer_cache_item.layer_extruded->layer->bottom_print_z() + EPSILON; - auto add_overlapping = [&layer_cache_item, bottom_z](const MyLayersPtr &layers, size_t idx_top) { + auto add_overlapping = [&layer_cache_item, bottom_z](const SupportGeneratorLayersPtr &layers, size_t idx_top) { for (int i = int(idx_top) - 1; i >= 0 && layers[i]->print_z > bottom_z; -- i) layer_cache_item.overlapping.push_back(layers[i]); }; add_overlapping(top_contacts, idx_layer_top_contact); - if (layer_cache_item.layer_extruded->layer->layer_type == sltBottomContact) { + if (layer_cache_item.layer_extruded->layer->layer_type == SupporLayerType::BottomContact) { // Bottom contact layer may overlap with a base layer, which may be changed to interface layer. add_overlapping(intermediate_layers, idx_layer_intermediate); add_overlapping(interface_layers, idx_layer_interface); @@ -4283,8 +4522,13 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Order the layers by lexicographically by an increasing print_z and a decreasing layer height. std::stable_sort(layer_cache_item.overlapping.begin(), layer_cache_item.overlapping.end(), [](auto *l1, auto *l2) { return *l1 < *l2; }); } - if (! polys.empty()) - expolygons_append(support_layer.support_islands, union_ex(polys)); + assert(support_layer.support_islands.empty()); + if (! polys.empty()) { + support_layer.support_islands = union_ex(polys); + support_layer.support_islands_bboxes.reserve(support_layer.support_islands.size()); + for (const ExPolygon &expoly : support_layer.support_islands) + support_layer.support_islands_bboxes.emplace_back(get_extents(expoly).inflated(SCALED_EPSILON)); + } } // for each support_layer_id }); @@ -4322,7 +4566,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( } }; for (const SupportLayer *support_layer : support_layers) - assert(Test::verify_nonempty(&support_layer->support_fills)); + assert(Test::verify_nonempty(&support_layer->support_fills)); #endif // NDEBUG } diff --git a/src/libslic3r/SupportMaterial.hpp b/src/libslic3r/SupportMaterial.hpp index 65604ef72..b578adb30 100644 --- a/src/libslic3r/SupportMaterial.hpp +++ b/src/libslic3r/SupportMaterial.hpp @@ -11,147 +11,187 @@ class PrintObject; class PrintConfig; class PrintObjectConfig; +// Support layer type to be used by SupportGeneratorLayer. This type carries a much more detailed information +// about the support layer type than the final support layers stored in a PrintObject. +enum SupporLayerType { + Unknown = 0, + // Ratft base layer, to be printed with the support material. + RaftBase, + // Raft interface layer, to be printed with the support interface material. + RaftInterface, + // Bottom contact layer placed over a top surface of an object. To be printed with a support interface material. + BottomContact, + // Dense interface layer, to be printed with the support interface material. + // This layer is separated from an object by an BottomContact layer. + BottomInterface, + // Sparse base support layer, to be printed with a support material. + Base, + // Dense interface layer, to be printed with the support interface material. + // This layer is separated from an object with TopContact layer. + TopInterface, + // Top contact layer directly supporting an overhang. To be printed with a support interface material. + TopContact, + // Some undecided type yet. It will turn into Base first, then it may turn into BottomInterface or TopInterface. + Intermediate, +}; + +// A support layer type used internally by the SupportMaterial class. This class carries a much more detailed +// information about the support layer than the layers stored in the PrintObject, mainly +// the SupportGeneratorLayer is aware of the bridging flow and the interface gaps between the object and the support. +class SupportGeneratorLayer +{ +public: + void reset() { + *this = SupportGeneratorLayer(); + } + + bool operator==(const SupportGeneratorLayer &layer2) const { + return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging; + } + + // Order the layers by lexicographically by an increasing print_z and a decreasing layer height. + bool operator<(const SupportGeneratorLayer &layer2) const { + if (print_z < layer2.print_z) { + return true; + } else if (print_z == layer2.print_z) { + if (height > layer2.height) + return true; + else if (height == layer2.height) { + // Bridging layers first. + return bridging && ! layer2.bridging; + } else + return false; + } else + return false; + } + + void merge(SupportGeneratorLayer &&rhs) { + // The union_() does not support move semantic yet, but maybe one day it will. + this->polygons = union_(this->polygons, std::move(rhs.polygons)); + auto merge = [](std::unique_ptr &dst, std::unique_ptr &src) { + if (! dst || dst->empty()) + dst = std::move(src); + else if (src && ! src->empty()) + *dst = union_(*dst, std::move(*src)); + }; + merge(this->contact_polygons, rhs.contact_polygons); + merge(this->overhang_polygons, rhs.overhang_polygons); + merge(this->enforcer_polygons, rhs.enforcer_polygons); + rhs.reset(); + } + + // For the bridging flow, bottom_print_z will be above bottom_z to account for the vertical separation. + // For the non-bridging flow, bottom_print_z will be equal to bottom_z. + coordf_t bottom_print_z() const { return print_z - height; } + + // To sort the extremes of top / bottom interface layers. + coordf_t extreme_z() const { return (this->layer_type == SupporLayerType::TopContact) ? this->bottom_z : this->print_z; } + + SupporLayerType layer_type { SupporLayerType::Unknown }; + // Z used for printing, in unscaled coordinates. + coordf_t print_z { 0 }; + // Bottom Z of this layer. For soluble layers, bottom_z + height = print_z, + // otherwise bottom_z + gap + height = print_z. + coordf_t bottom_z { 0 }; + // Layer height in unscaled coordinates. + coordf_t height { 0 }; + // Index of a PrintObject layer_id supported by this layer. This will be set for top contact layers. + // If this is not a contact layer, it will be set to size_t(-1). + size_t idx_object_layer_above { size_t(-1) }; + // Index of a PrintObject layer_id, which supports this layer. This will be set for bottom contact layers. + // If this is not a contact layer, it will be set to size_t(-1). + size_t idx_object_layer_below { size_t(-1) }; + // Use a bridging flow when printing this support layer. + bool bridging { false }; + + // Polygons to be filled by the support pattern. + Polygons polygons; + // Currently for the contact layers only. + std::unique_ptr contact_polygons; + std::unique_ptr overhang_polygons; + // Enforcers need to be propagated independently in case the "support on build plate only" option is enabled. + std::unique_ptr enforcer_polygons; +}; + +// Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained +// up to the end of a generate() method. The layer storage may be replaced by an allocator class in the future, +// which would allocate layers by multiple chunks. +using SupportGeneratorLayerStorage = std::deque; +using SupportGeneratorLayersPtr = std::vector; + +struct SupportParameters { + SupportParameters(const PrintObject &object); + + Flow first_layer_flow; + Flow support_material_flow; + Flow support_material_interface_flow; + Flow support_material_bottom_interface_flow; + // Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder? + bool can_merge_support_regions; + + coordf_t support_layer_height_min; +// coordf_t support_layer_height_max; + + coordf_t gap_xy; + + float base_angle; + float interface_angle; + coordf_t interface_spacing; + coordf_t interface_density; + coordf_t support_spacing; + coordf_t support_density; + + InfillPattern base_fill_pattern; + InfillPattern interface_fill_pattern; + InfillPattern contact_fill_pattern; + bool with_sheath; +}; + +// Generate raft layers, also expand the 1st support layer +// in case there is no raft layer to improve support adhesion. +SupportGeneratorLayersPtr generate_raft_base( + const PrintObject &object, + const SupportParameters &support_params, + const SlicingParameters &slicing_params, + const SupportGeneratorLayersPtr &top_contacts, + const SupportGeneratorLayersPtr &interface_layers, + const SupportGeneratorLayersPtr &base_interface_layers, + const SupportGeneratorLayersPtr &base_layers, + SupportGeneratorLayerStorage &layer_storage); + +// returns sorted layers +SupportGeneratorLayersPtr generate_support_layers( + PrintObject &object, + const SupportGeneratorLayersPtr &raft_layers, + const SupportGeneratorLayersPtr &bottom_contacts, + const SupportGeneratorLayersPtr &top_contacts, + const SupportGeneratorLayersPtr &intermediate_layers, + const SupportGeneratorLayersPtr &interface_layers, + const SupportGeneratorLayersPtr &base_interface_layers); + +// Produce the support G-code. +// Used by both classic and tree supports. +void generate_support_toolpaths( + SupportLayerPtrs &support_layers, + const PrintObjectConfig &config, + const SupportParameters &support_params, + const SlicingParameters &slicing_params, + const SupportGeneratorLayersPtr &raft_layers, + const SupportGeneratorLayersPtr &bottom_contacts, + const SupportGeneratorLayersPtr &top_contacts, + const SupportGeneratorLayersPtr &intermediate_layers, + const SupportGeneratorLayersPtr &interface_layers, + const SupportGeneratorLayersPtr &base_interface_layers); + +void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers); +void export_print_z_polygons_and_extrusions_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers, SupportLayer& support_layer); + // This class manages raft and supports for a single PrintObject. // Instantiated by Slic3r::Print::Object->_support_material() // This class is instantiated before the slicing starts as Object.pm will query // the parameters of the raft to determine the 1st layer height and thickness. class PrintObjectSupportMaterial { -public: - // Support layer type to be used by MyLayer. This type carries a much more detailed information - // about the support layer type than the final support layers stored in a PrintObject. - enum SupporLayerType { - sltUnknown = 0, - // Ratft base layer, to be printed with the support material. - sltRaftBase, - // Raft interface layer, to be printed with the support interface material. - sltRaftInterface, - // Bottom contact layer placed over a top surface of an object. To be printed with a support interface material. - sltBottomContact, - // Dense interface layer, to be printed with the support interface material. - // This layer is separated from an object by an sltBottomContact layer. - sltBottomInterface, - // Sparse base support layer, to be printed with a support material. - sltBase, - // Dense interface layer, to be printed with the support interface material. - // This layer is separated from an object with sltTopContact layer. - sltTopInterface, - // Top contact layer directly supporting an overhang. To be printed with a support interface material. - sltTopContact, - // Some undecided type yet. It will turn into sltBase first, then it may turn into sltBottomInterface or sltTopInterface. - sltIntermediate, - }; - - // A support layer type used internally by the SupportMaterial class. This class carries a much more detailed - // information about the support layer than the layers stored in the PrintObject, mainly - // the MyLayer is aware of the bridging flow and the interface gaps between the object and the support. - class MyLayer - { - public: - void reset() { - *this = MyLayer(); - } - - bool operator==(const MyLayer &layer2) const { - return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging; - } - - // Order the layers by lexicographically by an increasing print_z and a decreasing layer height. - bool operator<(const MyLayer &layer2) const { - if (print_z < layer2.print_z) { - return true; - } else if (print_z == layer2.print_z) { - if (height > layer2.height) - return true; - else if (height == layer2.height) { - // Bridging layers first. - return bridging && ! layer2.bridging; - } else - return false; - } else - return false; - } - - void merge(MyLayer &&rhs) { - // The union_() does not support move semantic yet, but maybe one day it will. - this->polygons = union_(this->polygons, std::move(rhs.polygons)); - auto merge = [](std::unique_ptr &dst, std::unique_ptr &src) { - if (! dst || dst->empty()) - dst = std::move(src); - else if (src && ! src->empty()) - *dst = union_(*dst, std::move(*src)); - }; - merge(this->contact_polygons, rhs.contact_polygons); - merge(this->overhang_polygons, rhs.overhang_polygons); - merge(this->enforcer_polygons, rhs.enforcer_polygons); - rhs.reset(); - } - - // For the bridging flow, bottom_print_z will be above bottom_z to account for the vertical separation. - // For the non-bridging flow, bottom_print_z will be equal to bottom_z. - coordf_t bottom_print_z() const { return print_z - height; } - - // To sort the extremes of top / bottom interface layers. - coordf_t extreme_z() const { return (this->layer_type == sltTopContact) ? this->bottom_z : this->print_z; } - - SupporLayerType layer_type { sltUnknown }; - // Z used for printing, in unscaled coordinates. - coordf_t print_z { 0 }; - // Bottom Z of this layer. For soluble layers, bottom_z + height = print_z, - // otherwise bottom_z + gap + height = print_z. - coordf_t bottom_z { 0 }; - // Layer height in unscaled coordinates. - coordf_t height { 0 }; - // Index of a PrintObject layer_id supported by this layer. This will be set for top contact layers. - // If this is not a contact layer, it will be set to size_t(-1). - size_t idx_object_layer_above { size_t(-1) }; - // Index of a PrintObject layer_id, which supports this layer. This will be set for bottom contact layers. - // If this is not a contact layer, it will be set to size_t(-1). - size_t idx_object_layer_below { size_t(-1) }; - // Use a bridging flow when printing this support layer. - bool bridging { false }; - - // Polygons to be filled by the support pattern. - Polygons polygons; - // Currently for the contact layers only. - std::unique_ptr contact_polygons; - std::unique_ptr overhang_polygons; - // Enforcers need to be propagated independently in case the "support on build plate only" option is enabled. - std::unique_ptr enforcer_polygons; - }; - - struct SupportParams { - Flow first_layer_flow; - Flow support_material_flow; - Flow support_material_interface_flow; - Flow support_material_bottom_interface_flow; - // Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder? - bool can_merge_support_regions; - - coordf_t support_layer_height_min; - // coordf_t support_layer_height_max; - - coordf_t gap_xy; - - float base_angle; - float interface_angle; - coordf_t interface_spacing; - coordf_t interface_density; - coordf_t support_spacing; - coordf_t support_density; - - InfillPattern base_fill_pattern; - InfillPattern interface_fill_pattern; - InfillPattern contact_fill_pattern; - bool with_sheath; - }; - - // Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained - // up to the end of a generate() method. The layer storage may be replaced by an allocator class in the future, - // which would allocate layers by multiple chunks. - typedef std::deque MyLayerStorage; - typedef std::vector MyLayersPtr; - public: PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params); @@ -175,58 +215,48 @@ private: // Generate top contact layers supporting overhangs. // For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined. // If supports over bed surface only are requested, don't generate contact layers over an object. - MyLayersPtr top_contact_layers(const PrintObject &object, const std::vector &buildplate_covered, MyLayerStorage &layer_storage) const; + SupportGeneratorLayersPtr top_contact_layers(const PrintObject &object, const std::vector &buildplate_covered, SupportGeneratorLayerStorage &layer_storage) const; // Generate bottom contact layers supporting the top contact layers. // For a soluble interface material synchronize the layer heights with the object, // otherwise set the layer height to a bridging flow of a support interface nozzle. - MyLayersPtr bottom_contact_layers_and_layer_support_areas( - const PrintObject &object, const MyLayersPtr &top_contacts, std::vector &buildplate_covered, - MyLayerStorage &layer_storage, std::vector &layer_support_areas) const; + SupportGeneratorLayersPtr bottom_contact_layers_and_layer_support_areas( + const PrintObject &object, const SupportGeneratorLayersPtr &top_contacts, std::vector &buildplate_covered, + SupportGeneratorLayerStorage &layer_storage, std::vector &layer_support_areas) const; // Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them. - void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const MyLayersPtr &bottom_contacts, MyLayersPtr &top_contacts) const; + void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const SupportGeneratorLayersPtr &bottom_contacts, SupportGeneratorLayersPtr &top_contacts) const; // Generate raft layers and the intermediate support layers between the bottom contact and top contact surfaces. - MyLayersPtr raft_and_intermediate_support_layers( + SupportGeneratorLayersPtr raft_and_intermediate_support_layers( const PrintObject &object, - const MyLayersPtr &bottom_contacts, - const MyLayersPtr &top_contacts, - MyLayerStorage &layer_storage) const; + const SupportGeneratorLayersPtr &bottom_contacts, + const SupportGeneratorLayersPtr &top_contacts, + SupportGeneratorLayerStorage &layer_storage) const; // Fill in the base layers with polygons. void generate_base_layers( const PrintObject &object, - const MyLayersPtr &bottom_contacts, - const MyLayersPtr &top_contacts, - MyLayersPtr &intermediate_layers, + const SupportGeneratorLayersPtr &bottom_contacts, + const SupportGeneratorLayersPtr &top_contacts, + SupportGeneratorLayersPtr &intermediate_layers, const std::vector &layer_support_areas) const; - // Generate raft layers, also expand the 1st support layer - // in case there is no raft layer to improve support adhesion. - MyLayersPtr generate_raft_base( - const PrintObject &object, - const MyLayersPtr &top_contacts, - const MyLayersPtr &interface_layers, - const MyLayersPtr &base_interface_layers, - const MyLayersPtr &base_layers, - MyLayerStorage &layer_storage) const; - // Turn some of the base layers into base interface layers. // For soluble interfaces with non-soluble bases, print maximum two first interface layers with the base // extruder to improve adhesion of the soluble filament to the base. - std::pair generate_interface_layers( - const MyLayersPtr &bottom_contacts, - const MyLayersPtr &top_contacts, - MyLayersPtr &intermediate_layers, - MyLayerStorage &layer_storage) const; + std::pair generate_interface_layers( + const SupportGeneratorLayersPtr &bottom_contacts, + const SupportGeneratorLayersPtr &top_contacts, + SupportGeneratorLayersPtr &intermediate_layers, + SupportGeneratorLayerStorage &layer_storage) const; // Trim support layers by an object to leave a defined gap between // the support volume and the object. void trim_support_layers_by_object( const PrintObject &object, - MyLayersPtr &support_layers, + SupportGeneratorLayersPtr &support_layers, const coordf_t gap_extra_above, const coordf_t gap_extra_below, const coordf_t gap_xy) const; @@ -236,25 +266,14 @@ private: void clip_with_shape(); */ - // Produce the actual G-code. - void generate_toolpaths( - SupportLayerPtrs &support_layers, - const MyLayersPtr &raft_layers, - const MyLayersPtr &bottom_contacts, - const MyLayersPtr &top_contacts, - const MyLayersPtr &intermediate_layers, - const MyLayersPtr &interface_layers, - const MyLayersPtr &base_interface_layers) const; - // Following objects are not owned by SupportMaterial class. - const PrintObject *m_object; const PrintConfig *m_print_config; const PrintObjectConfig *m_object_config; // Pre-calculated parameters shared between the object slicer and the support generator, // carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc. SlicingParameters m_slicing_params; // Various precomputed support parameters to be shared with external functions. - SupportParams m_support_params; + SupportParameters m_support_params; }; } // namespace Slic3r diff --git a/src/libslic3r/SurfaceCollection.cpp b/src/libslic3r/SurfaceCollection.cpp index ec847d2a3..2ec071f7d 100644 --- a/src/libslic3r/SurfaceCollection.cpp +++ b/src/libslic3r/SurfaceCollection.cpp @@ -65,14 +65,11 @@ SurfacesPtr SurfaceCollection::filter_by_types(const SurfaceType *types, int nty return ss; } -void SurfaceCollection::filter_by_type(SurfaceType type, Polygons* polygons) +void SurfaceCollection::filter_by_type(SurfaceType type, Polygons *polygons) const { - for (Surfaces::iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) { - if (surface->surface_type == type) { - Polygons pp = surface->expolygon; - polygons->insert(polygons->end(), pp.begin(), pp.end()); - } - } + for (const Surface &surface : this->surfaces) + if (surface.surface_type == type) + polygons_append(*polygons, to_polygons(surface.expolygon)); } void SurfaceCollection::keep_type(const SurfaceType type) @@ -124,6 +121,22 @@ void SurfaceCollection::remove_type(const SurfaceType type) surfaces.erase(surfaces.begin() + j, surfaces.end()); } +void SurfaceCollection::remove_type(const SurfaceType type, ExPolygons *polygons) +{ + size_t j = 0; + for (size_t i = 0; i < surfaces.size(); ++ i) { + if (Surface &surface = surfaces[i]; surface.surface_type == type) { + polygons->emplace_back(std::move(surface.expolygon)); + } else { + if (j < i) + std::swap(surfaces[i], surfaces[j]); + ++ j; + } + } + if (j < surfaces.size()) + surfaces.erase(surfaces.begin() + j, surfaces.end()); +} + void SurfaceCollection::remove_types(const SurfaceType *types, int ntypes) { size_t j = 0; diff --git a/src/libslic3r/SurfaceCollection.hpp b/src/libslic3r/SurfaceCollection.hpp index 7e01a68df..8c27a507b 100644 --- a/src/libslic3r/SurfaceCollection.hpp +++ b/src/libslic3r/SurfaceCollection.hpp @@ -32,7 +32,8 @@ public: void keep_types(const SurfaceType *types, int ntypes); void remove_type(const SurfaceType type); void remove_types(const SurfaceType *types, int ntypes); - void filter_by_type(SurfaceType type, Polygons* polygons); + void filter_by_type(SurfaceType type, Polygons *polygons) const; + void remove_type(const SurfaceType type, ExPolygons *polygons); void set_type(SurfaceType type) { for (Surface &surface : this->surfaces) surface.surface_type = type; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index ee2c0cd80..ff514cc02 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -47,8 +47,6 @@ #define ENABLE_PREVIEW_LAYER_TIME (1 && ENABLE_2_5_0_ALPHA1) // Enable showing time estimate for travel moves in legend #define ENABLE_TRAVEL_TIME (1 && ENABLE_2_5_0_ALPHA1) -// Enable not killing focus in object manipulator fields when hovering over 3D scene -#define ENABLE_OBJECT_MANIPULATOR_FOCUS (0 && ENABLE_2_5_0_ALPHA1) // Enable removal of wipe tower magic object_id equal to 1000 #define ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL (1 && ENABLE_2_5_0_ALPHA1) // Enable removal of legacy OpenGL calls diff --git a/src/libslic3r/Thread.cpp b/src/libslic3r/Thread.cpp index c099f8de6..4fe07fb59 100644 --- a/src/libslic3r/Thread.cpp +++ b/src/libslic3r/Thread.cpp @@ -203,7 +203,7 @@ void name_tbb_thread_pool_threads_set_locale() const size_t nthreads_hw = tbb::this_task_arena::max_concurrency(); size_t nthreads = nthreads_hw; -#ifdef SLIC3R_PROFILE +#if 0 // Shiny profiler is not thread safe, thus disable parallelization. disable_multi_threading(); nthreads = 1; diff --git a/src/libslic3r/TreeModelVolumes.cpp b/src/libslic3r/TreeModelVolumes.cpp new file mode 100644 index 000000000..aa968d996 --- /dev/null +++ b/src/libslic3r/TreeModelVolumes.cpp @@ -0,0 +1,777 @@ +// Tree supports by Thomas Rahm, losely based on Tree Supports by CuraEngine. +// Original source of Thomas Rahm's tree supports: +// https://github.com/ThomasRahm/CuraEngine +// +// Original CuraEngine copyright: +// Copyright (c) 2021 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. + +#include "TreeModelVolumes.hpp" +#include "TreeSupport.hpp" + +#include "BuildVolume.hpp" +#include "ClipperUtils.hpp" +#include "Flow.hpp" +#include "Layer.hpp" +#include "Point.hpp" +#include "Print.hpp" +#include "PrintConfig.hpp" +#include "Utils.hpp" + +#include + +#include +#include + +namespace Slic3r +{ + +// or warning +// had to use a define beacuse the macro processing inside macro BOOST_LOG_TRIVIAL() +#define error_level_not_in_cache error + +TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &print_object) +{ + const PrintConfig &print_config = print_object.print()->config(); + const PrintObjectConfig &config = print_object.config(); + const SlicingParameters &slicing_params = print_object.slicing_parameters(); +// const std::vector printing_extruders = print_object.object_extruders(); + + // Support must be enabled and set to Tree style. + assert(config.support_material); + assert(config.support_material_style == smsTree); + + // Calculate maximum external perimeter width over all printing regions, taking into account the default layer height. + coordf_t external_perimeter_width = 0.; + for (size_t region_id = 0; region_id < print_object.num_printing_regions(); ++ region_id) { + const PrintRegion ®ion = print_object.printing_region(region_id); + external_perimeter_width = std::max(external_perimeter_width, region.flow(print_object, frExternalPerimeter, config.layer_height).width()); + } + + this->layer_height = scaled(config.layer_height.value); + this->resolution = scaled(print_config.gcode_resolution.value); + this->min_feature_size = scaled(config.min_feature_size.value); + // +1 makes the threshold inclusive + this->support_angle = 0.5 * M_PI - std::clamp((config.support_material_threshold + 1) * M_PI / 180., 0., 0.5 * M_PI); + this->support_line_width = support_material_flow(&print_object, config.layer_height).scaled_width(); + this->support_roof_line_width = support_material_interface_flow(&print_object, config.layer_height).scaled_width(); + //FIXME add it to SlicingParameters and reuse in both tree and normal supports? + this->support_bottom_enable = config.support_material_interface_layers.value > 0 && config.support_material_bottom_interface_layers.value != 0; + this->support_bottom_height = this->support_bottom_enable ? + (config.support_material_bottom_interface_layers.value > 0 ? + config.support_material_bottom_interface_layers.value : + config.support_material_interface_layers.value) * this->layer_height : + 0; + this->support_material_buildplate_only = config.support_material_buildplate_only; + this->support_xy_distance = scaled(config.support_material_xy_spacing.get_abs_value(external_perimeter_width)); + // Separation of interfaces, it is likely smaller than support_xy_distance. + this->support_xy_distance_overhang = std::min(this->support_xy_distance, scaled(0.5 * external_perimeter_width)); + this->support_top_distance = scaled(slicing_params.gap_support_object); + this->support_bottom_distance = scaled(slicing_params.gap_object_support); +// this->support_interface_skip_height = +// this->support_infill_angles = + this->support_roof_enable = config.support_material_interface_layers.value > 0; + this->support_roof_height = config.support_material_interface_layers.value * this->layer_height; +// this->minimum_roof_area = +// this->support_roof_angles = + this->support_roof_pattern = config.support_material_interface_pattern; + this->support_pattern = config.support_material_pattern; + this->support_line_spacing = scaled(config.support_material_spacing.value); +// this->support_bottom_offset = +// this->support_wall_count = config.support_material_with_sheath ? 1 : 0; + this->support_wall_count = 1; + this->support_roof_line_distance = scaled(config.support_material_interface_spacing.value) + this->support_roof_line_width; +// this->minimum_support_area = +// this->minimum_bottom_area = +// this->support_offset = +} + +//FIXME Machine border is currently ignored. +static Polygons calculateMachineBorderCollision(Polygon machine_border) +{ + // Put a border of 1m around the print volume so that we don't collide. +#if 1 + //FIXME just returning no border will let tree support legs collide with print bed boundary + return {}; +#else + //FIXME offsetting by 1000mm easily overflows int32_tr coordinate. + Polygons out = offset(machine_border, scaled(1000.), jtMiter, 1.2); + machine_border.reverse(); // Makes the polygon negative so that we subtract the actual volume from the collision area. + out.emplace_back(std::move(machine_border)); + return out; +#endif +} + +TreeModelVolumes::TreeModelVolumes( + const PrintObject &print_object, + const BuildVolume &build_volume, + const coord_t max_move, const coord_t max_move_slow, size_t current_mesh_idx, + double progress_multiplier, double progress_offset, const std::vector& additional_excluded_areas) : + // -2 to avoid rounding errors + m_max_move{ std::max(max_move - 2, 0) }, m_max_move_slow{ std::max(max_move_slow - 2, 0) }, +#ifdef SLIC3R_TREESUPPORTS_PROGRESS + m_progress_multiplier{ progress_multiplier }, m_progress_offset{ progress_offset }, +#endif // SLIC3R_TREESUPPORTS_PROGRESS + m_machine_border{ calculateMachineBorderCollision(build_volume.polygon()) } +{ +#if 0 + std::unordered_map mesh_to_layeroutline_idx; + for (size_t mesh_idx = 0; mesh_idx < storage.meshes.size(); ++ mesh_idx) { + SliceMeshStorage mesh = storage.meshes[mesh_idx]; + bool added = false; + for (size_t idx = 0; idx < m_layer_outlines.size(); ++ idx) + if (TreeSupport::TreeSupportSettings(m_layer_outlines[idx].first) == TreeSupport::TreeSupportSettings(mesh.settings)) { + added = true; + mesh_to_layeroutline_idx[mesh_idx] = idx; + } + if (! added) { + mesh_to_layeroutline_idx[mesh_idx] = m_layer_outlines.size(); + m_layer_outlines.emplace_back(mesh.settings, std::vector(storage.support.supportLayers.size(), Polygons())); + } + } + for (size_t idx = 0; idx < m_layer_outlines.size(); ++ idx) { + tbb::parallel_for(tbb::blocked_range(0, m_layer_outlines[idx].second.size()), + [&](const tbb::blocked_range &range) { + for (const size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) + m_layer_outlines[idx].second[layer_idx] = union_(m_layer_outlines[idx].second[layer_idx]); + }); + } + m_current_outline_idx = mesh_to_layeroutline_idx[current_mesh_idx]; + +#else + { + m_anti_overhang = print_object.slice_support_blockers(); + TreeSupportMeshGroupSettings mesh_settings(print_object); + m_layer_outlines.emplace_back(mesh_settings, std::vector{}); + m_current_outline_idx = 0; + std::vector &outlines = m_layer_outlines.front().second; + outlines.assign(print_object.layer_count(), Polygons{}); + tbb::parallel_for(tbb::blocked_range(0, print_object.layer_count(), std::min(1, std::max(16, print_object.layer_count() / (8 * tbb::this_task_arena::max_concurrency())))), + [&](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) + outlines[layer_idx] = to_polygons(expolygons_simplify(print_object.get_layer(layer_idx)->lslices, mesh_settings.resolution)); + }); + } +#endif + + m_support_rests_on_model = false; + m_min_resolution = std::numeric_limits::max(); + for (auto data_pair : m_layer_outlines) { + m_support_rests_on_model |= ! data_pair.first.support_material_buildplate_only; + m_min_resolution = std::min(m_min_resolution, data_pair.first.resolution); + } + + const TreeSupport::TreeSupportSettings config{ m_layer_outlines[m_current_outline_idx].first }; + m_current_min_xy_dist = config.xy_min_distance; + m_current_min_xy_dist_delta = config.xy_distance - m_current_min_xy_dist; + assert(m_current_min_xy_dist_delta >= 0); + m_increase_until_radius = config.increase_radius_until_radius; + m_radius_0 = config.getRadius(0); + +#if 0 + for (size_t mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++) { + SliceMeshStorage mesh = storage.meshes[mesh_idx]; + tbb::parallel_for(tbb::blocked_range(0, m_layer_outlines[mesh_to_layeroutline_idx[mesh_idx]].second.size()), + [&](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) + if (layer_idx < mesh.layer_nr_max_filled_layer) { + Polygons outline = extractOutlineFromMesh(mesh, layer_idx); + append(m_layer_outlines[mesh_to_layeroutline_idx[mesh_idx]].second[layer_idx], outline); + } + }); + } + if (! additional_excluded_areas.empty()) { + tbb::parallel_for(tbb::blocked_range(0, m_anti_overhang.size()), + [&](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { + if (layer_idx < coord_t(additional_excluded_areas.size())) + append(m_anti_overhang[layer_idx], additional_excluded_areas[layer_idx]); + // if (SUPPORT_TREE_AVOID_SUPPORT_BLOCKER) + // append(m_anti_overhang[layer_idx], storage.support.supportLayers[layer_idx].anti_overhang); + //FIXME block wipe tower + // if (storage.primeTower.enabled) + // append(m_anti_overhang[layer_idx], layer_idx == 0 ? storage.primeTower.outer_poly_first_layer : storage.primeTower.outer_poly); + m_anti_overhang[layer_idx] = union_(m_anti_overhang[layer_idx]); + } + }); + } +#endif +} + +void TreeModelVolumes::precalculate(const coord_t max_layer) +{ + auto t_start = std::chrono::high_resolution_clock::now(); + m_precalculated = true; + + // Get the config corresponding to one mesh that is in the current group. Which one has to be irrelevant. + // Not the prettiest way to do this, but it ensures some calculations that may be a bit more complex + // like inital layer diameter are only done in once. + TreeSupport::TreeSupportSettings config(m_layer_outlines[m_current_outline_idx].first); + + { + // calculate which radius each layer in the tip may have. + std::vector possible_tip_radiis; + for (size_t distance_to_top = 0; distance_to_top <= config.tip_layers; ++ distance_to_top) { + possible_tip_radiis.emplace_back(ceilRadius(config.getRadius(distance_to_top))); + possible_tip_radiis.emplace_back(ceilRadius(config.getRadius(distance_to_top) + m_current_min_xy_dist_delta)); + } + sort_remove_duplicates(possible_tip_radiis); + // It theoretically may happen in the tip, that the radius can change so much in-between 2 layers, + // that a ceil step is skipped (as in there is a radius r so that ceilRadius(radius(dtt)) radius_until_layer; + // while it is possible to calculate, up to which layer the avoidance should be calculated, this simulation is easier to understand, and does not need to be adjusted if something of the radius calculation is changed. + // Overhead with an assumed worst case of 6600 layers was about 2ms + for (LayerIndex distance_to_top = 0; distance_to_top <= max_layer; ++ distance_to_top) { + const LayerIndex current_layer = max_layer - distance_to_top; + auto update_radius_until_layer = [&radius_until_layer, current_layer](coord_t r) { + auto it = radius_until_layer.find(r); + if (it == radius_until_layer.end()) + radius_until_layer.emplace_hint(it, r, current_layer); + }; + // regular radius + update_radius_until_layer(ceilRadius(config.getRadius(distance_to_top, 0) + m_current_min_xy_dist_delta)); + // the maximum radius that the radius with the min_xy_dist can achieve + update_radius_until_layer(ceilRadius(config.getRadius(distance_to_top, 0))); + update_radius_until_layer(ceilRadius(config.recommendedMinRadius(current_layer) + m_current_min_xy_dist_delta)); + } + + // Copy to deque to use in parallel for later. + std::vector relevant_avoidance_radiis{ radius_until_layer.begin(), radius_until_layer.end() }; + + // Append additional radiis needed for collision. + // To calculate collision holefree for every radius, the collision of radius m_increase_until_radius will be required. + radius_until_layer[ceilRadius(m_increase_until_radius + m_current_min_xy_dist_delta)] = max_layer; + // Collision for radius 0 needs to be calculated everywhere, as it will be used to ensure valid xy_distance in drawAreas. + radius_until_layer[0] = max_layer; + if (m_current_min_xy_dist_delta != 0) + radius_until_layer[m_current_min_xy_dist_delta] = max_layer; + + // Now that required_avoidance_limit contains the maximum of ild and regular required radius just copy. + std::vector relevant_collision_radiis{ radius_until_layer.begin(), radius_until_layer.end() }; + + // Calculate the relevant collisions + calculateCollision(relevant_collision_radiis); + + // calculate a separate Collisions with all holes removed. These are relevant for some avoidances that try to avoid holes (called safe) + std::vector relevant_hole_collision_radiis; + for (RadiusLayerPair key : relevant_avoidance_radiis) + if (key.first < m_increase_until_radius + m_current_min_xy_dist_delta) + relevant_hole_collision_radiis.emplace_back(key); + + // Calculate collisions without holes, built from regular collision + calculateCollisionHolefree(relevant_hole_collision_radiis); + // Let placables be calculated from calculateAvoidance() for better parallelization. + if (m_support_rests_on_model) + calculatePlaceables(relevant_avoidance_radiis); + + auto t_coll = std::chrono::high_resolution_clock::now(); + + // Calculate the relevant avoidances in parallel as far as possible + { + tbb::task_group task_group; + task_group.run([this, relevant_avoidance_radiis]{ calculateAvoidance(relevant_avoidance_radiis, true, m_support_rests_on_model); }); + task_group.run([this, relevant_avoidance_radiis]{ calculateWallRestrictions(relevant_avoidance_radiis); }); + task_group.wait(); + } + auto t_end = std::chrono::high_resolution_clock::now(); + auto dur_col = 0.001 * std::chrono::duration_cast(t_coll - t_start).count(); + auto dur_avo = 0.001 * std::chrono::duration_cast(t_end - t_coll).count(); + +// m_precalculated = true; + BOOST_LOG_TRIVIAL(info) << "Precalculating collision took" << dur_col << " ms. Precalculating avoidance took " << dur_avo << " ms."; +} + +const Polygons& TreeModelVolumes::getCollision(const coord_t orig_radius, LayerIndex layer_idx, bool min_xy_dist) const +{ + const coord_t radius = this->ceilRadius(orig_radius, min_xy_dist); + if (std::optional> result = m_collision_cache.getArea({ radius, layer_idx }); result) + return (*result).get(); + if (m_precalculated) { + BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate collision at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!"; + TreeSupport::showError("Not precalculated Collision requested.", false); + } + const_cast(this)->calculateCollision(radius, layer_idx); + return getCollision(orig_radius, layer_idx, min_xy_dist); +} + +// Private. Only called internally by calculateAvoidance() and calculateAvoidanceToModel(), radius is already snapped to grid. +const Polygons& TreeModelVolumes::getCollisionHolefree(coord_t radius, LayerIndex layer_idx) const +{ + assert(radius == this->ceilRadius(radius)); + assert(radius < m_increase_until_radius + m_current_min_xy_dist_delta); + if (std::optional> result = m_collision_cache_holefree.getArea({ radius, layer_idx }); result) + return (*result).get(); + if (m_precalculated) { + BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate collision holefree at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!"; + TreeSupport::showError("Not precalculated Holefree Collision requested.", false); + } + const_cast(this)->calculateCollisionHolefree({ radius, layer_idx }); + return getCollisionHolefree(radius, layer_idx); +} + +const Polygons& TreeModelVolumes::getAvoidance(const coord_t orig_radius, LayerIndex layer_idx, AvoidanceType type, bool to_model, bool min_xy_dist) const +{ + if (layer_idx == 0) // What on the layer directly above buildplate do i have to avoid to reach the buildplate ... + return getCollision(orig_radius, layer_idx, min_xy_dist); + + const coord_t radius = this->ceilRadius(orig_radius, min_xy_dist); + if (type == AvoidanceType::FastSafe && radius >= m_increase_until_radius + m_current_min_xy_dist_delta) + // no holes anymore by definition at this request + type = AvoidanceType::Fast; + + if (std::optional> result = + this->avoidance_cache(type, to_model).getArea({ radius, layer_idx }); + result) + return (*result).get(); + + if (m_precalculated) { + if (to_model) { + BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Avoidance to model at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!"; + TreeSupport::showError("Not precalculated Avoidance(to model) requested.", false); + } else { + BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Avoidance at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!"; + TreeSupport::showError("Not precalculated Avoidance(to buildplate) requested.", false); + } + } + const_cast(this)->calculateAvoidance({ radius, layer_idx }, ! to_model, to_model); + // Retrive failed and correct result was calculated. Now it has to be retrived. + return getAvoidance(orig_radius, layer_idx, type, to_model, min_xy_dist); +} + +const Polygons& TreeModelVolumes::getPlaceableAreas(const coord_t orig_radius, LayerIndex layer_idx) const +{ + if (orig_radius == 0) + return this->getCollision(0, layer_idx, true); + + const coord_t radius = ceilRadius(orig_radius); + if (std::optional> result = m_placeable_areas_cache.getArea({ radius, layer_idx }); result) + return (*result).get(); + if (m_precalculated) { + BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Placeable Areas at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!"; + TreeSupport::showError("Not precalculated Placeable areas requested.", false); + } + const_cast(this)->calculatePlaceables(radius, layer_idx); + return getPlaceableAreas(orig_radius, layer_idx); +} + +const Polygons& TreeModelVolumes::getWallRestriction(const coord_t orig_radius, LayerIndex layer_idx, bool min_xy_dist) const +{ + assert(layer_idx > 0); + if (layer_idx == 0) + // Should never be requested as there will be no going below layer 0 ..., + // but just to be sure some semi-sane catch. Alternative would be empty Polygon. + return getCollision(orig_radius, layer_idx, min_xy_dist); + + min_xy_dist &= m_current_min_xy_dist_delta > 0; + + const coord_t radius = ceilRadius(orig_radius); + if (std::optional> result = + (min_xy_dist ? m_wall_restrictions_cache_min : m_wall_restrictions_cache).getArea({ radius, layer_idx }); + result) + return (*result).get(); + if (m_precalculated) { + BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Wall restricions at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!"; + TreeSupport::showError( + min_xy_dist ? + "Not precalculated Wall restriction of minimum xy distance requested )." : + "Not precalculated Wall restriction requested )." + , false); + } + const_cast(this)->calculateWallRestrictions({ radius, layer_idx }); + return getWallRestriction(orig_radius, layer_idx, min_xy_dist); // Retrieve failed and correct result was calculated. Now it has to be retrieved. +} + +void TreeModelVolumes::calculateCollision(const std::vector &keys) +{ + tbb::parallel_for(tbb::blocked_range(0, keys.size()), + [&](const tbb::blocked_range &range) { + for (size_t ikey = range.begin(); ikey != range.end(); ++ ikey) { + const LayerIndex radius = keys[ikey].first; + const size_t max_layer_idx = keys[ikey].second; + // recursive call to parallel_for. + calculateCollision(radius, max_layer_idx); + } + }); +} + +void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex max_layer_idx) +{ +// assert(radius == this->ceilRadius(radius)); + + // Process the outlines from least layers to most layers so that the final union will run over the longest vector. + std::vector layer_outline_indices(m_layer_outlines.size(), 0); + std::iota(layer_outline_indices.begin(), layer_outline_indices.end(), 0); + std::sort(layer_outline_indices.begin(), layer_outline_indices.end(), + [this](size_t i, size_t j) { return m_layer_outlines[i].second.size() < m_layer_outlines[j].second.size(); }); + + const LayerIndex min_layer_last = m_collision_cache.getMaxCalculatedLayer(radius); + std::vector data(max_layer_idx + 1 - min_layer_last, Polygons{}); + const bool calculate_placable = m_support_rests_on_model && radius == 0; + std::vector data_placeable; + if (calculate_placable) + data_placeable = std::vector(max_layer_idx + 1 - min_layer_last, Polygons{}); + + for (size_t outline_idx : layer_outline_indices) + if (const std::vector &outlines = m_layer_outlines[outline_idx].second; ! outlines.empty()) { + const TreeSupportMeshGroupSettings &settings = m_layer_outlines[outline_idx].first; + const coord_t layer_height = settings.layer_height; + const coord_t z_distance_bottom = settings.support_bottom_distance; + const int z_distance_bottom_layers = round_up_divide(z_distance_bottom, layer_height); + const int z_distance_top_layers = round_up_divide(settings.support_top_distance, layer_height); + const LayerIndex max_required_layer = std::min(outlines.size(), max_layer_idx + std::max(coord_t(1), z_distance_top_layers)); + const LayerIndex min_layer_bottom = std::max(0, min_layer_last - int(z_distance_bottom_layers)); + const coord_t xy_distance = outline_idx == m_current_outline_idx ? m_current_min_xy_dist : + // technically this causes collision for the normal xy_distance to be larger by m_current_min_xy_dist_delta for all + // not currently processing meshes as this delta will be added at request time. + // avoiding this would require saving each collision for each outline_idx separately. + // and later for each avoidance... But avoidance calculation has to be for the whole scene and can NOT be done for each outline_idx separately and combined later. + // so avoiding this inaccuracy seems infeasible as it would require 2x the avoidance calculations => 0.5x the performance. + //FIXME support_xy_distance is not corrected for "soluble" flag, see TreeSupportSettings constructor. + settings.support_xy_distance; + + // 1) Calculate offsets of collision areas in parallel. + std::vector collision_areas_offsetted(max_required_layer + 1 - min_layer_bottom); + tbb::parallel_for(tbb::blocked_range(min_layer_bottom, max_required_layer + 1), + [&outlines, &machine_border = m_machine_border, offset_value = radius + xy_distance, min_layer_bottom, &collision_areas_offsetted] + (const tbb::blocked_range &range) { + for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++ layer_idx) { + Polygons collision_areas = machine_border; + append(collision_areas, outlines[layer_idx]); + // jtRound is not needed here, as the overshoot can not cause errors in the algorithm, because no assumptions are made about the model. + // if a key does not exist when it is accessed it is added! + collision_areas_offsetted[layer_idx - min_layer_bottom] = offset_value == 0 ? union_(collision_areas) : offset(union_ex(collision_areas), offset_value, ClipperLib::jtMiter, 1.2); + } + }); + + // 2) Sum over top / bottom ranges. + const bool last = outline_idx == layer_outline_indices.size(); + tbb::parallel_for(tbb::blocked_range(min_layer_last + 1, max_layer_idx + 1), + [&collision_areas_offsetted, &anti_overhang = m_anti_overhang, min_layer_bottom, radius, z_distance_bottom_layers, z_distance_top_layers, min_resolution = m_min_resolution, &data, min_layer_last, last] + (const tbb::blocked_range& range) { + for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++layer_idx) { + Polygons collisions; + for (int i = -z_distance_bottom_layers; i <= z_distance_top_layers; ++ i) { + int j = layer_idx + i - min_layer_bottom; + if (j >= 0 && j < int(collision_areas_offsetted.size())) + append(collisions, collision_areas_offsetted[j]); + } + collisions = last && layer_idx < int(anti_overhang.size()) ? union_(collisions, offset(union_ex(anti_overhang[layer_idx]), radius, ClipperLib::jtMiter, 1.2)) : union_(collisions); + auto &dst = data[layer_idx - (min_layer_last + 1)]; + if (last) { + if (! dst.empty()) + collisions = union_(collisions, dst); + dst = polygons_simplify(collisions, min_resolution); + } else + append(dst, collisions); + } + }); + + // 3) Optionally calculate placables. + if (calculate_placable) { + // Calculating both the collision areas and placable areas. + tbb::parallel_for(tbb::blocked_range(std::max(min_layer_last + 1, z_distance_bottom_layers + 1), max_layer_idx + 1), + [&collision_areas_offsetted, &anti_overhang = m_anti_overhang, min_layer_bottom, z_distance_bottom_layers, last, min_resolution = m_min_resolution, &data_placeable, min_layer_last] + (const tbb::blocked_range& range) { + for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++ layer_idx) { + LayerIndex layer_idx_below = layer_idx - (z_distance_bottom_layers + 1) - min_layer_bottom; + assert(layer_idx_below >= 0); + auto ¤t = collision_areas_offsetted[layer_idx - min_layer_bottom]; + auto &below = collision_areas_offsetted[layer_idx_below]; + auto placable = diff(below, layer_idx < int(anti_overhang.size()) ? union_(current, anti_overhang[layer_idx - (z_distance_bottom_layers + 1)]) : current); + auto &dst = data_placeable[layer_idx - (min_layer_last + 1)]; + if (last) { + if (! dst.empty()) + placable = union_(placable, dst); + dst = polygons_simplify(placable, min_resolution); + } else + append(dst, placable); + } + }); + } else { + // Calculating just the collision areas. + } + } +#ifdef SLIC3R_TREESUPPORTS_PROGRESS + { + std::lock_guard critical_section(*m_critical_progress); + if (m_precalculated && m_precalculation_progress < TREE_PROGRESS_PRECALC_COLL) { + m_precalculation_progress += TREE_PROGRESS_PRECALC_COLL / keys.size(); + Progress::messageProgress(Progress::Stage::SUPPORT, m_precalculation_progress * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL); + } + } +#endif + m_collision_cache.insert(std::move(data), min_layer_last + 1, radius); + if (calculate_placable) + m_placeable_areas_cache.insert(std::move(data_placeable), min_layer_last + 1, radius); +} + +void TreeModelVolumes::calculateCollisionHolefree(const std::vector &keys) +{ + LayerIndex max_layer = 0; + for (long long unsigned int i = 0; i < keys.size(); i++) + max_layer = std::max(max_layer, keys[i].second); + + tbb::parallel_for(tbb::blocked_range(0, max_layer + 1, keys.size()), + [&](const tbb::blocked_range &range) { + RadiusLayerPolygonCacheData data; + for (LayerIndex layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { + for (RadiusLayerPair key : keys) + if (layer_idx <= key.second) { + // Logically increase the collision by m_increase_until_radius + coord_t radius = key.first; + assert(radius == this->ceilRadius(radius)); + assert(radius < m_increase_until_radius + m_current_min_xy_dist_delta); + coord_t increase_radius_ceil = ceilRadius(m_increase_until_radius, false) - radius; + assert(increase_radius_ceil > 0); + // this union is important as otherwise holes(in form of lines that will increase to holes in a later step) can get unioned onto the area. + data[RadiusLayerPair(radius, layer_idx)] = polygons_simplify( + offset(union_ex(getCollision(m_increase_until_radius, layer_idx, false)), + 5 - increase_radius_ceil, ClipperLib::jtRound, m_min_resolution), + m_min_resolution); + } + } + m_collision_cache_holefree.insert(std::move(data)); + }); +} + +void TreeModelVolumes::calculateAvoidance(const std::vector &keys, bool to_build_plate, bool to_model) +{ + // For every RadiusLayer pair there are 3 avoidances that have to be calculated. + // Prepare tasks for parallelization. + struct AvoidanceTask { + AvoidanceType type; + coord_t radius; + LayerIndex max_required_layer; + bool to_model; + LayerIndex start_layer; + + bool slow() const { return this->type == AvoidanceType::Slow; } + bool holefree() const { return this->type == AvoidanceType::FastSafe; } + }; + + std::vector avoidance_tasks; + avoidance_tasks.reserve((int(to_build_plate) + int(to_model)) * keys.size() * size_t(AvoidanceType::Count)); + + for (int iter_idx = 0; iter_idx < 2 * int(keys.size()) * int(AvoidanceType::Count); ++ iter_idx) { + AvoidanceTask task{ + AvoidanceType(iter_idx % int(AvoidanceType::Count)), + keys[iter_idx / 6].first, // radius + keys[iter_idx / 6].second, // max_layer + ((iter_idx / 3) & 1) != 0 // to_model + }; + // Ensure start_layer is at least 1 as if no avoidance was calculated yet getMaxCalculatedLayer() returns -1. + task.start_layer = std::max(1, 1 + avoidance_cache(task.type, task.to_model).getMaxCalculatedLayer(task.radius)); + if (task.start_layer > task.max_required_layer) { + BOOST_LOG_TRIVIAL(debug) << "Calculation requested for value already calculated?"; + continue; + } + if (! task.holefree() || task.radius < m_increase_until_radius + m_current_min_xy_dist_delta) + avoidance_tasks.emplace_back(task); + } + + tbb::parallel_for(tbb::blocked_range(0, avoidance_tasks.size(), 1), + [this, &avoidance_tasks](const tbb::blocked_range &range) { + for (size_t task_idx = range.begin(); task_idx < range.end(); ++ task_idx) { + const AvoidanceTask &task = avoidance_tasks[task_idx]; + assert(! task.holefree() || task.radius < m_increase_until_radius + m_current_min_xy_dist_delta); + if (task.to_model) + // ensuring Placeableareas are calculated + getPlaceableAreas(task.radius, task.max_required_layer); + // The following loop propagating avoidance regions bottom up is inherently serial. + const bool collision_holefree = (task.slow() || task.holefree()) && task.radius < m_increase_until_radius + m_current_min_xy_dist_delta; + const float max_move = task.slow() ? m_max_move_slow : m_max_move; + // Limiting the offset step so that unioning the shrunk latest_avoidance with the current layer collisions + // will not create gaps in the resulting avoidance region letting a tree support branch tunneling through an object wall. + float move_step = 1.9 * std::max(task.radius, m_current_min_xy_dist); + int move_steps = round_up_divide(max_move, move_step); + assert(move_steps > 0); + float last_move_step = max_move - (move_steps - 1) * move_step; + if (last_move_step < scaled(0.05)) { + assert(move_steps > 1); + if (move_steps > 1) { + // Avoid taking a very short last step, stretch the other steps a bit instead. + move_step = max_move / (-- move_steps); + last_move_step = move_step; + } + } + // minDist as the delta was already added, also avoidance for layer 0 will return the collision. + Polygons latest_avoidance = getAvoidance(task.radius, task.start_layer - 1, task.type, task.to_model, true); + std::vector> data; + data.reserve(task.max_required_layer + 1 - task.start_layer); + for (LayerIndex layer_idx = task.start_layer; layer_idx <= task.max_required_layer; ++ layer_idx) { + // Merge current layer collisions with shrunk last_avoidance. + const Polygons ¤t_layer_collisions = collision_holefree ? getCollisionHolefree(task.radius, layer_idx) : getCollision(task.radius, layer_idx, true); + // For mildly steep branch angles only one step will be taken. + for (int istep = 0; istep < move_steps; ++ istep) + latest_avoidance = union_(current_layer_collisions, + offset(latest_avoidance, + istep + 1 == move_steps ? - last_move_step : - move_step, + ClipperLib::jtRound, m_min_resolution)); + if (task.to_model) + latest_avoidance = diff(latest_avoidance, getPlaceableAreas(task.radius, layer_idx)); + latest_avoidance = polygons_simplify(latest_avoidance, m_min_resolution); + data.emplace_back(RadiusLayerPair{task.radius, layer_idx}, latest_avoidance); + } +#ifdef SLIC3R_TREESUPPORTS_PROGRESS + { + std::lock_guard critical_section(*m_critical_progress); + if (m_precalculated && m_precalculation_progress < TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_PRECALC_AVO) { + m_precalculation_progress += to_model ? + 0.4 * TREE_PROGRESS_PRECALC_AVO / (keys.size() * 3) : + m_support_rests_on_model ? 0.4 : 1 * TREE_PROGRESS_PRECALC_AVO / (keys.size() * 3); + Progress::messageProgress(Progress::Stage::SUPPORT, m_precalculation_progress * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL); + } + } +#endif + avoidance_cache(task.type, task.to_model).insert(std::move(data)); + } + }); +} + + +void TreeModelVolumes::calculatePlaceables(const std::vector &keys) +{ + tbb::parallel_for(tbb::blocked_range(0, keys.size()), + [&, keys](const tbb::blocked_range& range) { + for (size_t key_idx = range.begin(); key_idx < range.end(); ++ key_idx) + this->calculatePlaceables(keys[key_idx].first, keys[key_idx].second); + }); +} + +void TreeModelVolumes::calculatePlaceables(const coord_t radius, const LayerIndex max_required_layer) +{ + LayerIndex start_layer = 1 + m_placeable_areas_cache.getMaxCalculatedLayer(radius); + if (start_layer > max_required_layer) { + BOOST_LOG_TRIVIAL(debug) << "Requested calculation for value already calculated ?"; + return; + } + + std::vector data(max_required_layer + 1 - start_layer, Polygons{}); + + if (start_layer == 0) + data[0] = diff(m_machine_border, getCollision(radius, 0, true)); + + tbb::parallel_for(tbb::blocked_range(std::max(1, start_layer), max_required_layer + 1), + [this, &data, radius, start_layer](const tbb::blocked_range& range) { + for (LayerIndex layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) + data[layer_idx - start_layer] = offset(union_ex(getPlaceableAreas(0, layer_idx)), - radius, jtMiter, 1.2); + }); +#ifdef SLIC3R_TREESUPPORTS_PROGRESS + { + std::lock_guard critical_section(*m_critical_progress); + if (m_precalculated && m_precalculation_progress < TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_PRECALC_AVO) { + m_precalculation_progress += 0.2 * TREE_PROGRESS_PRECALC_AVO / (keys.size()); + Progress::messageProgress(Progress::Stage::SUPPORT, m_precalculation_progress * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL); + } + } +#endif + m_placeable_areas_cache.insert(std::move(data), start_layer, radius); +} + +void TreeModelVolumes::calculateWallRestrictions(const std::vector &keys) +{ + // Wall restrictions are mainly important when they represent actual walls that are printed, and not "just" the configured z_distance, because technically valid placement is no excuse for moving through a wall. + // As they exist to prevent accidentially moving though a wall at high speed between layers like thie (x = wall,i = influence area,o= empty space,d = blocked area because of z distance) Assume maximum movement distance is two characters and maximum safe movement distance of one character + + /* Potential issue addressed by the wall restrictions: Influence area may lag through a wall + * layer z+1:iiiiiiiiiiioooo + * layer z+0:xxxxxiiiiiiiooo + * layer z-1:ooooixxxxxxxxxx + */ + + // The radius for the upper collission has to be 0 as otherwise one may not enter areas that may be forbidden on layer_idx but not one below (c = not an influence area even though it should ): + /* + * layer z+1:xxxxxiiiiiioo + * layer z+0:dddddiiiiiiio + * layer z-1:dddocdddddddd + */ + // Also there can not just the collision of the lower layer be used because if it were: + /* + * layer z+1:dddddiiiiiiiiiio + * layer z+0:xxxxxddddddddddc + * layer z-1:dddddxxxxxxxxxxc + */ + // Or of the upper layer be used because if it were: + /* + * layer z+1:dddddiiiiiiiiiio + * layer z+0:xxxxcddddddddddc + * layer z-1:ddddcxxxxxxxxxxc + */ + + // And just offseting with maximum movement distance (and not in multiple steps) could cause: + /* + * layer z: oxiiiiiiiiioo + * layer z-1: ixiiiiiiiiiii + */ + + tbb::parallel_for(tbb::blocked_range(0, keys.size()), + [&, keys](const tbb::blocked_range &range) { + for (size_t key_idx = range.begin(); key_idx < range.end(); ++ key_idx) { + const coord_t radius = keys[key_idx].first; + const LayerIndex max_required_layer = keys[key_idx].second; + const coord_t min_layer_bottom = std::max(1, m_wall_restrictions_cache.getMaxCalculatedLayer(radius)); + const size_t buffer_size = max_required_layer + 1 - min_layer_bottom; + std::vector data(buffer_size, Polygons{}); + std::vector data_min; + if (m_current_min_xy_dist_delta > 0) + data_min.assign(buffer_size, Polygons{}); + tbb::parallel_for(tbb::blocked_range(min_layer_bottom, max_required_layer + 1), + [this, &data, &data_min, radius, min_layer_bottom](const tbb::blocked_range &range) { + for (LayerIndex layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { + data[layer_idx - min_layer_bottom] = polygons_simplify( + // radius contains m_current_min_xy_dist_delta already if required + intersection(getCollision(0, layer_idx, false), getCollision(radius, layer_idx - 1, true)), + m_min_resolution); + if (! data_min.empty()) + data_min[layer_idx - min_layer_bottom] = + polygons_simplify( + intersection(getCollision(0, layer_idx, true), getCollision(radius, layer_idx - 1, true)), + m_min_resolution); + } + }); + m_wall_restrictions_cache.insert(std::move(data), min_layer_bottom, radius); + if (! data_min.empty()) + m_wall_restrictions_cache_min.insert(std::move(data_min), min_layer_bottom, radius); + } + }); +} + +coord_t TreeModelVolumes::ceilRadius(const coord_t radius) const +{ + if (radius == 0) + return 0; + + coord_t out = m_radius_0; + if (radius > m_radius_0) { + // generate SUPPORT_TREE_PRE_EXPONENTIAL_STEPS of radiis before starting to exponentially increase them. + coord_t initial_radius_delta = SUPPORT_TREE_EXPONENTIAL_THRESHOLD - m_radius_0; + auto ignore = [this](coord_t r) { return std::binary_search(m_ignorable_radii.begin(), m_ignorable_radii.end(), r); }; + if (initial_radius_delta > SUPPORT_TREE_COLLISION_RESOLUTION) { + const int num_steps = round_up_divide(initial_radius_delta, SUPPORT_TREE_EXPONENTIAL_THRESHOLD); + const int stepsize = initial_radius_delta / num_steps; + out += stepsize; + for (auto step = 0; step < num_steps; ++ step) { + if (out >= radius && ! ignore(out)) + return out; + out += stepsize; + } + } else + out += SUPPORT_TREE_COLLISION_RESOLUTION; + while (out < radius || ignore(out)) { + assert(out * SUPPORT_TREE_EXPONENTIAL_FACTOR > out + SUPPORT_TREE_COLLISION_RESOLUTION); + out = out * SUPPORT_TREE_EXPONENTIAL_FACTOR; + } + } + return out; +} + +} diff --git a/src/libslic3r/TreeModelVolumes.hpp b/src/libslic3r/TreeModelVolumes.hpp new file mode 100644 index 000000000..f87204cf5 --- /dev/null +++ b/src/libslic3r/TreeModelVolumes.hpp @@ -0,0 +1,610 @@ +// Tree supports by Thomas Rahm, losely based on Tree Supports by CuraEngine. +// Original source of Thomas Rahm's tree supports: +// https://github.com/ThomasRahm/CuraEngine +// +// Original CuraEngine copyright: +// Copyright (c) 2021 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. + +#ifndef slic3r_TreeModelVolumes_hpp +#define slic3r_TreeModelVolumes_hpp + +#include +#include +#include + +#include + +#include "Point.hpp" +#include "Polygon.hpp" +#include "PrintConfig.hpp" + +namespace Slic3r +{ + +using LayerIndex = int; + +class BuildVolume; +class PrintObject; + +struct TreeSupportMeshGroupSettings { + TreeSupportMeshGroupSettings() = default; + explicit TreeSupportMeshGroupSettings(const PrintObject &print_object); + +/*********************************************************************/ +/* Print parameters, not support specific: */ +/*********************************************************************/ + coord_t layer_height { scaled(0.15) }; + // Maximum Deviation (meshfix_maximum_deviation) + // The maximum deviation allowed when reducing the resolution for the Maximum Resolution setting. If you increase this, + // the print will be less accurate, but the g-code will be smaller. Maximum Deviation is a limit for Maximum Resolution, + // so if the two conflict the Maximum Deviation will always be held true. + coord_t resolution { scaled(0.025) }; + // Minimum Feature Size (aka minimum line width) + // Minimum thickness of thin features. Model features that are thinner than this value will not be printed, while features thicker + // than the Minimum Feature Size will be widened to the Minimum Wall Line Width. + coord_t min_feature_size { scaled(0.1) }; + +/*********************************************************************/ +/* General support parameters: */ +/*********************************************************************/ + + // Support Overhang Angle + // The minimum angle of overhangs for which support is added. At a value of 0° all overhangs are supported, 90° will not provide any support. + double support_angle { 50. * M_PI / 180. }; + // Support Line Width + // Width of a single support structure line. + coord_t support_line_width { scaled(0.4) }; + // Support Roof Line Width: Width of a single support roof line. + coord_t support_roof_line_width { scaled(0.4) }; + // Enable Support Floor (aka bottom interfaces) + // Generate a dense slab of material between the bottom of the support and the model. This will create a skin between the model and support. + bool support_bottom_enable { false }; + // Support Floor Thickness + // The thickness of the support floors. This controls the number of dense layers that are printed on top of places of a model on which support rests. + coord_t support_bottom_height { scaled(1.) }; + bool support_material_buildplate_only { false }; + // Support X/Y Distance + // Distance of the support structure from the print in the X/Y directions. + // minimum: 0, maximum warning: 1.5 * machine_nozzle_tip_outer_diameter + coord_t support_xy_distance { scaled(0.7) }; + // Minimum Support X/Y Distance + // Distance of the support structure from the overhang in the X/Y directions. + // minimum_value: 0, minimum warning": support_xy_distance - support_line_width * 2, maximum warning: support_xy_distance + coord_t support_xy_distance_overhang { scaled(0.2) }; + // Support Top Distance + // Distance from the top of the support to the print. + coord_t support_top_distance { scaled(0.1) }; + // Support Bottom Distance + // Distance from the print to the bottom of the support. + coord_t support_bottom_distance { scaled(0.1) }; + //FIXME likely not needed, optimization for clipping of interface layers + // When checking where there's model above and below the support, take steps of the given height. Lower values will slice slower, while higher values + // may cause normal support to be printed in some places where there should have been support interface. + coord_t support_interface_skip_height { scaled(0.3) }; + // Support Infill Line Directions + // A list of integer line directions to use. Elements from the list are used sequentially as the layers progress and when the end + // of the list is reached, it starts at the beginning again. The list items are separated by commas and the whole list is contained + // in square brackets. Default is an empty list which means use the default angle 0 degrees. +// std::vector support_infill_angles {}; + // Enable Support Roof + // Generate a dense slab of material between the top of support and the model. This will create a skin between the model and support. + bool support_roof_enable { false }; + // Support Roof Thickness + // The thickness of the support roofs. This controls the amount of dense layers at the top of the support on which the model rests. + coord_t support_roof_height { scaled(1.) }; + // Minimum Support Roof Area + // Minimum area size for the roofs of the support. Polygons which have an area smaller than this value will be printed as normal support. + double minimum_roof_area { scaled(scaled(1.)) }; + // A list of integer line directions to use. Elements from the list are used sequentially as the layers progress + // and when the end of the list is reached, it starts at the beginning again. The list items are separated + // by commas and the whole list is contained in square brackets. Default is an empty list which means + // use the default angles (alternates between 45 and 135 degrees if interfaces are quite thick or 90 degrees). + std::vector support_roof_angles {}; + // Support Roof Pattern (aka top interface) + // The pattern with which the roofs of the support are printed. + SupportMaterialInterfacePattern support_roof_pattern { smipAuto }; + // Support Pattern + // The pattern of the support structures of the print. The different options available result in sturdy or easy to remove support. + SupportMaterialPattern support_pattern { smpRectilinear }; + // Support Line Distance + // Distance between the printed support structure lines. This setting is calculated by the support density. + coord_t support_line_spacing { scaled(2.66 - 0.4) }; + // Support Floor Horizontal Expansion + // Amount of offset applied to the floors of the support. + coord_t support_bottom_offset { scaled(0.) }; + // Support Wall Line Count + // The number of walls with which to surround support infill. Adding a wall can make support print more reliably + // and can support overhangs better, but increases print time and material used. + // tree: 1, zig-zag: 0, concentric: 1 + int support_wall_count { 1 }; + // Support Roof Line Distance + // Distance between the printed support roof lines. This setting is calculated by the Support Roof Density, but can be adjusted separately. + coord_t support_roof_line_distance { scaled(0.4) }; + // Minimum Support Area + // Minimum area size for support polygons. Polygons which have an area smaller than this value will not be generated. + coord_t minimum_support_area { scaled(0.) }; + // Minimum Support Floor Area + // Minimum area size for the floors of the support. Polygons which have an area smaller than this value will be printed as normal support. + coord_t minimum_bottom_area { scaled(1.0) }; + // Support Horizontal Expansion + // Amount of offset applied to all support polygons in each layer. Positive values can smooth out the support areas and result in more sturdy support. + coord_t support_offset { scaled(0.) }; + +/*********************************************************************/ +/* Parameters for the Cura tree supports implementation: */ +/*********************************************************************/ + + // Tree Support Maximum Branch Angle + // The maximum angle of the branches, when the branches have to avoid the model. Use a lower angle to make them more vertical and more stable. Use a higher angle to be able to have more reach. + // minimum: 0, minimum warning: 20, maximum: 89, maximum warning": 85 + double support_tree_angle { 60. * M_PI / 180. }; + // Tree Support Branch Diameter Angle + // The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length. + // A bit of an angle can increase stability of the tree support. + // minimum: 0, maximum: 89.9999, maximum warning: 15 + double support_tree_branch_diameter_angle { 5. * M_PI / 180. }; + // Tree Support Branch Distance + // How far apart the branches need to be when they touch the model. Making this distance small will cause + // the tree support to touch the model at more points, causing better overhang but making support harder to remove. + coord_t support_tree_branch_distance { scaled(1.) }; + // Tree Support Branch Diameter + // The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this. + // minimum: 0.001, minimum warning: support_line_width * 2 + coord_t support_tree_branch_diameter { scaled(2.) }; + +/*********************************************************************/ +/* Parameters new to the Thomas Rahm's tree supports implementation: */ +/*********************************************************************/ + + // Tree Support Preferred Branch Angle + // The preferred angle of the branches, when they do not have to avoid the model. Use a lower angle to make them more vertical and more stable. Use a higher angle for branches to merge faster. + // minimum: 0, minimum warning: 10, maximum: support_tree_angle, maximum warning: support_tree_angle-1 + double support_tree_angle_slow { 50. * M_PI / 180. }; + // Tree Support Diameter Increase To Model + // The most the diameter of a branch that has to connect to the model may increase by merging with branches that could reach the buildplate. + // Increasing this reduces print time, but increases the area of support that rests on model + // minimum: 0 + coord_t support_tree_max_diameter_increase_by_merges_when_support_to_model { scaled(1.0) }; + // Tree Support Minimum Height To Model + // How tall a branch has to be if it is placed on the model. Prevents small blobs of support. This setting is ignored when a branch is supporting a support roof. + // minimum: 0, maximum warning: 5 + coord_t support_tree_min_height_to_model { scaled(1.0) }; + // Tree Support Inital Layer Diameter + // Diameter every branch tries to achieve when reaching the buildplate. Improves bed adhesion. + // minimum: 0, maximum warning: 20 + coord_t support_tree_bp_diameter { scaled(7.5) }; + // Tree Support Branch Density + // Adjusts the density of the support structure used to generate the tips of the branches. A higher value results in better overhangs, + // but the supports are harder to remove. Use Support Roof for very high values or ensure support density is similarly high at the top. + // 5%-35% + double support_tree_top_rate { 15. }; + // Tree Support Tip Diameter + // The diameter of the top of the tip of the branches of tree support." + // minimum: min_wall_line_width, minimum warning: min_wall_line_width+0.05, maximum_value: support_tree_branch_diameter, value: support_line_width + coord_t support_tree_tip_diameter { scaled(0.4) }; + + // Support Interface Priority + // How support interface and support will interact when they overlap. Currently only implemented for support roof. + //enum support_interface_priority { support_lines_overwrite_interface_area }; +}; + +class TreeModelVolumes +{ +public: + TreeModelVolumes() = default; + explicit TreeModelVolumes(const PrintObject &print_object, const BuildVolume &build_volume, + coord_t max_move, coord_t max_move_slow, size_t current_mesh_idx, double progress_multiplier, + double progress_offset, const std::vector &additional_excluded_areas = {}); + TreeModelVolumes(TreeModelVolumes&&) = default; + TreeModelVolumes& operator=(TreeModelVolumes&&) = default; + + TreeModelVolumes(const TreeModelVolumes&) = delete; + TreeModelVolumes& operator=(const TreeModelVolumes&) = delete; + + enum class AvoidanceType + { + Slow, + FastSafe, + Fast, + Count + }; + + /*! + * \brief Precalculate avoidances and collisions up to max_layer. + * + * Knowledge about branch angle is used to only calculate avoidances and collisions that may actually be needed. + * Not calling precalculate() will cause the class to lazily calculate avoidances and collisions as needed, which will be a lot slower on systems with more then one or two cores! + */ + void precalculate(const coord_t max_layer); + + /*! + * \brief Provides the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. + * + * The result is a 2D area that would cause nodes of radius \p radius to + * collide with the model. + * + * \param radius The radius of the node of interest + * \param layer_idx The layer of interest + * \param min_xy_dist Is the minimum xy distance used. + * \return Polygons object + */ + const Polygons& getCollision(const coord_t radius, LayerIndex layer_idx, bool min_xy_dist) const; + + /*! + * \brief Provides the areas that have to be avoided by the tree's branches + * in order to reach the build plate. + * + * The result is a 2D area that would cause nodes of radius \p radius to + * collide with the model or be unable to reach the build platform. + * + * The input collision areas are inset by the maximum move distance and + * propagated upwards. + * + * \param radius The radius of the node of interest + * \param layer_idx The layer of interest + * \param type Is the propagation with the maximum move distance slow required. + * \param to_model Does the avoidance allow good connections with the model. + * \param min_xy_dist is the minimum xy distance used. + * \return Polygons object + */ + const Polygons& getAvoidance(coord_t radius, LayerIndex layer_idx, AvoidanceType type, bool to_model, bool min_xy_dist) const; + /*! + * \brief Provides the area represents all areas on the model where the branch does completely fit on the given layer. + * \param radius The radius of the node of interest + * \param layer_idx The layer of interest + * \return Polygons object + */ + const Polygons& getPlaceableAreas(coord_t radius, LayerIndex layer_idx) const; + /*! + * \brief Provides the area that represents the walls, as in the printed area, of the model. This is an abstract representation not equal with the outline. See calculateWallRestrictions for better description. + * \param radius The radius of the node of interest. + * \param layer_idx The layer of interest. + * \param min_xy_dist is the minimum xy distance used. + * \return Polygons object + */ + const Polygons& getWallRestriction(coord_t radius, LayerIndex layer_idx, bool min_xy_dist) const; + /*! + * \brief Round \p radius upwards to either a multiple of m_radius_sample_resolution or a exponentially increasing value + * + * It also adds the difference between the minimum xy distance and the regular one. + * + * \param radius The radius of the node of interest + * \param min_xy_dist is the minimum xy distance used. + * \return The rounded radius + */ + coord_t ceilRadius(const coord_t radius, const bool min_xy_dist) const { + assert(radius >= 0); + return min_xy_dist ? + this->ceilRadius(radius) : + // special case as if a radius 0 is requested it could be to ensure correct xy distance. As such it is beneficial if the collision is as close to the configured values as possible. + radius > 0 ? this->ceilRadius(radius + m_current_min_xy_dist_delta) : m_current_min_xy_dist_delta; + } + /*! + * \brief Round \p radius upwards to the maximum that would still round up to the same value as the provided one. + * + * \param radius The radius of the node of interest + * \param min_xy_dist is the minimum xy distance used. + * \return The maximum radius, resulting in the same rounding. + */ + coord_t getRadiusNextCeil(coord_t radius, bool min_xy_dist) const { + assert(radius > 0); + return min_xy_dist ? + this->ceilRadius(radius) : + this->ceilRadius(radius + m_current_min_xy_dist_delta) - m_current_min_xy_dist_delta; + } + +private: + /*! + * \brief Convenience typedef for the keys to the caches + */ + using RadiusLayerPair = std::pair; + using RadiusLayerPolygonCacheData = std::unordered_map>; + class RadiusLayerPolygonCache { + public: + RadiusLayerPolygonCache() = default; + RadiusLayerPolygonCache(RadiusLayerPolygonCache &&rhs) : data(std::move(rhs.data)) {} + RadiusLayerPolygonCache& operator=(RadiusLayerPolygonCache &&rhs) { data = std::move(rhs.data); return *this; } + + RadiusLayerPolygonCache(const RadiusLayerPolygonCache&) = delete; + RadiusLayerPolygonCache& operator=(const RadiusLayerPolygonCache&) = delete; + + void insert(RadiusLayerPolygonCacheData &&in) { + std::lock_guard guard(this->mutex); + for (auto& d : in) + this->data.emplace(d.first, std::move(d.second)); + } + void insert(std::vector> &&in) { + std::lock_guard guard(this->mutex); + for (auto& d : in) + this->data.emplace(d.first, std::move(d.second)); + } + // by layer + void insert(std::vector> &&in, coord_t radius) { + std::lock_guard guard(this->mutex); + for (auto &d : in) + this->data.emplace(RadiusLayerPair{ radius, d.first }, std::move(d.second)); + } + void insert(std::vector &&in, coord_t first_layer_idx, coord_t radius) { + std::lock_guard guard(this->mutex); + for (auto &d : in) + this->data.emplace(RadiusLayerPair{ radius, first_layer_idx ++ }, std::move(d)); + } + /*! + * \brief Checks a cache for a given RadiusLayerPair and returns it if it is found + * \param key RadiusLayerPair of the requested areas. The radius will be calculated up to the provided layer. + * \return A wrapped optional reference of the requested area (if it was found, an empty optional if nothing was found) + */ + std::optional> getArea(const TreeModelVolumes::RadiusLayerPair &key) const { + std::lock_guard guard(this->mutex); + const auto it = this->data.find(key); + return it == this->data.end() ? + std::optional>{} : std::optional>{ it->second }; + } + /*! + * \brief Get the highest already calculated layer in the cache. + * \param radius The radius for which the highest already calculated layer has to be found. + * \param map The cache in which the lookup is performed. + * + * \return A wrapped optional reference of the requested area (if it was found, an empty optional if nothing was found) + */ + LayerIndex getMaxCalculatedLayer(coord_t radius) const { + std::lock_guard guard(this->mutex); + int max_layer = -1; + // the placeable on model areas do not exist on layer 0, as there can not be model below it. As such it may be possible that layer 1 is available, but layer 0 does not exist. + if (this->data.find({ radius, 1 }) != this->data.end()) + max_layer = 1; + while (this->data.count(TreeModelVolumes::RadiusLayerPair(radius, max_layer + 1)) > 0) + ++ max_layer; + return max_layer; + } + + private: + RadiusLayerPolygonCacheData data; + mutable std::mutex mutex; + }; + + + /*! + * \brief Provides the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. Holes are removed. + * + * The result is a 2D area that would cause nodes of given radius to + * collide with the model or be inside a hole. + * A Hole is defined as an area, in which a branch with m_increase_until_radius radius would collide with the wall. + * minimum xy distance is always used. + * \param radius The radius of the node of interest + * \param layer_idx The layer of interest + * \param min_xy_dist Is the minimum xy distance used. + * \return Polygons object + */ + const Polygons& getCollisionHolefree(coord_t radius, LayerIndex layer_idx) const; + + /*! + * \brief Round \p radius upwards to either a multiple of m_radius_sample_resolution or a exponentially increasing value + * + * \param radius The radius of the node of interest + */ + coord_t ceilRadius(const coord_t radius) const; + + /*! + * \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. + * + * The result is a 2D area that would cause nodes of given radius to + * collide with the model. Result is saved in the cache. + * \param keys RadiusLayerPairs of all requested areas. Every radius will be calculated up to the provided layer. + */ + void calculateCollision(const std::vector &keys); + void calculateCollision(const coord_t radius, const LayerIndex max_layer_idx); + /*! + * \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. Holes are removed. + * + * The result is a 2D area that would cause nodes of given radius to + * collide with the model or be inside a hole. Result is saved in the cache. + * A Hole is defined as an area, in which a branch with m_increase_until_radius radius would collide with the wall. + * \param keys RadiusLayerPairs of all requested areas. Every radius will be calculated up to the provided layer. + */ + void calculateCollisionHolefree(const std::vector &keys); + + /*! + * \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. Holes are removed. + * + * The result is a 2D area that would cause nodes of given radius to + * collide with the model or be inside a hole. Result is saved in the cache. + * A Hole is defined as an area, in which a branch with m_increase_until_radius radius would collide with the wall. + * \param key RadiusLayerPairs the requested areas. The radius will be calculated up to the provided layer. + */ + void calculateCollisionHolefree(RadiusLayerPair key) + { + calculateCollisionHolefree(std::vector{ RadiusLayerPair(key) }); + } + + /*! + * \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model. + * + * The result is a 2D area that would cause nodes of radius \p radius to + * collide with the model. Result is saved in the cache. + * \param keys RadiusLayerPairs of all requested areas. Every radius will be calculated up to the provided layer. + */ + void calculateAvoidance(const std::vector &keys, bool to_build_plate, bool to_model); + + /*! + * \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model. + * + * The result is a 2D area that would cause nodes of radius \p radius to + * collide with the model. Result is saved in the cache. + * \param key RadiusLayerPair of the requested areas. It will be calculated up to the provided layer. + */ + void calculateAvoidance(RadiusLayerPair key, bool to_build_plate, bool to_model) + { + calculateAvoidance(std::vector{ RadiusLayerPair(key) }, to_build_plate, to_model); + } + + /*! + * \brief Creates the areas where a branch of a given radius can be place on the model. + * Result is saved in the cache. + * \param key RadiusLayerPair of the requested areas. It will be calculated up to the provided layer. + */ + void calculatePlaceables(const coord_t radius, const LayerIndex max_required_layer); + + + /*! + * \brief Creates the areas where a branch of a given radius can be placed on the model. + * Result is saved in the cache. + * \param keys RadiusLayerPair of the requested areas. The radius will be calculated up to the provided layer. + */ + void calculatePlaceables(const std::vector &keys); + + /*! + * \brief Creates the areas that can not be passed when expanding an area downwards. As such these areas are an somewhat abstract representation of a wall (as in a printed object). + * + * These areas are at least xy_min_dist wide. When calculating it is always assumed that every wall is printed on top of another (as in has an overlap with the wall a layer below). Result is saved in the corresponding cache. + * + * \param keys RadiusLayerPairs of all requested areas. Every radius will be calculated up to the provided layer. + */ + void calculateWallRestrictions(const std::vector &keys); + + /*! + * \brief Creates the areas that can not be passed when expanding an area downwards. As such these areas are an somewhat abstract representation of a wall (as in a printed object). + * These areas are at least xy_min_dist wide. When calculating it is always assumed that every wall is printed on top of another (as in has an overlap with the wall a layer below). Result is saved in the corresponding cache. + * \param key RadiusLayerPair of the requested area. It well be will be calculated up to the provided layer. + */ + void calculateWallRestrictions(RadiusLayerPair key) + { + calculateWallRestrictions(std::vector{ RadiusLayerPair(key) }); + } + + /*! + * \brief The maximum distance that the center point of a tree branch may move in consecutive layers if it has to avoid the model. + */ + coord_t m_max_move; + /*! + * \brief The maximum distance that the centre-point of a tree branch may + * move in consecutive layers if it does not have to avoid the model + */ + coord_t m_max_move_slow; + /*! + * \brief The smallest maximum resolution for simplify + */ + coord_t m_min_resolution; + + bool m_precalculated = false; + /*! + * \brief The index to access the outline corresponding with the currently processing mesh + */ + size_t m_current_outline_idx; + /*! + * \brief The minimum required clearance between the model and the tree branches + */ + coord_t m_current_min_xy_dist; + /*! + * \brief The difference between the minimum required clearance between the model and the tree branches and the regular one. + */ + coord_t m_current_min_xy_dist_delta; + /*! + * \brief Does at least one mesh allow support to rest on a model. + */ + bool m_support_rests_on_model; +#ifdef SLIC3R_TREESUPPORTS_PROGRESS + /*! + * \brief The progress of the precalculate function for communicating it to the progress bar. + */ + coord_t m_precalculation_progress = 0; + /*! + * \brief The progress multiplier of all values added progress bar. + * Required for the progress bar the behave as expected when areas have to be calculated multiple times + */ + double m_progress_multiplier; + /*! + * \brief The progress offset added to all values communicated to the progress bar. + * Required for the progress bar the behave as expected when areas have to be calculated multiple times + */ + double m_progress_offset; +#endif // SLIC3R_TREESUPPORTS_PROGRESS + /*! + * \brief Increase radius in the resulting drawn branches, even if the avoidance does not allow it. Will be cut later to still fit. + */ + coord_t m_increase_until_radius; + + /*! + * \brief Polygons representing the limits of the printable area of the + * machine + */ + Polygons m_machine_border; + /*! + * \brief Storage for layer outlines and the corresponding settings of the meshes grouped by meshes with identical setting. + */ + std::vector>> m_layer_outlines; + /*! + * \brief Storage for areas that should be avoided, like support blocker or previous generated trees. + */ + std::vector m_anti_overhang; + /*! + * \brief Radii that can be ignored by ceilRadius as they will never be requested, sorted. + */ + std::vector m_ignorable_radii; + + /*! + * \brief Smallest radius a branch can have. This is the radius of a SupportElement with DTT=0. + */ + coord_t m_radius_0; + + /*! + * \brief Caches for the collision, avoidance and areas on the model where support can be placed safely + * at given radius and layer indices. + */ + RadiusLayerPolygonCache m_collision_cache; + RadiusLayerPolygonCache m_collision_cache_holefree; + RadiusLayerPolygonCache m_avoidance_cache; + RadiusLayerPolygonCache m_avoidance_cache_slow; + RadiusLayerPolygonCache m_avoidance_cache_to_model; + RadiusLayerPolygonCache m_avoidance_cache_to_model_slow; + RadiusLayerPolygonCache m_placeable_areas_cache; + + /*! + * \brief Caches to avoid holes smaller than the radius until which the radius is always increased, as they are free of holes. + * Also called safe avoidances, as they are safe regarding not running into holes. + */ + RadiusLayerPolygonCache m_avoidance_cache_holefree; + RadiusLayerPolygonCache m_avoidance_cache_holefree_to_model; + + RadiusLayerPolygonCache& avoidance_cache(const AvoidanceType type, const bool to_model) { + if (to_model) { + switch (type) { + case AvoidanceType::Fast: return m_avoidance_cache_to_model; + case AvoidanceType::Slow: return m_avoidance_cache_to_model_slow; + case AvoidanceType::Count: assert(false); + case AvoidanceType::FastSafe: return m_avoidance_cache_holefree_to_model; + } + } else { + switch (type) { + case AvoidanceType::Fast: return m_avoidance_cache; + case AvoidanceType::Slow: return m_avoidance_cache_slow; + case AvoidanceType::Count: assert(false); + case AvoidanceType::FastSafe: return m_avoidance_cache_holefree; + } + } + assert(false); + return m_avoidance_cache; + } + const RadiusLayerPolygonCache& avoidance_cache(const AvoidanceType type, const bool to_model) const { + return const_cast(this)->avoidance_cache(type, to_model); + } + + /*! + * \brief Caches to represent walls not allowed to be passed over. + */ + RadiusLayerPolygonCache m_wall_restrictions_cache; + + // A different cache for min_xy_dist as the maximal safe distance an influence area can be increased(guaranteed overlap of two walls in consecutive layer) + // is much smaller when min_xy_dist is used. This causes the area of the wall restriction to be thinner and as such just using the min_xy_dist wall + // restriction would be slower. + RadiusLayerPolygonCache m_wall_restrictions_cache_min; + +#ifdef SLIC3R_TREESUPPORTS_PROGRESS + std::unique_ptr m_critical_progress { std::make_unique() }; +#endif // SLIC3R_TREESUPPORTS_PROGRESS +}; + +} + +#endif //slic3r_TreeModelVolumes_hpp diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp new file mode 100644 index 000000000..56ccfd7d1 --- /dev/null +++ b/src/libslic3r/TreeSupport.cpp @@ -0,0 +1,2844 @@ +// Tree supports by Thomas Rahm, losely based on Tree Supports by CuraEngine. +// Original source of Thomas Rahm's tree supports: +// https://github.com/ThomasRahm/CuraEngine +// +// Original CuraEngine copyright: +// Copyright (c) 2021 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. + +#include "TreeSupport.hpp" +#include "BuildVolume.hpp" +#include "ClipperUtils.hpp" +#include "EdgeGrid.hpp" +#include "Fill/Fill.hpp" +#include "Layer.hpp" +#include "Print.hpp" +#include "MultiPoint.hpp" +#include "Polygon.hpp" +#include "Polyline.hpp" +#include "MutablePolygon.hpp" +#include "SupportMaterial.hpp" + +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 + #include //todo Remove! ONLY FOR PUBLIC BETA!! +#endif // _WIN32 + +#include + +#include +#include +#include + +namespace Slic3r +{ + +enum class LineStatus +{ + INVALID, + TO_MODEL, + TO_MODEL_GRACIOUS, + TO_MODEL_GRACIOUS_SAFE, + TO_BP, + TO_BP_SAFE +}; + +using LineInformation = std::vector>; +using LineInformations = std::vector; + +static inline void validate_range(const Point &pt) +{ + static constexpr const int32_t hi = 65536 * 16384; + if (pt.x() > hi || pt.y() > hi || -pt.x() > hi || -pt.y() > hi) + throw ClipperLib::clipperException("Coordinate outside allowed range"); +} + +static inline void validate_range(const Points &points) +{ + for (const Point &p : points) + validate_range(p); +} + +static inline void validate_range(const MultiPoint &mp) +{ + validate_range(mp.points); +} + +static inline void validate_range(const Polygons &polygons) +{ + for (const Polygon &p : polygons) + validate_range(p); +} + +static inline void validate_range(const Polylines &polylines) +{ + for (const Polyline &p : polylines) + validate_range(p); +} + +static inline void validate_range(const LineInformation &lines) +{ + for (const auto& p : lines) + validate_range(p.first); +} + +static inline void validate_range(const LineInformations &lines) +{ + for (const LineInformation &l : lines) + validate_range(l); +} + +static inline void clip_for_diff(const Polygon &src, const BoundingBox &bbox, Polygon &out) +{ + out.clear(); + const size_t cnt = src.points.size(); + if (cnt < 3) + return; + + enum class Side { + Left = 1, + Right = 2, + Top = 4, + Bottom = 8 + }; + + auto sides = [bbox](const Point &p) { + return int(p.x() < bbox.min.x()) * int(Side::Left) + + int(p.x() > bbox.max.x()) * int(Side::Right) + + int(p.y() < bbox.min.y()) * int(Side::Bottom) + + int(p.y() > bbox.max.y()) * int(Side::Top); + }; + + int sides_prev = sides(src.points.back()); + int sides_this = sides(src.points.front()); + const size_t last = cnt - 1; + for (size_t i = 0; i < last; ++ i) { + int sides_next = sides(src.points[i + 1]); + if (// This point is inside. Take it. + sides_this == 0 || + // Either this point is outside and previous or next is inside, or + // the edge possibly cuts corner of the bounding box. + (sides_prev & sides_this & sides_next) == 0) { + out.points.emplace_back(src.points[i]); + sides_prev = sides_this; + } else { + // All the three points (this, prev, next) are outside at the same side. + // Ignore this point. + } + sides_this = sides_next; + } + // For the last point, if src is completely outside bbox, then out.points will be empty. Just use the first point instead. + int sides_next = sides(out.points.empty() ? src.points.front() : out.points.front()); + if (// The last point is inside. Take it. + sides_this == 0 || + // Either this point is outside and previous or next is inside, or + // the edge possibly cuts corner of the bounding box. + (sides_prev & sides_this & sides_next) == 0) + out.points.emplace_back(src.points.back()); +} + +[[nodiscard]] static inline Polygon clip_for_diff(const Polygon &src, const BoundingBox &bbox) +{ + Polygon out; + clip_for_diff(src, bbox, out); + return out; +} + +[[nodiscard]] static inline Polygons clip_for_diff(const Polygons &src, const BoundingBox &bbox) +{ + Polygons out; + out.reserve(src.size()); + for (const Polygon &p : src) + out.emplace_back(clip_for_diff(p, bbox)); + return out; +} + +[[nodiscard]] static inline Polygons diff_clipped(const Polygons &src, const Polygons &clipping) +{ + return diff(src, clip_for_diff(clipping, get_extents(src).inflated(SCALED_EPSILON))); +} + +static constexpr const auto tiny_area_threshold = sqr(scaled(0.001)); + +static std::vector>> group_meshes(const Print &print, const std::vector &print_object_ids) +{ + std::vector>> grouped_meshes; + + //FIXME this is ugly, it does not belong here. + for (size_t object_id = 0; object_id < print_object_ids.size(); ++ object_id) { + const PrintObject &print_object = *print.get_object(object_id); + const PrintObjectConfig &object_config = print_object.config(); + if (object_config.support_material_contact_distance < EPSILON) + // || min_feature_size < scaled(0.1) that is the minimum line width + TreeSupport::TreeSupportSettings::soluble = true; + } + + size_t largest_printed_mesh_idx = 0; + + // Group all meshes that can be processed together. NOTE this is different from mesh-groups! Only one setting object is needed per group, + // as different settings in the same group may only occur in the tip, which uses the original settings objects from the meshes. + for (size_t object_id = 0; object_id < print_object_ids.size(); ++ object_id) { + const PrintObject &print_object = *print.get_object(object_id); +#ifndef NDEBUG + const PrintObjectConfig &object_config = print_object.config(); +#endif // NDEBUG + // Support must be enabled and set to Tree style. + assert(object_config.support_material); + assert(object_config.support_material_style == smsTree); + + bool found_existing_group = false; + TreeSupport::TreeSupportSettings next_settings{ TreeSupportMeshGroupSettings{ print_object } }; + //FIXME for now only a single object per group is enabled. +#if 0 + for (size_t idx = 0; idx < grouped_meshes.size(); ++ idx) + if (next_settings == grouped_meshes[idx].first) { + found_existing_group = true; + grouped_meshes[idx].second.emplace_back(object_id); + // handle some settings that are only used for performance reasons. This ensures that a horrible set setting intended to improve performance can not reduce it drastically. + grouped_meshes[idx].first.performance_interface_skip_layers = std::min(grouped_meshes[idx].first.performance_interface_skip_layers, next_settings.performance_interface_skip_layers); + } +#endif + if (! found_existing_group) + grouped_meshes.emplace_back(next_settings, std::vector{ object_id }); + + // no need to do this per mesh group as adaptive layers and raft setting are not setable per mesh. + if (print.get_object(largest_printed_mesh_idx)->layers().back()->print_z < print_object.layers().back()->print_z) + largest_printed_mesh_idx = object_id; + } + +#if 0 + { + std::vector known_z(storage.meshes[largest_printed_mesh_idx].layers.size()); + for (size_t z = 0; z < storage.meshes[largest_printed_mesh_idx].layers.size(); z++) + known_z[z] = storage.meshes[largest_printed_mesh_idx].layers[z].printZ; + for (size_t idx = 0; idx < grouped_meshes.size(); ++ idx) + grouped_meshes[idx].first.setActualZ(known_z); + } +#endif + + return grouped_meshes; +} + +#if 0 +// todo remove as only for debugging relevant +[[nodiscard]] static std::string getPolygonAsString(const Polygons& poly) +{ + std::string ret; + for (auto path : poly) + for (Point p : path) { + if (ret != "") + ret += ","; + ret += "(" + std::to_string(p.x()) + "," + std::to_string(p.y()) + ")"; + } + return ret; +} +#endif + +//todo Remove! Only relevant for public BETA! +static bool inline g_showed_critical_error = false; +static bool inline g_showed_performance_warning = false; +void TreeSupport::showError(std::string message, bool critical) +{ // todo Remove! ONLY FOR PUBLIC BETA!! + +#if defined(_WIN32) && defined(TREE_SUPPORT_SHOW_ERRORS) + auto bugtype = std::string(critical ? " This is a critical bug. It may cause missing or malformed branches.\n" : "This bug should only decrease performance.\n"); + bool show = (critical && !g_showed_critical_error) || (!critical && !g_showed_performance_warning); + (critical ? g_showed_critical_error : g_showed_performance_warning) = true; + + if (show) + MessageBoxA(nullptr, std::string("TreeSupport_2 MOD detected an error while generating the tree support.\nPlease report this back to me with profile and model.\nRevision 5.0\n" + message + "\n" + bugtype).c_str(), + "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING); +#endif // WIN32 +} + +[[nodiscard]] static const std::vector generate_overhangs(const PrintObject &print_object) +{ + std::vector out(print_object.layer_count(), Polygons{}); + + const PrintObjectConfig &config = print_object.config(); + const bool support_auto = config.support_material_auto.value; + const int support_enforce_layers = config.support_material_enforce_layers.value; + std::vector enforcers_layers{ print_object.slice_support_enforcers() }; + std::vector blockers_layers{ print_object.slice_support_blockers() }; + print_object.project_and_append_custom_facets(false, EnforcerBlockerType::ENFORCER, enforcers_layers); + print_object.project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers_layers); + const int support_threshold = config.support_material_threshold.value; + const bool support_threshold_auto = support_threshold == 0; + // +1 makes the threshold inclusive + double tan_threshold = support_threshold_auto ? 0. : tan(M_PI * double(support_threshold + 1) / 180.); + + tbb::parallel_for(tbb::blocked_range(1, out.size()), + [&print_object, &enforcers_layers, &blockers_layers, support_auto, support_enforce_layers, support_threshold_auto, tan_threshold, &out] + (const tbb::blocked_range &range) { + for (LayerIndex layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { + const Layer ¤t_layer = *print_object.get_layer(layer_id); + const Layer &lower_layer = *print_object.get_layer(layer_id - 1); + // Full overhangs with zero lower_layer_offset and no blockers applied. + Polygons raw_overhangs; + bool raw_overhangs_calculated = false; + // Final overhangs. + Polygons overhangs; + // For how many layers full overhangs shall be supported. + const bool enforced_layer = layer_id < support_enforce_layers; + if (support_auto || enforced_layer) { + float lower_layer_offset; + if (enforced_layer) + lower_layer_offset = 0; + else if (support_threshold_auto) { + float external_perimeter_width = 0; + for (const LayerRegion *layerm : lower_layer.regions()) + external_perimeter_width += layerm->flow(frExternalPerimeter).scaled_width(); + external_perimeter_width /= lower_layer.region_count(); + lower_layer_offset = float(0.5 * external_perimeter_width); + } else + lower_layer_offset = scaled(lower_layer.height / tan_threshold); + overhangs = lower_layer_offset == 0 ? + diff(current_layer.lslices, lower_layer.lslices) : + diff(current_layer.lslices, offset(lower_layer.lslices, lower_layer_offset)); + if (lower_layer_offset == 0) { + raw_overhangs = overhangs; + raw_overhangs_calculated = true; + } + if (! (enforced_layer || blockers_layers.empty() || blockers_layers[layer_id].empty())) + overhangs = diff(overhangs, blockers_layers[layer_id], ApplySafetyOffset::Yes); + } + if (! enforcers_layers.empty() && ! enforcers_layers[layer_id].empty()) + // Has some support enforcers at this layer, apply them to the overhangs, don't apply the support threshold angle. + if (Polygons enforced_overhangs = intersection(raw_overhangs_calculated ? raw_overhangs : diff(current_layer.lslices, lower_layer.lslices), enforcers_layers[layer_id]); + ! enforced_overhangs.empty()) { + //FIXME this is a hack to make enforcers work on steep overhangs. + enforced_overhangs = diff(offset(enforced_overhangs, + //FIXME this is a fudge constant! + scaled(0.4)), + lower_layer.lslices); + overhangs = overhangs.empty() ? std::move(enforced_overhangs) : union_(overhangs, enforced_overhangs); + } + out[layer_id] = std::move(overhangs); + } + }); + + return out; +} + +/*! + * \brief Precalculates all avoidances, that could be required. + * + * \param storage[in] Background storage to access meshes. + * \param currently_processing_meshes[in] Indexes of all meshes that are processed in this iteration + */ +[[nodiscard]] static LayerIndex precalculate(const Print &print, const std::vector &overhangs, const TreeSupport::TreeSupportSettings &config, const std::vector &object_ids, TreeModelVolumes &volumes) +{ + // calculate top most layer that is relevant for support + LayerIndex max_layer = 0; + for (size_t object_id : object_ids) { + const PrintObject &print_object = *print.get_object(object_id); + int max_support_layer_id = 0; + for (int layer_id = 1; layer_id < int(print_object.layer_count()); ++ layer_id) + if (! overhangs[layer_id].empty()) + max_support_layer_id = layer_id; + max_layer = std::max(max_support_layer_id - int(config.z_distance_top_layers), 0); + } + if (max_layer > 0) + // The actual precalculation happens in TreeModelVolumes. + volumes.precalculate(max_layer); + return max_layer; +} + +//FIXME this is an ugly wrapper interface for a single print object and a phony build volume. +void TreeSupport::generateSupportAreas(PrintObject& print_object) +{ + size_t idx = 0; + for (PrintObject *po : print_object.print()->objects()) { + if (po == &print_object) + break; + ++ idx; + } + this->generateSupportAreas(*print_object.print(), BuildVolume(Pointfs{ Vec2d{ -300., -300. }, Vec2d{ -300., +300. }, Vec2d{ +300., +300. }, Vec2d{ +300., -300. } }, 0.), { idx }); +} + +void TreeSupport::generateSupportAreas(Print &print, const BuildVolume &build_volume, const std::vector &print_object_ids) +{ + g_showed_critical_error = false; + g_showed_performance_warning = false; + + std::vector>> grouped_meshes = group_meshes(print, print_object_ids); + if (grouped_meshes.empty()) + return; + + size_t counter = 0; + + // Process every mesh group. These groups can not be processed parallel, as the processing in each group is parallelized, and nested parallelization is disables and slow. + for (std::pair> &processing : grouped_meshes) + { + // process each combination of meshes + // this struct is used to easy retrieve setting. No other function except those in TreeModelVolumes and generateInitialAreas have knowledge of the existence of multiple meshes being processed. + //FIXME this is a copy + m_config = processing.first; + BOOST_LOG_TRIVIAL(info) << "Processing support tree mesh group " << counter + 1 << " of " << grouped_meshes.size() << " containing " << grouped_meshes[counter].second.size() << " meshes."; + auto t_start = std::chrono::high_resolution_clock::now(); +#if 0 + std::vector exclude(num_support_layers); + // get all already existing support areas and exclude them + tbb::parallel_for(tbb::blocked_range(0, num_support_layers), + [&](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { + Polygons exlude_at_layer; + append(exlude_at_layer, storage.support.supportLayers[layer_idx].support_bottom); + append(exlude_at_layer, storage.support.supportLayers[layer_idx].support_roof); + for (auto part : storage.support.supportLayers[layer_idx].support_infill_parts) + append(exlude_at_layer, part.outline); + exclude[layer_idx] = union_(exlude_at_layer); + } + }); +#endif +#ifdef SLIC3R_TREESUPPORTS_PROGRESS + m_progress_multiplier = 1.0 / double(m_grouped_meshes.size()); + m_progress_offset = counter == 0 ? 0 : TREE_PROGRESS_TOTAL * (double(counter) * m_progress_multiplier); +#endif // SLIC3R_TREESUPPORT_PROGRESS + PrintObject &print_object = *print.get_object(processing.second.front()); + m_volumes = TreeModelVolumes(print_object, build_volume, m_config.maximum_move_distance, m_config.maximum_move_distance_slow, processing.second.front(), m_progress_multiplier, m_progress_offset, /* additional_excluded_areas */{}); + + //FIXME generating overhangs just for the furst mesh of the group. + assert(processing.second.size() == 1); + std::vector overhangs = generate_overhangs(*print.get_object(processing.second.front())); + + // ### Precalculate avoidances, collision etc. + size_t num_support_layers = precalculate(print, overhangs, processing.first, processing.second, m_volumes); + if (num_support_layers == 0) + continue; + + auto t_precalc = std::chrono::high_resolution_clock::now(); + + // value is the area where support may be placed. As this is calculated in CreateLayerPathing it is saved and reused in drawAreas + std::vector> move_bounds(num_support_layers); + + // ### Place tips of the support tree + SupportGeneratorLayersPtr bottom_contacts(num_support_layers, nullptr); + SupportGeneratorLayersPtr top_contacts(num_support_layers, nullptr); + SupportGeneratorLayersPtr top_interface_layers(num_support_layers, nullptr); + SupportGeneratorLayersPtr intermediate_layers(num_support_layers, nullptr); + SupportGeneratorLayerStorage layer_storage; + + for (size_t mesh_idx : processing.second) + generateInitialAreas(*print.get_object(mesh_idx), overhangs, move_bounds, top_contacts, top_interface_layers, layer_storage); + auto t_gen = std::chrono::high_resolution_clock::now(); + + // ### Propagate the influence areas downwards. + createLayerPathing(move_bounds); + auto t_path = std::chrono::high_resolution_clock::now(); + + // ### Set a point in each influence area + createNodesFromArea(move_bounds); + auto t_place = std::chrono::high_resolution_clock::now(); + + // ### draw these points as circles + drawAreas(*print.get_object(processing.second.front()), overhangs, move_bounds, + bottom_contacts, top_contacts, intermediate_layers, layer_storage); + + auto t_draw = std::chrono::high_resolution_clock::now(); + auto dur_pre_gen = 0.001 * std::chrono::duration_cast(t_precalc - t_start).count(); + auto dur_gen = 0.001 * std::chrono::duration_cast(t_gen - t_precalc).count(); + auto dur_path = 0.001 * std::chrono::duration_cast(t_path - t_gen).count(); + auto dur_place = 0.001 * std::chrono::duration_cast(t_place - t_path).count(); + auto dur_draw = 0.001 * std::chrono::duration_cast(t_draw - t_place).count(); + auto dur_total = 0.001 * std::chrono::duration_cast(t_draw - t_start).count(); + BOOST_LOG_TRIVIAL(info) << + "Total time used creating Tree support for the currently grouped meshes: " << dur_total << " ms. " + "Different subtasks:\nCalculating Avoidance: " << dur_pre_gen << " ms " + "Creating inital influence areas: " << dur_gen << " ms " + "Influence area creation: " << dur_path << "ms " + "Placement of Points in InfluenceAreas: " << dur_place << "ms " + "Drawing result as support " << dur_draw << " ms"; +// if (m_config.branch_radius==2121) +// BOOST_LOG_TRIVIAL(error) << "Why ask questions when you already know the answer twice.\n (This is not a real bug, please dont report it.)"; + + for (auto &layer : move_bounds) { + for (auto elem : layer) { + delete elem->area; + delete elem; + } + } + + auto remove_undefined_layers = [](SupportGeneratorLayersPtr &layers) { + layers.erase(std::remove_if(layers.begin(), layers.end(), [](const SupportGeneratorLayer* ptr) { return ptr == nullptr; }), layers.end()); + }; + remove_undefined_layers(bottom_contacts); + remove_undefined_layers(top_contacts); + remove_undefined_layers(intermediate_layers); + + // Produce the support G-code. + // Used by both classic and tree supports. + SupportParameters support_params(print_object); + support_params.with_sheath = true; + support_params.support_density = 0; + SupportGeneratorLayersPtr interface_layers, base_interface_layers; + SupportGeneratorLayersPtr raft_layers = generate_raft_base(print_object, support_params, print_object.slicing_parameters(), top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage); +#if 1 //#ifdef SLIC3R_DEBUG + SupportGeneratorLayersPtr layers_sorted = +#endif // SLIC3R_DEBUG + generate_support_layers(print_object, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers); + // Don't fill in the tree supports, make them hollow with just a single sheath line. + generate_support_toolpaths(print_object.support_layers(), print_object.config(), support_params, print_object.slicing_parameters(), + raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers); + + #if 0 +//#ifdef SLIC3R_DEBUG + { + static int iRun = 0; + ++ iRun; + size_t layer_id = 0; + for (int i = 0; i < int(layers_sorted.size());) { + // Find the last layer with roughly the same print_z, find the minimum layer height of all. + // Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should. + int j = i + 1; + coordf_t zmax = layers_sorted[i]->print_z + EPSILON; + bool empty = layers_sorted[i]->polygons.empty(); + for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j) + if (!layers_sorted[j]->polygons.empty()) + empty = false; + if (!empty) { + export_print_z_polygons_to_svg( + debug_out_path("support-%d-%lf.svg", iRun, layers_sorted[i]->print_z).c_str(), + layers_sorted.data() + i, j - i); + export_print_z_polygons_and_extrusions_to_svg( + debug_out_path("support-w-fills-%d-%lf.svg", iRun, layers_sorted[i]->print_z).c_str(), + layers_sorted.data() + i, j - i, + *print_object.support_layers()[layer_id]); + ++layer_id; + } + i = j; + } + } +#endif /* SLIC3R_DEBUG */ + + ++ counter; + } + +// storage.support.generated = true; +} + +/*! + * \brief Converts a Polygons object representing a line into the internal format. + * + * \param polylines[in] The Polyline that will be converted. + * \param layer_idx[in] The current layer. + * \return All lines of the \p polylines object, with information for each point regarding in which avoidance it is currently valid in. + */ +// Called by TreeSupport::generateInitialAreas() +[[nodiscard]] static LineInformations convertLinesToInternal( + const TreeModelVolumes &volumes, const TreeSupport::TreeSupportSettings &config, + const Polylines &polylines, LayerIndex layer_idx) +{ + const bool min_xy_dist = config.xy_distance > config.xy_min_distance; + + LineInformations result; + // Also checks if the position is valid, if it is NOT, it deletes that point + for (const Polyline &line : polylines) { + LineInformation res_line; + for (Point p : line) { + if (! contains(volumes.getAvoidance(config.getRadius(0), layer_idx, TreeModelVolumes::AvoidanceType::FastSafe, false, min_xy_dist), p)) + res_line.emplace_back(p, LineStatus::TO_BP_SAFE); + else if (! contains(volumes.getAvoidance(config.getRadius(0), layer_idx, TreeModelVolumes::AvoidanceType::Fast, false, min_xy_dist), p)) + res_line.emplace_back(p, LineStatus::TO_BP); + else if (config.support_rests_on_model && ! contains(volumes.getAvoidance(config.getRadius(0), layer_idx, TreeModelVolumes::AvoidanceType::FastSafe, true, min_xy_dist), p)) + res_line.emplace_back(p, LineStatus::TO_MODEL_GRACIOUS_SAFE); + else if (config.support_rests_on_model && ! contains(volumes.getAvoidance(config.getRadius(0), layer_idx, TreeModelVolumes::AvoidanceType::Fast, true, min_xy_dist), p)) + res_line.emplace_back(p, LineStatus::TO_MODEL_GRACIOUS); + else if (config.support_rests_on_model && ! contains(volumes.getCollision(config.getRadius(0), layer_idx, min_xy_dist), p)) + res_line.emplace_back(p, LineStatus::TO_MODEL); + else if (!res_line.empty()) { + result.emplace_back(res_line); + res_line.clear(); + } + } + if (!res_line.empty()) { + result.emplace_back(res_line); + res_line.clear(); + } + } + + validate_range(result); + return result; +} + +/*! + * \brief Converts lines in internal format into a Polygons object representing these lines. + * + * \param lines[in] The lines that will be converted. + * \return All lines of the \p lines object as a Polygons object. + */ +[[nodiscard]] static Polylines convertInternalToLines(LineInformations lines) +{ + Polylines result; + for (LineInformation line : lines) { + Polyline path; + for (auto point_data : line) + path.points.emplace_back(point_data.first); + result.emplace_back(std::move(path)); + } + validate_range(result); + return result; +} + +/*! + * \brief Evaluates if a point has to be added now. Required for a splitLines call in generateInitialAreas. + * + * \param current_layer[in] The layer on which the point lies, point and its status. + * \return whether the point is valid. + */ +[[nodiscard]] static bool evaluatePointForNextLayerFunction( + const TreeModelVolumes &volumes, const TreeSupport::TreeSupportSettings &config, + size_t current_layer, std::pair &p) +{ + using AvoidanceType = TreeSupport::AvoidanceType; + const bool min_xy_dist = config.xy_distance > config.xy_min_distance; + if (! contains(volumes.getAvoidance(config.getRadius(0), current_layer - 1, p.second == LineStatus::TO_BP_SAFE ? AvoidanceType::FastSafe : AvoidanceType::Fast, false, min_xy_dist), p.first)) + return true; + if (config.support_rests_on_model && (p.second != LineStatus::TO_BP && p.second != LineStatus::TO_BP_SAFE)) + return ! contains( + p.second == LineStatus::TO_MODEL_GRACIOUS || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE ? + volumes.getAvoidance(config.getRadius(0), current_layer - 1, p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE ? AvoidanceType::FastSafe : AvoidanceType::Fast, true, min_xy_dist) : + volumes.getCollision(config.getRadius(0), current_layer - 1, min_xy_dist), + p.first); + return false; +} + +/*! + * \brief Evaluates which points of some lines are not valid one layer below and which are. Assumes all points are valid on the current layer. Validity is evaluated using supplied lambda. + * + * \param lines[in] The lines that have to be evaluated. + * \param evaluatePoint[in] The function used to evaluate the points. + * \return A pair with which points are still valid in the first slot and which are not in the second slot. + */ +template +[[nodiscard]] static std::pair splitLines(LineInformations lines, EvaluatePointFn evaluatePoint) +{ + // assumes all Points on the current line are valid + + LineInformations keep(1); + LineInformations set_free(1); + enum STATE + { + keeping, + freeing + }; + for (std::vector> line : lines) { + STATE current = keeping; + LineInformation resulting_line; + for (std::pair me : line) { + if (evaluatePoint(me)) { + if (keeping != current) { + if (!resulting_line.empty()) { + set_free.emplace_back(resulting_line); + resulting_line.clear(); + } + current = keeping; + } + resulting_line.emplace_back(me); + } else { + if (freeing != current) { + if (!resulting_line.empty()) { + keep.emplace_back(resulting_line); + resulting_line.clear(); + } + current = freeing; + } + resulting_line.emplace_back(me); + } + } + if (!resulting_line.empty()) { + if (current == keeping) + keep.emplace_back(resulting_line); + else + set_free.emplace_back(resulting_line); + } + } + validate_range(keep); + validate_range(set_free); + return std::pair>>, std::vector>>>(keep, set_free); +} + +// Ported from CURA's PolygonUtils::getNextPointWithDistance() +// Sample a next point at distance "dist" from start_pt on polyline segment (start_idx, start_idx + 1). +// Returns sample point and start index of its segment on polyline if such sample exists. +static std::optional> polyline_sample_next_point_at_distance(const Points &polyline, const Point &start_pt, size_t start_idx, double dist) +{ + const double dist2 = sqr(dist); + const auto dist2i = int64_t(dist2); + static constexpr const auto eps = scaled(0.01); + + for (size_t i = start_idx + 1; i < polyline.size(); ++ i) { + const Point p1 = polyline[i]; + if ((p1 - start_pt).cast().squaredNorm() >= dist2i) { + // The end point is outside the circle with center "start_pt" and radius "dist". + const Point p0 = polyline[i - 1]; + Vec2d v = (p1 - p0).cast(); + double l2v = v.squaredNorm(); + if (l2v < sqr(eps)) { + // Very short segment. + Point c = (p0 + p1) / 2; + if (std::abs((start_pt - c).cast().norm() - dist) < eps) + return std::pair{ c, i - 1 }; + else + continue; + } + Vec2d p0f = (start_pt - p0).cast(); + // Foot point of start_pt into v. + Vec2d foot_pt = v * (p0f.dot(v) / l2v); + // Vector from foot point of "start_pt" to "start_pt". + Vec2d xf = p0f - foot_pt; + // Squared distance of "start_pt" from the ray (p0, p1). + double l2_from_line = xf.squaredNorm(); + double det = dist2 - l2_from_line; + + if (det > - SCALED_EPSILON) { + // The ray (p0, p1) touches or intersects a circle centered at "start_pt" with radius "dist". + // Distance of the circle intersection point from the foot point. + double dist_circle_intersection = std::sqrt(std::max(0., det)); + if ((v - foot_pt).cast().norm() > dist_circle_intersection) { + // Intersection of the circle with the segment (p0, p1) is on the right side (close to p1) from the foot point. + Point p = p0 + (foot_pt + v * (dist_circle_intersection / sqrt(l2v))).cast(); + validate_range(p); + return std::pair{ p, i - 1 }; + } + } + } + } + return {}; +} + +/*! + * \brief Eensures that every line segment is about distance in length. The resulting lines may differ from the original but all points are on the original + * + * \param input[in] The lines on which evenly spaced points should be placed. + * \param distance[in] The distance the points should be from each other. + * \param min_points[in] The amount of points that have to be placed. If not enough can be placed the distance will be reduced to place this many points. + * \return A Polygons object containing the evenly spaced points. Does not represent an area, more a collection of points on lines. + */ +[[nodiscard]] static Polylines ensureMaximumDistancePolyline(const Polylines &input, double distance, size_t min_points) +{ + Polylines result; + for (Polyline part : input) { + if (part.empty()) + continue; + + double len = length(part.points); + Polyline line; + double current_distance = std::max(distance, scaled(0.1)); + if (len < 2 * distance && min_points <= 1) + { + // Insert the opposite point of the first one. + //FIXME pretty expensive + Polyline pl(part); + pl.clip_end(len / 2); + line.points.emplace_back(pl.points.back()); + } + else + { + size_t optimal_end_index = part.size() - 1; + + if (part.front() == part.back()) { + size_t optimal_start_index = 0; + // If the polyline was a polygon, there is a high chance it was an overhang. Overhangs that are <60� tend to be very thin areas, so lets get the beginning and end of them and ensure that they are supported. + // The first point of the line will always be supported, so rotate the order of points in this polyline that one of the two corresponding points that are furthest from each other is in the beginning. + // The other will be manually added (optimal_end_index) + coord_t max_dist2_between_vertecies = 0; + for (size_t idx = 0; idx < part.size() - 1; ++ idx) { + for (size_t inner_idx = 0; inner_idx < part.size() - 1; inner_idx++) { + if ((part[idx] - part[inner_idx]).cast().squaredNorm() > max_dist2_between_vertecies) { + optimal_start_index = idx; + optimal_end_index = inner_idx; + max_dist2_between_vertecies = (part[idx] - part[inner_idx]).cast().squaredNorm(); + } + } + } + std::rotate(part.begin(), part.begin() + optimal_start_index, part.end() - 1); + part[part.size() - 1] = part[0]; // restore that property that this polyline ends where it started. + optimal_end_index = (part.size() + optimal_end_index - optimal_start_index - 1) % (part.size() - 1); + } + + while (line.size() < min_points && current_distance >= scaled(0.1)) + { + line.clear(); + Point current_point = part[0]; + line.points.emplace_back(part[0]); + if (min_points > 1 || (part[0] - part[optimal_end_index]).cast().norm() > current_distance) + line.points.emplace_back(part[optimal_end_index]); + size_t current_index = 0; + std::optional> next_point; + double next_distance = current_distance; + // Get points so that at least min_points are added and they each are current_distance away from each other. If that is impossible, decrease current_distance a bit. + // The input are lines, that means that the line from the last to the first vertex does not have to exist, so exclude all points that are on this line! + while ((next_point = polyline_sample_next_point_at_distance(part.points, current_point, current_index, next_distance))) { + // Not every point that is distance away, is valid, as it may be much closer to another point. This is especially the case when the overhang is very thin. + // So this ensures that the points are actually a certain distance from each other. + // This assurance is only made on a per polygon basis, as different but close polygon may not be able to use support below the other polygon. + double min_distance_to_existing_point = std::numeric_limits::max(); + for (Point p : line) + min_distance_to_existing_point = std::min(min_distance_to_existing_point, (p - next_point->first).cast().norm()); + if (min_distance_to_existing_point >= current_distance) { + // viable point was found. Add to possible result. + line.points.emplace_back(next_point->first); + current_point = next_point->first; + current_index = next_point->second; + next_distance = current_distance; + } else { + if (current_point == next_point->first) { + // In case a fixpoint is encountered, better aggressively overcompensate so the code does not become stuck here... + BOOST_LOG_TRIVIAL(warning) << "Tree Support: Encountered a fixpoint in polyline_sample_next_point_at_distance. This is expected to happen if the distance (currently " << next_distance << + ") is smaller than 100"; + TreeSupport::showError("Encountered issue while placing tips. Some tips may be missing.", true); + if (next_distance > 2 * current_distance) + // This case should never happen, but better safe than sorry. + break; + next_distance += current_distance; + continue; + } + // if the point was too close, the next possible viable point is at least distance-min_distance_to_existing_point away from the one that was just checked. + next_distance = std::max(current_distance - min_distance_to_existing_point, scaled(0.1)); + current_point = next_point->first; + current_index = next_point->second; + } + } + current_distance *= 0.9; + } + } + result.emplace_back(std::move(line)); + } + validate_range(result); + return result; +} + +/*! + * \brief Returns Polylines representing the (infill) lines that will result in slicing the given area + * + * \param area[in] The area that has to be filled with infill. + * \param roof[in] Whether the roofing or regular support settings should be used. + * \param layer_idx[in] The current layer index. + * \param support_infill_distance[in] The distance that should be between the infill lines. + * + * \return A Polygons object that represents the resulting infill lines. + */ +[[nodiscard]] static Polylines generateSupportInfillLines( + const Polygons &polygon, const SupportParameters &support_params, + bool roof, LayerIndex layer_idx, coord_t support_infill_distance) +{ +#if 0 + Polygons gaps; + // as we effectivly use lines to place our supportPoints we may use the Infill class for it, while not made for it it works perfect + + const EFillMethod pattern = roof ? config.roof_pattern : config.support_pattern; + +// const bool zig_zaggify_infill = roof ? pattern == EFillMethod::ZIG_ZAG : config.zig_zaggify_support; + const bool connect_polygons = false; + constexpr coord_t support_roof_overlap = 0; + constexpr size_t infill_multiplier = 1; + constexpr coord_t outline_offset = 0; + const int support_shift = roof ? 0 : support_infill_distance / 2; + const size_t wall_line_count = include_walls && !roof ? config.support_wall_count : 0; + const Point infill_origin; + constexpr Polygons* perimeter_gaps = nullptr; + constexpr bool use_endpieces = true; + const bool connected_zigzags = roof ? false : config.connect_zigzags; + const size_t zag_skip_count = roof ? 0 : config.zag_skip_count; + constexpr coord_t pocket_size = 0; + std::vector angles = roof ? config.support_roof_angles : config.support_infill_angles; + std::vector toolpaths; + + const coord_t z = config.getActualZ(layer_idx); + int divisor = static_cast(angles.size()); + int index = ((layer_idx % divisor) + divisor) % divisor; + const AngleRadians fill_angle = angles[index]; + Infill roof_computation(pattern, true /* zig_zaggify_infill */, connect_polygons, polygon, + roof ? config.support_roof_line_width : config.support_line_width, support_infill_distance, support_roof_overlap, infill_multiplier, + fill_angle, z, support_shift, config.resolution, wall_line_count, infill_origin, + perimeter_gaps, connected_zigzags, use_endpieces, false /* skip_some_zags */, zag_skip_count, pocket_size); + Polygons polygons; + Polygons lines; + roof_computation.generate(toolpaths, polygons, lines, config.settings); + append(lines, to_polylines(polygons)); + return lines; +#else +#ifdef _WIN32 + if (! BoundingBox(Point::new_scale(-170., -170.), Point::new_scale(170., 170.)).contains(get_extents(polygon))) + ::MessageBoxA(nullptr, "TreeSupport infill kravsky", "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING); +#endif // _WIN32 + + const Flow &flow = roof ? support_params.support_material_interface_flow : support_params.support_material_flow; + std::unique_ptr filler = std::unique_ptr(Fill::new_from_type(roof ? support_params.interface_fill_pattern : support_params.base_fill_pattern)); + FillParams fill_params; + + filler->layer_id = layer_idx; + filler->spacing = flow.spacing(); + filler->angle = roof ? + //fixme support_layer.interface_id() instead of layer_idx + (support_params.interface_angle + (layer_idx & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)) : + support_params.base_angle; + + fill_params.density = float(roof ? support_params.interface_density : scaled(filler->spacing) / (scaled(filler->spacing) + float(support_infill_distance))); + fill_params.dont_adjust = true; + + Polylines out; + for (ExPolygon &expoly : union_ex(polygon)) { + // The surface type does not matter. + assert(area(expoly) > 0.); +#ifdef _WIN32 + if (area(expoly) <= 0.) + ::MessageBoxA(nullptr, "TreeSupport infill negative area", "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING); +#endif // _WIN32 + assert(intersecting_edges(to_polygons(expoly)).empty()); +#ifdef _WIN32 + if (! intersecting_edges(to_polygons(expoly)).empty()) + ::MessageBoxA(nullptr, "TreeSupport infill self intersections", "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING); +#endif // _WIN32 + Surface surface(stInternal, std::move(expoly)); + try { + Polylines pl = filler->fill_surface(&surface, fill_params); + assert(pl.empty() || get_extents(surface.expolygon).inflated(SCALED_EPSILON).contains(get_extents(pl))); +#ifdef _WIN32 + if (! pl.empty() && ! get_extents(surface.expolygon).inflated(SCALED_EPSILON).contains(get_extents(pl))) + ::MessageBoxA(nullptr, "TreeSupport infill failure", "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING); +#endif // _WIN32 + append(out, std::move(pl)); + } catch (InfillFailedException &) { + } + } + validate_range(out); + return out; +#endif +} + +/*! + * \brief Unions two Polygons. Ensures that if the input is non empty that the output also will be non empty. + * \param first[in] The first Polygon. + * \param second[in] The second Polygon. + * \return The union of both Polygons + */ +[[nodiscard]] static Polygons safeUnion(const Polygons first, const Polygons second = Polygons()) +{ + // unionPolygons can slowly remove Polygons under certain circumstances, because of rounding issues (Polygons that have a thin area). + // This does not cause a problem when actually using it on large areas, but as influence areas (representing centerpoints) can be very thin, this does occur so this ugly workaround is needed + // Here is an example of a Polygons object that will loose vertices when unioning, and will be gone after a few times unionPolygons was called: + /* + Polygons example; + Polygon exampleInner; + exampleInner.add(Point(120410,83599));//A + exampleInner.add(Point(120384,83643));//B + exampleInner.add(Point(120399,83618));//C + exampleInner.add(Point(120414,83591));//D + exampleInner.add(Point(120423,83570));//E + exampleInner.add(Point(120419,83580));//F + example.add(exampleInner); + for(int i=0;i<10;i++){ + log("Iteration %d Example area: %f\n",i,area(example)); + example=example.unionPolygons(); + } +*/ + + Polygons result; + if (! first.empty() || ! second.empty()) { + result = union_(first, second); + if (result.empty()) { + BOOST_LOG_TRIVIAL(debug) << "Caught an area destroying union, enlarging areas a bit."; + // just take the few lines we have, and offset them a tiny bit. Needs to be offsetPolylines, as offset may aleady have problems with the area. + result = union_(offset(to_polylines(first), scaled(0.002), jtMiter, 1.2), offset(to_polylines(second), scaled(0.002), jtMiter, 1.2)); + } + } + + return result; +} + +/*! + * \brief Offsets (increases the area of) a polygons object in multiple steps to ensure that it does not lag through over a given obstacle. + * \param me[in] Polygons object that has to be offset. + * \param distance[in] The distance by which me should be offset. Expects values >=0. + * \param collision[in] The area representing obstacles. + * \param last_step_offset_without_check[in] The most it is allowed to offset in one step. + * \param min_amount_offset[in] How many steps have to be done at least. As this uses round offset this increases the amount of vertices, which may be required if Polygons get very small. Required as arcTolerance is not exposed in offset, which should result with a similar result. + * \return The resulting Polygons object. + */ +[[nodiscard]] static Polygons safeOffsetInc(const Polygons& me, coord_t distance, const Polygons& collision, coord_t safe_step_size, coord_t last_step_offset_without_check, size_t min_amount_offset) +{ + bool do_final_difference = last_step_offset_without_check == 0; + Polygons ret = safeUnion(me); // ensure sane input + + // Trim the collision polygons with the region of interest for diff() efficiency. + Polygons collision_trimmed_buffer; + auto collision_trimmed = [&collision_trimmed_buffer, &collision, &ret, distance]() -> const Polygons& { + if (collision_trimmed_buffer.empty() && ! collision.empty()) + collision_trimmed_buffer = clip_for_diff(collision, get_extents(ret).inflated(std::max(0, distance) + SCALED_EPSILON)); + return collision_trimmed_buffer; + }; + + if (distance == 0) + return do_final_difference ? diff(ret, collision_trimmed()) : union_(ret); + if (safe_step_size < 0 || last_step_offset_without_check < 0) { + BOOST_LOG_TRIVIAL(error) << "Offset increase got invalid parameter!"; + TreeSupport::showError("Negative offset distance... How did you manage this ?", true); + return do_final_difference ? diff(ret, collision_trimmed()) : union_(ret); + } + + coord_t step_size = safe_step_size; + int steps = distance > last_step_offset_without_check ? (distance - last_step_offset_without_check) / step_size : 0; + if (distance - steps * step_size > last_step_offset_without_check) { + if ((steps + 1) * step_size <= distance) + // This will be the case when last_step_offset_without_check >= safe_step_size + ++ steps; + else + do_final_difference = true; + } + if (steps + (distance < last_step_offset_without_check || distance % step_size != 0) < min_amount_offset && min_amount_offset > 1) { + // yes one can add a bool as the standard specifies that a result from compare operators has to be 0 or 1 + // reduce the stepsize to ensure it is offset the required amount of times + step_size = distance / min_amount_offset; + if (step_size >= safe_step_size) { + // effectivly reduce last_step_offset_without_check + step_size = safe_step_size; + steps = min_amount_offset; + } else + steps = distance / step_size; + } + // offset in steps + for (int i = 0; i < steps; ++ i) { + ret = diff(offset(ret, step_size, ClipperLib::jtRound, scaled(0.01)), collision_trimmed()); + // ensure that if many offsets are done the performance does not suffer extremely by the new vertices of jtRound. + if (i % 10 == 7) + ret = polygons_simplify(ret, scaled(0.015)); + } + // offset the remainder + float last_offset = distance - steps * step_size; + if (last_offset > SCALED_EPSILON) + ret = offset(ret, distance - steps * step_size, ClipperLib::jtRound, scaled(0.01)); + ret = polygons_simplify(ret, scaled(0.015)); + + if (do_final_difference) + ret = diff(ret, collision_trimmed()); + return union_(ret); +} + +static inline SupportGeneratorLayer& layer_initialize( + SupportGeneratorLayer &layer_new, + const SupporLayerType layer_type, + const SlicingParameters &slicing_params, + const size_t layer_idx) +{ + layer_new.layer_type = layer_type; + layer_new.print_z = slicing_params.object_print_z_min + slicing_params.first_object_layer_height + layer_idx * slicing_params.layer_height; + layer_new.height = layer_idx == 0 ? slicing_params.first_object_layer_height : slicing_params.layer_height; + layer_new.bottom_z = layer_idx == 0 ? slicing_params.object_print_z_min : layer_new.print_z - layer_new.height; + return layer_new; +} + +// Using the std::deque as an allocator. +inline SupportGeneratorLayer& layer_allocate( + std::deque &layer_storage, + SupporLayerType layer_type, + const SlicingParameters &slicing_params, + size_t layer_idx) +{ + //FIXME take raft into account. + layer_storage.push_back(SupportGeneratorLayer()); + return layer_initialize(layer_storage.back(), layer_type, slicing_params, layer_idx); +} + +inline SupportGeneratorLayer& layer_allocate( + std::deque &layer_storage, + tbb::spin_mutex& layer_storage_mutex, + SupporLayerType layer_type, + const SlicingParameters &slicing_params, + size_t layer_idx) +{ + tbb::spin_mutex::scoped_lock lock(layer_storage_mutex); + layer_storage.push_back(SupportGeneratorLayer()); + return layer_initialize(layer_storage.back(), layer_type, slicing_params, layer_idx); +} + +void TreeSupport::generateInitialAreas( + const PrintObject &print_object, + const std::vector &overhangs, + std::vector> &move_bounds, + SupportGeneratorLayersPtr &top_contacts, + SupportGeneratorLayersPtr &top_interface_layers, + SupportGeneratorLayerStorage &layer_storage) +{ + static constexpr const auto base_radius = scaled(0.01); + const Polygon base_circle = make_circle(base_radius, SUPPORT_TREE_CIRCLE_RESOLUTION); + TreeSupportMeshGroupSettings mesh_group_settings(print_object); + TreeSupportSettings mesh_config{ mesh_group_settings }; + SupportParameters support_params(print_object); + support_params.with_sheath = true; + support_params.support_density = 0; + + const size_t z_distance_delta = mesh_config.z_distance_top_layers + 1; // To ensure z_distance_top_layers are left empty between the overhang (zeroth empty layer), the support has to be added z_distance_top_layers+1 layers below + + const bool min_xy_dist = mesh_config.xy_distance > mesh_config.xy_min_distance; + +#if 0 + if (mesh.overhang_areas.size() <= z_distance_delta) + return; +#endif + + const coord_t connect_length = (mesh_config.support_line_width * 100. / mesh_group_settings.support_tree_top_rate) + std::max(2. * mesh_config.min_radius - 1.0 * mesh_config.support_line_width, 0.0); + // As r*r=x*x+y*y (circle equation): If a circle with center at (0,0) the top most point is at (0,r) as in y=r. + // This calculates how far one has to move on the x-axis so that y=r-support_line_width/2. + // In other words how far does one need to move on the x-axis to be support_line_width/2 away from the circle line. + // As a circle is round this length is identical for every axis as long as the 90 degrees angle between both remains. + const coord_t circle_length_to_half_linewidth_change = mesh_config.min_radius < mesh_config.support_line_width ? mesh_config.min_radius / 2 : sqrt(sqr(mesh_config.min_radius) - sqr(mesh_config.min_radius - mesh_config.support_line_width / 2)); + // Extra support offset to compensate for larger tip radiis. Also outset a bit more when z overwrites xy, because supporting something with a part of a support line is better than not supporting it at all. + //FIXME Vojtech: This is not sufficient for support enforcers to work. + //FIXME There is no account for the support overhang angle. + //FIXME There is no account for the width of the collision regions. + const coord_t extra_outset = std::max(coord_t(0), mesh_config.min_radius - mesh_config.support_line_width) + (min_xy_dist ? mesh_config.support_line_width / 2 : 0) + //FIXME this is a heuristic value for support enforcers to work. +// + 10 * mesh_config.support_line_width; + ; + const size_t support_roof_layers = mesh_group_settings.support_roof_enable ? (mesh_group_settings.support_roof_height + mesh_config.layer_height / 2) / mesh_config.layer_height : 0; + const bool roof_enabled = support_roof_layers != 0; + const bool force_tip_to_roof = sqr(mesh_config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area && roof_enabled; + //FIXME mesh_group_settings.support_angle does not apply to enforcers and also it does not apply to automatic support angle (by half the external perimeter width). + const coord_t max_overhang_speed = (mesh_group_settings.support_angle < 0.5 * M_PI) ? (coord_t)(tan(mesh_group_settings.support_angle) * mesh_config.layer_height) : std::numeric_limits::max(); + const size_t max_overhang_insert_lag = std::max((size_t)round_up_divide(mesh_config.xy_distance, max_overhang_speed / 2), 2 * mesh_config.z_distance_top_layers); // cap for how much layer below the overhang a new support point may be added, as other than with regular support every new inserted point may cause extra material and time cost. Could also be an user setting or differently calculated. Idea is that if an overhang does not turn valid in double the amount of layers a slope of support angle would take to travel xy_distance, nothing reasonable will come from it. The 2*z_distance_delta is only a catch for when the support angle is very high. + + //FIXME + size_t num_support_layers = print_object.layer_count(); + std::vector> already_inserted(num_support_layers - z_distance_delta); + + std::mutex mutex_layer_storage, mutex_movebounds; + tbb::parallel_for(tbb::blocked_range(1, num_support_layers - z_distance_delta), + [this, &print_object, &overhangs, &mesh_config, &mesh_group_settings, &support_params, + z_distance_delta, min_xy_dist, force_tip_to_roof, roof_enabled, support_roof_layers, extra_outset, circle_length_to_half_linewidth_change, connect_length, max_overhang_insert_lag, + &base_circle, &mutex_layer_storage, &mutex_movebounds, &top_contacts, &layer_storage, &already_inserted, + &move_bounds](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { + if (overhangs[layer_idx + z_distance_delta].empty()) + continue; + // take the least restrictive avoidance possible + Polygons relevant_forbidden; + { + const Polygons &relevant_forbidden_raw = (mesh_config.support_rests_on_model ? + (SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL ? m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx, AvoidanceType::Fast, true, min_xy_dist) : + m_volumes.getCollision(mesh_config.getRadius(0), layer_idx, min_xy_dist)) : + m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx, AvoidanceType::Fast, false, min_xy_dist)); + // prevent rounding errors down the line, points placed directly on the line of the forbidden area may not be added otherwise. + relevant_forbidden = offset(union_ex(relevant_forbidden_raw), scaled(0.005), jtMiter, 1.2); + } + + auto generateLines = [&](const Polygons& area, bool roof, LayerIndex layer_idx) -> Polylines { + const coord_t support_infill_distance = roof ? mesh_group_settings.support_roof_line_distance : mesh_group_settings.support_tree_branch_distance; + return generateSupportInfillLines(area, support_params, roof, layer_idx, support_infill_distance); + }; + + auto addPointAsInfluenceArea = [&](std::pair p, size_t dtt, LayerIndex insert_layer, size_t dont_move_until, bool roof, bool skip_ovalisation) + { + bool to_bp = p.second == LineStatus::TO_BP || p.second == LineStatus::TO_BP_SAFE; + bool gracious = to_bp || p.second == LineStatus::TO_MODEL_GRACIOUS || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE; + bool safe_radius = p.second == LineStatus::TO_BP_SAFE || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE; + if (!mesh_config.support_rests_on_model && !to_bp) { + BOOST_LOG_TRIVIAL(warning) << "Tried to add an invalid support point"; + TreeSupport::showError("Unable to add tip. Some overhang may not be supported correctly.", true); + return; + } + Polygon circle; + for (Point corner : base_circle) + circle.points.emplace_back(p.first + corner); + { + std::lock_guard critical_section_movebounds(mutex_movebounds); + if (! already_inserted[insert_layer].count(p.first / ((mesh_config.min_radius + 1) / 10))) { + // normalize the point a bit to also catch points which are so close that inserting it would achieve nothing + already_inserted[insert_layer].emplace(p.first / ((mesh_config.min_radius + 1) / 10)); + SupportElement* elem = new SupportElement(dtt, insert_layer, p.first, to_bp, gracious, min_xy_dist, dont_move_until, roof, safe_radius, force_tip_to_roof, skip_ovalisation); + elem->area = new Polygons(); + validate_range(circle); + elem->area->emplace_back(std::move(circle)); + move_bounds[insert_layer].emplace(elem); + } + } + }; + + auto addLinesAsInfluenceAreas = [&](LineInformations lines, size_t roof_tip_layers, LayerIndex insert_layer_idx, bool supports_roof, size_t dont_move_until) + { + validate_range(lines); + // Add tip area as roof (happens when minimum roof area > minimum tip area) if possible + size_t dtt_roof_tip; + for (dtt_roof_tip = 0; dtt_roof_tip < roof_tip_layers && insert_layer_idx - dtt_roof_tip >= 1; dtt_roof_tip++) + { + auto evaluateRoofWillGenerate = [&](std::pair p) { + //FIXME Vojtech: The circle is just shifted, it has a known size, the infill should fit all the time! +#if 0 + Polygon roof_circle; + for (Point corner : base_circle) + roof_circle.points.emplace_back(p.first + corner * mesh_config.min_radius); + return !generateSupportInfillLines({ roof_circle }, mesh_config, true, insert_layer_idx - dtt_roof_tip, mesh_config.support_roof_line_distance).empty(); +#else + return true; +#endif + }; + + std::pair split = + // keep all lines that are still valid on the next layer + splitLines(lines, [this, insert_layer_idx, dtt_roof_tip](std::pair &p){ return evaluatePointForNextLayerFunction(m_volumes, m_config, insert_layer_idx - dtt_roof_tip, p); }); + + for (LineInformation line : split.second) // add all points that would not be valid + for (std::pair point_data : line) + addPointAsInfluenceArea(point_data, 0, insert_layer_idx - dtt_roof_tip, roof_tip_layers - dtt_roof_tip, dtt_roof_tip != 0, false); + + // not all roofs are guaranteed to actually generate lines, so filter these out and add them as points + split = splitLines(split.first, evaluateRoofWillGenerate); + lines = split.first; + + for (LineInformation line : split.second) + for (std::pair point_data : line) + addPointAsInfluenceArea(point_data, 0, insert_layer_idx - dtt_roof_tip, roof_tip_layers - dtt_roof_tip, dtt_roof_tip != 0, false); + + // add all tips as roof to the roof storage + Polygons added_roofs; + for (LineInformation line : lines) + for (std::pair p : line) { + Polygon roof_circle; + for (Point corner : base_circle) + roof_circle.points.emplace_back(p.first + corner * mesh_config.min_radius / base_radius); + added_roofs.emplace_back(roof_circle); + } + if (! added_roofs.empty()) { + added_roofs = union_(added_roofs); + { + std::lock_guard lock(mutex_layer_storage); + SupportGeneratorLayer *&l = top_contacts[insert_layer_idx - dtt_roof_tip]; + if (l == nullptr) + l = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), insert_layer_idx - dtt_roof_tip); + append(l->polygons, std::move(added_roofs)); + } + } + } + + for (LineInformation line : lines) { + bool disable_ovalistation = mesh_config.min_radius < 3 * mesh_config.support_line_width && roof_tip_layers == 0 && dtt_roof_tip == 0 && line.size() > 5; // If a line consists of enough tips, the assumption is that it is not a single tip, but part of a simulated support pattern. Ovalisation should be disabled for these to improve the quality of the lines when tip_diameter=line_width + for (auto point_data : line) + addPointAsInfluenceArea(point_data, 0, insert_layer_idx - dtt_roof_tip, dont_move_until > dtt_roof_tip ? dont_move_until - dtt_roof_tip : 0, dtt_roof_tip != 0 || supports_roof, disable_ovalistation); + } + }; + + // every overhang has saved if a roof should be generated for it. This can NOT be done in the for loop as an area may NOT have a roof + // even if it is larger than the minimum_roof_area when it is only larger because of the support horizontal expansion and + // it would not have a roof if the overhang is offset by support roof horizontal expansion instead. (At least this is the current behavior of the regular support) + Polygons overhang_regular; + { + const Polygons &overhang_raw = overhangs[layer_idx + z_distance_delta]; + overhang_regular = mesh_group_settings.support_offset == 0 ? + overhang_raw : + safeOffsetInc(overhang_raw, mesh_group_settings.support_offset, relevant_forbidden, mesh_config.min_radius * 1.75 + mesh_config.xy_min_distance, 0, 1); + // offset ensures that areas that could be supported by a part of a support line, are not considered unsupported overhang + Polygons remaining_overhang = intersection( + diff(mesh_group_settings.support_offset == 0 ? + overhang_raw : + offset(union_ex(overhang_raw), mesh_group_settings.support_offset, jtMiter, 1.2), + offset(union_ex(overhang_regular), mesh_config.support_line_width * 0.5, jtMiter, 1.2)), + relevant_forbidden); + + // Offset the area to compensate for large tip radiis. Offset happens in multiple steps to ensure the tip is as close to the original overhang as possible. + //+mesh_config.support_line_width / 80 to avoid calculating very small (useless) offsets because of rounding errors. + //FIXME likely a better approach would be to find correspondences between the full overhang and the trimmed overhang + // and if there is no correspondence, project the missing points to the clipping curve. + for (coord_t extra_total_offset_acc = 0; ! remaining_overhang.empty() && extra_total_offset_acc + mesh_config.support_line_width / 8 < extra_outset; ) { + const coord_t offset_current_step = std::min( + extra_total_offset_acc + 2 * mesh_config.support_line_width > mesh_config.min_radius ? + mesh_config.support_line_width / 8 : + circle_length_to_half_linewidth_change, + extra_outset - extra_total_offset_acc); + extra_total_offset_acc += offset_current_step; + const Polygons &raw_collision = m_volumes.getCollision(0, layer_idx, true); + const coord_t offset_step = mesh_config.xy_min_distance + mesh_config.support_line_width; + // Reducing the remaining overhang by the areas already supported. + //FIXME 1.5 * extra_total_offset_acc seems to be too much, it may remove some remaining overhang without being supported at all. + remaining_overhang = diff(remaining_overhang, safeOffsetInc(overhang_regular, 1.5 * extra_total_offset_acc, raw_collision, offset_step, 0, 1)); + // Extending the overhangs by the inflated remaining overhangs. + overhang_regular = union_(overhang_regular, diff(safeOffsetInc(remaining_overhang, extra_total_offset_acc, raw_collision, offset_step, 0, 1), relevant_forbidden)); + } + // If the xy distance overrides the z distance, some support needs to be inserted further down. + //=> Analyze which support points do not fit on this layer and check if they will fit a few layers down (while adding them an infinite amount of layers down would technically be closer the the setting description, it would not produce reasonable results. ) + if (! min_xy_dist) { + LineInformations overhang_lines; + { + //Vojtech: Generate support heads at support_tree_branch_distance spacing by producing a zig-zag infill at support_tree_branch_distance spacing, + // which is then resmapled + // support_line_width to form a line here as otherwise most will be unsupported. Technically this violates branch distance, + // mbut not only is this the only reasonable choice, but it ensures consistent behavior as some infill patterns generate + // each line segment as its own polyline part causing a similar line forming behavior. Also it is assumed that + // the area that is valid a layer below is to small for support roof. + Polylines polylines = ensureMaximumDistancePolyline(generateLines(remaining_overhang, false, layer_idx), mesh_config.min_radius, 1); + if (polylines.size() <= 3) + // add the outer wall to ensure it is correct supported instead + polylines = ensureMaximumDistancePolyline(to_polylines(remaining_overhang), connect_length, 3); + for (const auto &line : polylines) { + LineInformation res_line; + for (Point p : line) + res_line.emplace_back(p, LineStatus::INVALID); + overhang_lines.emplace_back(res_line); + } + validate_range(overhang_lines); + } + for (size_t lag_ctr = 1; lag_ctr <= max_overhang_insert_lag && !overhang_lines.empty() && layer_idx - coord_t(lag_ctr) >= 1; lag_ctr++) { + // get least restricted avoidance for layer_idx-lag_ctr + const Polygons &relevant_forbidden_below = (mesh_config.support_rests_on_model ? (SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL ? m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx - lag_ctr, AvoidanceType::Fast, true, min_xy_dist) : m_volumes.getCollision(mesh_config.getRadius(0), layer_idx - lag_ctr, min_xy_dist)) : m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx - lag_ctr, AvoidanceType::Fast, false, min_xy_dist)); + // it is not required to offset the forbidden area here as the points wont change: If points here are not inside the forbidden area neither will they be later when placing these points, as these are the same points. + auto evaluatePoint = [&](std::pair p) { return contains(relevant_forbidden_below, p.first); }; + + std::pair split = splitLines(overhang_lines, evaluatePoint); // keep all lines that are invalid + overhang_lines = split.first; + LineInformations fresh_valid_points = convertLinesToInternal(m_volumes, m_config, convertInternalToLines(split.second), layer_idx - lag_ctr); // set all now valid lines to their correct LineStatus. Easiest way is to just discard Avoidance information for each point and evaluate them again. + validate_range(fresh_valid_points); + + addLinesAsInfluenceAreas(fresh_valid_points, (force_tip_to_roof && lag_ctr <= support_roof_layers) ? support_roof_layers : 0, layer_idx - lag_ctr, false, roof_enabled ? support_roof_layers : 0); + } + } + } + + Polygons overhang_roofs; + std::vector> overhang_processing; + if (roof_enabled) { + static constexpr const coord_t support_roof_offset = 0; + overhang_roofs = safeOffsetInc(overhangs[layer_idx + z_distance_delta], support_roof_offset, relevant_forbidden, mesh_config.min_radius * 2 + mesh_config.xy_min_distance, 0, 1); + if (mesh_group_settings.minimum_support_area > 0) + remove_small(overhang_roofs, mesh_group_settings.minimum_roof_area); + overhang_regular = diff(overhang_regular, overhang_roofs, ApplySafetyOffset::Yes); + for (ExPolygon &roof_part : union_ex(overhang_roofs)) + overhang_processing.emplace_back(std::move(roof_part), true); + } + if (mesh_group_settings.minimum_support_area > 0) + remove_small(overhang_regular, mesh_group_settings.minimum_support_area); + + for (ExPolygon &support_part : union_ex(overhang_regular)) + overhang_processing.emplace_back(std::move(support_part), false); + + for (const std::pair &overhang_pair : overhang_processing) { + const bool roof_allowed_for_this_part = overhang_pair.second; + Polygons overhang_outset = to_polygons(overhang_pair.first); + const size_t min_support_points = std::max(coord_t(1), std::min(coord_t(3), coord_t(total_length(overhang_outset) / connect_length))); + LineInformations overhang_lines; + Polygons last_overhang = overhang_outset; + size_t dtt_roof = 0; + // Sometimes roofs could be empty as the pattern does not generate lines if the area is narrow enough (i am looking at you, concentric infill). + // To catch these cases the added roofs are saved to be evaluated later. + std::vector added_roofs(support_roof_layers); + + // Assumption is that roof will support roof further up to avoid a lot of unnecessary branches. Each layer down it is checked whether the roof area + // is still large enough to be a roof and aborted as soon as it is not. This part was already reworked a few times, and there could be an argument + // made to change it again if there are actual issues encountered regarding supporting roofs. + // Main problem is that some patterns change each layer, so just calculating points and checking if they are still valid an layer below is not useful, + // as the pattern may be different one layer below. Same with calculating which points are now no longer being generated as result from + // a decreasing roof, as there is no guarantee that a line will be above these points. Implementing a separate roof support behavior + // for each pattern harms maintainability as it very well could be >100 LOC + if (roof_allowed_for_this_part) { + for (dtt_roof = 0; dtt_roof < support_roof_layers && layer_idx - dtt_roof >= 1; dtt_roof++) { + // here the roof is handled. If roof can not be added the branches will try to not move instead + Polygons forbidden_next; + { + const Polygons &forbidden_next_raw = mesh_config.support_rests_on_model ? + (SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL ? + m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx - (dtt_roof + 1), AvoidanceType::Fast, true, min_xy_dist) : + m_volumes.getCollision(mesh_config.getRadius(0), layer_idx - (dtt_roof + 1), min_xy_dist)) : + m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx - (dtt_roof + 1), AvoidanceType::Fast, false, min_xy_dist); + // prevent rounding errors down the line + forbidden_next = offset(union_ex(forbidden_next_raw), scaled(0.005), jtMiter, 1.2); + } + Polygons overhang_outset_next = diff(overhang_outset, forbidden_next); + if (area(overhang_outset_next) < mesh_group_settings.minimum_roof_area) { + // next layer down the roof area would be to small so we have to insert our roof support here. Also convert squaremicrons to squaremilimeter + size_t dtt_before = dtt_roof > 0 ? dtt_roof - 1 : 0; + if (dtt_roof != 0) { + // Produce support head points supporting an interface layer: First produce the interface lines, then sample them. + overhang_lines = convertLinesToInternal(m_volumes, m_config, + ensureMaximumDistancePolyline(generateLines(last_overhang, true, layer_idx - dtt_before), connect_length, 1), layer_idx - dtt_before); + overhang_lines = splitLines(overhang_lines, + [this, layer_idx, dtt_before](std::pair &p){ return evaluatePointForNextLayerFunction(m_volumes, m_config, layer_idx - dtt_before, p); }).first; + } + break; + } + added_roofs[dtt_roof] = overhang_outset; + last_overhang = overhang_outset; + overhang_outset = overhang_outset_next; + } + } + + size_t layer_generation_dtt = std::max(dtt_roof, size_t(1)) - 1; // 1 inside max and -1 outside to avoid underflow. layer_generation_dtt=dtt_roof-1 if dtt_roof!=0; + // if the roof should be valid, check that the area does generate lines. This is NOT guaranteed. + if (overhang_lines.empty() && dtt_roof != 0 && generateLines(overhang_outset, true, layer_idx - layer_generation_dtt).empty()) + for (size_t idx = 0; idx < dtt_roof; idx++) { + // check for every roof area that it has resulting lines. Remember idx 1 means the 2. layer of roof => higher idx == lower layer + if (generateLines(added_roofs[idx], true, layer_idx - idx).empty()) { + dtt_roof = idx; + layer_generation_dtt = std::max(dtt_roof, size_t(1)) - 1; + break; + } + } + + { + std::lock_guard lock(mutex_layer_storage); + for (size_t idx = 0; idx < dtt_roof; ++ idx) + if (! added_roofs[idx].empty()) { + SupportGeneratorLayer *&l = top_contacts[layer_idx - idx]; + if (l == nullptr) + l = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), layer_idx - idx); + // will be unioned in finalizeInterfaceAndSupportAreas + append(l->polygons, std::move(added_roofs[idx])); + } + } + + if (overhang_lines.empty()) { + // support_line_width to form a line here as otherwise most will be unsupported. Technically this violates branch distance, but not only is this the only reasonable choice, + // but it ensures consistant behaviour as some infill patterns generate each line segment as its own polyline part causing a similar line forming behaviour. + // This is not doen when a roof is above as the roof will support the model and the trees only need to support the roof + Polylines polylines = ensureMaximumDistancePolyline(generateLines(overhang_outset, dtt_roof != 0, layer_idx - layer_generation_dtt), dtt_roof == 0 ? mesh_config.min_radius / 2 : connect_length, 1); + size_t point_count = 0; + for (const Polyline &poly : polylines) + point_count += poly.size(); + if (point_count <= min_support_points) { + // add the outer wall (of the overhang) to ensure it is correct supported instead. Try placing the support points in a way that they fully support the outer wall, instead of just the with half of the the support line width. + // I assume that even small overhangs are over one line width wide, so lets try to place the support points in a way that the full support area generated from them + // will support the overhang (if this is not done it may only be half). This WILL NOT be the case when supporting an angle of about < 60� so there is a fallback, + // as some support is better than none. + Polygons reduced_overhang_outset = offset(union_ex(overhang_outset), -mesh_config.support_line_width / 2.2, jtMiter, 1.2); + polylines = ensureMaximumDistancePolyline( + to_polylines(!reduced_overhang_outset.empty() && area(offset(diff_ex(overhang_outset, reduced_overhang_outset), std::max(mesh_config.support_line_width, connect_length), jtMiter, 1.2)) < sqr(scaled(0.001)) ? + reduced_overhang_outset : + overhang_outset), + connect_length, min_support_points); + } + LayerIndex last_insert_layer = layer_idx - dtt_roof; + overhang_lines = convertLinesToInternal(m_volumes, m_config, polylines, last_insert_layer); + } + + if (int(dtt_roof) >= layer_idx && roof_allowed_for_this_part && ! overhang_outset.empty()) { + // reached buildplate + std::lock_guard lock(mutex_layer_storage); + SupportGeneratorLayer*& l = top_contacts[0]; + if (l == nullptr) + l = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), 0); + append(l->polygons, std::move(overhang_outset)); + } else // normal trees have to be generated + addLinesAsInfluenceAreas(overhang_lines, force_tip_to_roof ? support_roof_layers - dtt_roof : 0, layer_idx - dtt_roof, dtt_roof > 0, roof_enabled ? support_roof_layers - dtt_roof : 0); + } + } + }); +} + +static unsigned int moveInside(const Polygons &polygons, Point &from, int distance = 0, int64_t maxDist2 = std::numeric_limits::max()) +{ + Point ret = from; + double bestDist2 = std::numeric_limits::max(); + auto bestPoly = static_cast(-1); + bool is_already_on_correct_side_of_boundary = false; // whether [from] is already on the right side of the boundary + for (unsigned int poly_idx = 0; poly_idx < polygons.size(); ++ poly_idx) { + const Polygon &poly = polygons[poly_idx]; + if (poly.size() < 2) + continue; + Point p0 = poly[poly.size() - 2]; + Point p1 = poly.back(); + // because we compare with vSize2 here (no division by zero), we also need to compare by vSize2 inside the loop + // to avoid integer rounding edge cases + bool projected_p_beyond_prev_segment = (p1 - p0).cast().dot((from - p0).cast()) >= (p1 - p0).cast().squaredNorm(); + for (const Point& p2 : poly) { + // X = A + Normal(B-A) * (((B-A) dot (P-A)) / VSize(B-A)); + // = A + (B-A) * ((B-A) dot (P-A)) / VSize2(B-A); + // X = P projected on AB + const Point& a = p1; + const Point& b = p2; + const Point& p = from; + auto ab = (b - a).cast(); + auto ap = (p - a).cast(); + int64_t ab_length2 = ab.squaredNorm(); + if (ab_length2 <= 0) { //A = B, i.e. the input polygon had two adjacent points on top of each other. + p1 = p2; //Skip only one of the points. + continue; + } + int64_t dot_prod = ab.dot(ap); + if (dot_prod <= 0) { // x is projected to before ab + if (projected_p_beyond_prev_segment) { + // case which looks like: > . + projected_p_beyond_prev_segment = false; + Point& x = p1; + + auto dist2 = (x - p).cast().squaredNorm(); + if (dist2 < bestDist2) { + bestDist2 = dist2; + bestPoly = poly_idx; + if (distance == 0) + ret = x; + else { + Vec2d abd = ab.cast(); + Vec2d p1p2 = (p1 - p0).cast(); + double lab = abd.norm(); + double lp1p2 = p1p2.norm(); + // inward direction irrespective of sign of [distance] + auto inward_dir = perp(abd * (scaled(10.0) / lab) + p1p2 * (scaled(10.0) / lp1p2)); + // MM2INT(10.0) to retain precision for the eventual normalization + ret = x + (inward_dir * (distance / inward_dir.norm())).cast(); + is_already_on_correct_side_of_boundary = inward_dir.dot((p - x).cast()) * distance >= 0; + } + } + } else { + projected_p_beyond_prev_segment = false; + p0 = p1; + p1 = p2; + continue; + } + } else if (dot_prod >= ab_length2) { + // x is projected to beyond ab + projected_p_beyond_prev_segment = true; + p0 = p1; + p1 = p2; + continue; + } else { + // x is projected to a point properly on the line segment (not onto a vertex). The case which looks like | . + projected_p_beyond_prev_segment = false; + Point x = a + (ab.cast() * (double(dot_prod) / double(ab_length2))).cast(); + auto dist2 = (p - x).cast().squaredNorm(); + if (dist2 < bestDist2) { + bestDist2 = dist2; + bestPoly = poly_idx; + if (distance == 0) + ret = x; + else { + Vec2d abd = ab.cast(); + Vec2d inward_dir = perp(abd * (distance / abd.norm())); // inward or outward depending on the sign of [distance] + ret = x + inward_dir.cast(); + is_already_on_correct_side_of_boundary = inward_dir.dot((p - x).cast()) >= 0; + } + } + } + p0 = p1; + p1 = p2; + } + } + // when the best point is already inside and we're moving inside, or when the best point is already outside and we're moving outside + if (is_already_on_correct_side_of_boundary) { + if (bestDist2 < distance * distance) + from = ret; + else { + // from = from; // original point stays unaltered. It is already inside by enough distance + } + return bestPoly; + } else if (bestDist2 < maxDist2) { + from = ret; + return bestPoly; + } + return -1; +} + +/*! + * \brief Merges Influence Areas if possible. + * + * Branches which do overlap have to be merged. This helper merges all elements in input with the elements into reduced_new_layer. + * Elements in input_aabb are merged together if possible, while elements reduced_new_layer_aabb are not checked against each other. + * + * \param reduced_aabb[in,out] The already processed elements. + * \param input_aabb[in] Not yet processed elements + * \param to_bp_areas[in] The Elements of the current Layer that will reach the buildplate. Value is the influence area where the center of a circle of support may be placed. + * \param to_model_areas[in] The Elements of the current Layer that do not have to reach the buildplate. Also contains main as every element that can reach the buildplate is not forced to. + * Value is the influence area where the center of a circle of support may be placed. + * \param influence_areas[in] The influence areas without avoidance removed. + * \param insert_bp_areas[out] Elements to be inserted into the main dictionary after the Helper terminates. + * \param insert_model_areas[out] Elements to be inserted into the secondary dictionary after the Helper terminates. + * \param insert_influence[out] Elements to be inserted into the dictionary containing the largest possibly valid influence area (ignoring if the area may not be there because of avoidance) + * \param erase[out] Elements that should be deleted from the above dictionaries. + * \param layer_idx[in] The Index of the current Layer. + */ +static void mergeHelper( + const TreeModelVolumes &volumes, const TreeSupport::TreeSupportSettings &config, + std::map& reduced_aabb, std::map& input_aabb, + const std::unordered_map& to_bp_areas, const std::unordered_map& to_model_areas, + const std::map& influence_areas, + std::unordered_map& insert_bp_areas, std::unordered_map& insert_model_areas, + std::unordered_map& insert_influence, std::vector& erase, const LayerIndex layer_idx) +{ + using SupportElement = TreeSupport::SupportElement; + + const bool first_merge_iteration = reduced_aabb.empty(); // If this is the first iteration, all elements in input have to be merged with each other + for (std::map::iterator influence_iter = input_aabb.begin(); influence_iter != input_aabb.end(); influence_iter++) + { + bool merged = false; + BoundingBox influence_aabb = influence_iter->second; + for (std::map::iterator reduced_check_iter = reduced_aabb.begin(); reduced_check_iter != reduced_aabb.end(); reduced_check_iter++) + { + // As every area has to be checked for overlaps with other areas, some fast heuristic is needed to abort early if clearly possible + // This is so performance critical that using a map lookup instead of the direct access of the cached AABBs can have a surprisingly large performance impact + BoundingBox aabb = reduced_check_iter->second; + if (aabb.overlap(influence_aabb)) { + if (!first_merge_iteration && input_aabb.count(reduced_check_iter->first)) + break; // Do not try to merge elements that already should have been merged. Done for potential performance improvement. + + bool merging_gracious_and_non_gracious = reduced_check_iter->first.to_model_gracious != influence_iter->first.to_model_gracious; // we do not want to merge a gracious with a non gracious area as bad placement could negatively impact the dependability of the whole subtree + bool merging_to_bp = reduced_check_iter->first.to_buildplate && influence_iter->first.to_buildplate; + bool merging_min_and_regular_xy = reduced_check_iter->first.use_min_xy_dist != influence_iter->first.use_min_xy_dist; // could cause some issues with the increase of one area, as it is assumed that if the smaller is increased by the delta to the larger it is engulfed by it already. But because a different collision may be removed from the in drawArea generated circles, this assumption could be wrong. + coord_t increased_to_model_radius = 0; + size_t larger_to_model_dtt = 0; + + if (!merging_to_bp) { + coord_t infl_radius = config.getRadius(influence_iter->first); // get the real radius increase as the user does not care for the collision model. + coord_t redu_radius = config.getRadius(reduced_check_iter->first); + if (reduced_check_iter->first.to_buildplate != influence_iter->first.to_buildplate) { + if (reduced_check_iter->first.to_buildplate) { + if (infl_radius < redu_radius) + increased_to_model_radius = influence_iter->first.increased_to_model_radius + redu_radius - infl_radius; + } else { + if (infl_radius > redu_radius) + increased_to_model_radius = reduced_check_iter->first.increased_to_model_radius + infl_radius - redu_radius; + } + } + larger_to_model_dtt = std::max(influence_iter->first.distance_to_top, reduced_check_iter->first.distance_to_top); + } + + // if a merge could place a stable branch on unstable ground, would be increasing the radius further than allowed to when merging to model and to_bp trees or would merge to model before it is known they will even been drawn the merge is skipped + if (merging_min_and_regular_xy || merging_gracious_and_non_gracious || increased_to_model_radius > config.max_to_model_radius_increase || (!merging_to_bp && larger_to_model_dtt < config.min_dtt_to_model && !reduced_check_iter->first.supports_roof && !influence_iter->first.supports_roof)) + continue; + + Polygons relevant_infl; + Polygons relevant_redu; + if (merging_to_bp) { + relevant_infl = to_bp_areas.count(influence_iter->first) ? to_bp_areas.at(influence_iter->first) : Polygons(); // influence_iter->first is a new element => not required to check if it was changed + relevant_redu = insert_bp_areas.count(reduced_check_iter->first) ? insert_bp_areas[reduced_check_iter->first] : (to_bp_areas.count(reduced_check_iter->first) ? to_bp_areas.at(reduced_check_iter->first) : Polygons()); + } else { + relevant_infl = to_model_areas.count(influence_iter->first) ? to_model_areas.at(influence_iter->first) : Polygons(); + relevant_redu = insert_model_areas.count(reduced_check_iter->first) ? insert_model_areas[reduced_check_iter->first] : (to_model_areas.count(reduced_check_iter->first) ? to_model_areas.at(reduced_check_iter->first) : Polygons()); + } + + const bool red_bigger = config.getCollisionRadius(reduced_check_iter->first) > config.getCollisionRadius(influence_iter->first); + std::pair smaller_rad = red_bigger ? std::pair(influence_iter->first, relevant_infl) : std::pair(reduced_check_iter->first, relevant_redu); + std::pair bigger_rad = red_bigger ? std::pair(reduced_check_iter->first, relevant_redu) : std::pair(influence_iter->first, relevant_infl); + const coord_t real_radius_delta = std::abs(config.getRadius(bigger_rad.first) - config.getRadius(smaller_rad.first)); + const coord_t smaller_collision_radius = config.getCollisionRadius(smaller_rad.first); + + // the area of the bigger radius is used to ensure correct placement regarding the relevant avoidance, so if that would change an invalid area may be created + if (!bigger_rad.first.can_use_safe_radius && smaller_rad.first.can_use_safe_radius) + continue; + + // the bigger radius is used to verify that the area is still valid after the increase with the delta. If there were a point where the big influence area could be valid with can_use_safe_radius the element would already be can_use_safe_radius + // the smaller radius, which gets increased by delta may reach into the area where use_min_xy_dist is no longer required. + bool use_min_radius = bigger_rad.first.use_min_xy_dist && smaller_rad.first.use_min_xy_dist; + + // The idea is that the influence area with the smaller collision radius is increased by the radius difference. + // If this area has any intersections with the influence area of the larger collision radius, a branch (of the larger collision radius) placed in this intersection, has already engulfed the branch of the smaller collision radius. + // Because of this a merge may happen even if the influence areas (that represent possible center points of branches) do not intersect yet. + // Remember that collision radius <= real radius as otherwise this assumption would be false. + Polygons small_rad_increased_by_big_minus_small = safeOffsetInc(smaller_rad.second, real_radius_delta, volumes.getCollision(smaller_collision_radius, layer_idx - 1, use_min_radius), 2 * (config.xy_distance + smaller_collision_radius - 3), 0, 0); // -3 avoids possible rounding errors + Polygons intersect = intersection(small_rad_increased_by_big_minus_small, bigger_rad.second); + + if (area(intersect) > tiny_area_threshold) { // dont use empty as a line is not empty, but for this use-case it very well may be (and would be one layer down as union does not keep lines) + if (area(offset(intersect, scaled(-0.025), jtMiter, 1.2)) <= tiny_area_threshold) // check if the overlap is large enough (Small ares tend to attract rounding errors in clipper). While 25 was guessed as enough, i did not have reason to change it. + continue; + + // Do the actual merge now that the branches are confirmed to be able to intersect. + + // calculate which point is closest to the point of the last merge (or tip center if no merge above it has happened) + // used at the end to estimate where to best place the branch on the bottom most layer + // could be replaced with a random point inside the new area + Point new_pos = reduced_check_iter->first.next_position; + if (! contains(intersect, new_pos)) + moveInside(intersect, new_pos); + + if (increased_to_model_radius == 0) + increased_to_model_radius = std::max(reduced_check_iter->first.increased_to_model_radius, influence_iter->first.increased_to_model_radius); + + SupportElement key(reduced_check_iter->first, influence_iter->first, layer_idx - 1, new_pos, increased_to_model_radius, config); + + Polygons intersect_influence; + Polygons infl_small = insert_influence.count(smaller_rad.first) ? insert_influence[smaller_rad.first] : (influence_areas.count(smaller_rad.first) ? influence_areas.at(smaller_rad.first) : Polygons()); + Polygons infl_big = insert_influence.count(bigger_rad.first) ? insert_influence[bigger_rad.first] : (influence_areas.count(bigger_rad.first) ? influence_areas.at(bigger_rad.first) : Polygons()); + Polygons small_rad_increased_by_big_minus_small_infl = safeOffsetInc(infl_small, real_radius_delta, volumes.getCollision(smaller_collision_radius, layer_idx - 1, use_min_radius), 2 * (config.xy_distance + smaller_collision_radius - 3), 0, 0); + intersect_influence = intersection(small_rad_increased_by_big_minus_small_infl, infl_big); // if the one with the bigger radius with the lower radius removed overlaps we can merge + intersect_influence = safeUnion(intersect_influence, intersect); // Rounding errors again. Do not ask me where or why. + + Polygons intersect_sec; + if (merging_to_bp && config.support_rests_on_model) { + if (key.to_model_gracious) { + Polygons sec_small = insert_model_areas.count(smaller_rad.first) ? insert_model_areas[smaller_rad.first] : (to_model_areas.count(smaller_rad.first) ? to_model_areas.at(smaller_rad.first) : Polygons()); + Polygons sec_big = insert_model_areas.count(bigger_rad.first) ? insert_model_areas[bigger_rad.first] : (to_model_areas.count(bigger_rad.first) ? to_model_areas.at(bigger_rad.first) : Polygons()); + Polygons small_rad_increased_by_big_minus_small_sec = safeOffsetInc(sec_small, real_radius_delta, volumes.getCollision(smaller_collision_radius, layer_idx - 1, use_min_radius), 2 * (config.xy_distance + smaller_collision_radius - 3), 0, 0); + intersect_sec = intersection(small_rad_increased_by_big_minus_small_sec, sec_big); // if the one with the bigger radius with the lower radius removed overlaps we can merge + intersect_influence = safeUnion(intersect_influence, intersect_sec); // still rounding errors + } else + intersect_sec = intersect_influence; + } + + // remove the now merged elements from all buckets, as they do not exist anymore in their old form + insert_bp_areas.erase(reduced_check_iter->first); + insert_bp_areas.erase(influence_iter->first); + insert_model_areas.erase(reduced_check_iter->first); + insert_model_areas.erase(influence_iter->first); + insert_influence.erase(reduced_check_iter->first); + insert_influence.erase(influence_iter->first); + + (merging_to_bp ? insert_bp_areas : insert_model_areas).emplace(key, intersect); + if (merging_to_bp && config.support_rests_on_model) + insert_model_areas.emplace(key, intersect_sec); + insert_influence.emplace(key, intersect_influence); + + erase.emplace_back(reduced_check_iter->first); + erase.emplace_back(influence_iter->first); + Polygons merge = diff_clipped(offset(union_(intersect, intersect_sec), config.getRadius(key), ClipperLib::jtRound, scaled(0.01)), volumes.getCollision(0, layer_idx - 1, false)); // regular union should be preferable here as Polygons tend to only become smaller through rounding errors (smaller!=has smaller area as holes have a negative area.). And if this area disappears because of rounding errors, the only downside is that it can not merge again on this layer. + + reduced_aabb.erase(reduced_check_iter->first); // this invalidates reduced_check_iter + reduced_aabb.emplace(key, get_extents(merge)); + + merged = true; + break; + } + } + } + + if (!merged) + reduced_aabb[influence_iter->first] = influence_aabb; + } +} + +/*! + * \brief Merges Influence Areas if possible. + * + * Branches which do overlap have to be merged. This manages the helper and uses a divide and conquer approach to parallelize this problem. This parallelization can at most accelerate the merging by a factor of 2. + * + * \param to_bp_areas[in] The Elements of the current Layer that will reach the buildplate. + * Value is the influence area where the center of a circle of support may be placed. + * \param to_model_areas[in] The Elements of the current Layer that do not have to reach the buildplate. Also contains main as every element that can reach the buildplate is not forced to. + * Value is the influence area where the center of a circle of support may be placed. + * \param influence_areas[in] The Elements of the current Layer without avoidances removed. This is the largest possible influence area for this layer. + * Value is the influence area where the center of a circle of support may be placed. + * \param layer_idx[in] The current layer. + */ +static void mergeInfluenceAreas( + const TreeModelVolumes &volumes, const TreeSupport::TreeSupportSettings &config, + std::unordered_map& to_bp_areas, std::unordered_map& to_model_areas, std::map& influence_areas, LayerIndex layer_idx) +{ + using SupportElement = TreeSupport::SupportElement; + /* + * Idea behind this is that the calculation of merges can be accelerated a bit using divide and conquer: + * If two groups of areas are already merged, only all elements in group 2 have to be merged into group one. + * This can only accelerate by factor 2 (as half the work is merging the last two groups). + * The actual merge logic is found in mergeHelper. This function only manages parallelization of different mergeHelper calls. + */ + + const size_t input_size = influence_areas.size(); + if (input_size == 0) + return; + + size_t num_threads = std::max(size_t(1), size_t(std::thread::hardware_concurrency())); // For some reason hardware concurrency can return 0; + constexpr int min_elements_per_bucket = 2; + + // max_bucket_count is input_size/min_elements_per_bucket round down to the next 2^n. + // The rounding to 2^n is to ensure improved performance, as every iteration two buckets will be merged, halving the amount of buckets. + // If halving would cause an uneven count, e.g. 3 Then bucket 0 and 1 would have to be merged, and in the next iteration the last remaining buckets. This is assumed to not be optimal performance-wise. + const size_t max_bucket_count = std::pow(2, std::floor(std::log(round_up_divide(input_size, min_elements_per_bucket)))); + int bucket_count = std::min(max_bucket_count, num_threads); // do not use more buckets than available threads. + + // To achieve that every element in a bucket is already correctly merged with other elements in this bucket + // an extra empty bucket is created for each bucket, and the elements are merged into the empty one. + // Each thread will then process two buckets by merging all elements in the second bucket into the first one as mergeHelper will disable not trying to merge elements from the same bucket in this case. + std::vector> buckets_area(2 * bucket_count); + std::vector> buckets_aabb(2 * bucket_count); + + size_t position = 0, counter = 0; + const size_t over_elements = input_size % bucket_count; + const size_t elements_per_step = input_size / bucket_count; + + // split the data in x parts to be able to divide and conquer + // the first "over_elements" of buckets gets elements_per_step+1 elements + for (std::map::iterator iter = influence_areas.begin(); iter != influence_areas.end(); ++ iter) { + buckets_area[position * 2 + 1].emplace(iter->first, iter->second); // only use every second bucket beginning with 1 as this makes the parallel call later easier as we assume everything in a bucket i%2==0 is already processed + ++ counter; + if ((counter == elements_per_step && position >= over_elements) || counter > elements_per_step) { + position++; + counter = 0; + } + } + + // precalculate the AABBs from the influence areas. + tbb::parallel_for(tbb::blocked_range(0, bucket_count), + [&](const tbb::blocked_range &range) { + for (size_t idx = range.begin(); idx < range.end(); ++ idx) { + // +=2 as in the beginning only uneven buckets will be filled + size_t bucket_idx = 2 * idx + 1; + for (const std::pair& input_pair : buckets_area[bucket_idx]) + buckets_aabb[bucket_idx].emplace(input_pair.first, get_extents(input_pair.second).inflated(config.getRadius(input_pair.first))); + } + }); + + while (buckets_area.size() > 1) { + // Some temporary storage, of elements that have to be inserted or removed from the background storage. Only one per two buckets required + std::vector> insert_main(buckets_area.size() / 2); + std::vector> insert_secondary(buckets_area.size() / 2); + std::vector> insert_influence(buckets_area.size() / 2); + std::vector> erase(buckets_area.size() / 2); + + tbb::parallel_for(tbb::blocked_range(0, buckets_area.size() / 2), + [&](const tbb::blocked_range &range) { + for (size_t idx = range.begin(); idx < range.end(); ++ idx) { + const size_t bucket_pair_idx = idx * 2; + // Merge bucket_count adjacent to each other, merging uneven bucket numbers into even buckets + mergeHelper(volumes, config, buckets_aabb[bucket_pair_idx], buckets_aabb[bucket_pair_idx + 1], to_bp_areas, to_model_areas, influence_areas, insert_main[bucket_pair_idx / 2], insert_secondary[bucket_pair_idx / 2], insert_influence[bucket_pair_idx / 2], erase[bucket_pair_idx / 2], layer_idx); + // clear now irrelevant max_bucket_count, and delete them later + buckets_area[bucket_pair_idx + 1].clear(); + buckets_aabb[bucket_pair_idx + 1].clear(); + } + }); + + for (size_t i = 0; i + 1 < buckets_area.size(); i += 2) { + for (SupportElement &del : erase[i / 2]) { + to_bp_areas.erase(del); + to_model_areas.erase(del); + influence_areas.erase(del); + } + for (const std::pair &tup : insert_main[i / 2]) + to_bp_areas.emplace(std::move(tup)); + for (const std::pair &tup : insert_secondary[i / 2]) + to_model_areas.emplace(std::move(tup)); + for (const std::pair &tup : insert_influence[i / 2]) + influence_areas.emplace(std::move(tup)); + } + + buckets_area.erase(std::remove_if(buckets_area.begin(), buckets_area.end(), [&](const std::map &x) { return x.empty(); }), buckets_area.end()); + buckets_aabb.erase(std::remove_if(buckets_aabb.begin(), buckets_aabb.end(), [&](const std::map &x) { return x.empty(); }), buckets_aabb.end()); + } +} + + +std::optional TreeSupport::increaseSingleArea(AreaIncreaseSettings settings, LayerIndex layer_idx, SupportElement* parent, const Polygons& relevant_offset, Polygons& to_bp_data, Polygons& to_model_data, Polygons& increased, const coord_t overspeed, const bool mergelayer) +{ + SupportElement current_elem(parent); // also increases DTT by one + Polygons check_layer_data; + if (settings.increase_radius) + current_elem.effective_radius_height += 1; + coord_t radius = m_config.getCollisionRadius(current_elem); + + if (settings.move) { + increased = relevant_offset; + if (overspeed > 0) { + const coord_t safe_movement_distance = (current_elem.use_min_xy_dist ? m_config.xy_min_distance : m_config.xy_distance) + (std::min(m_config.z_distance_top_layers, m_config.z_distance_bottom_layers) > 0 ? m_config.min_feature_size : 0); + // The difference to ensure that the result not only conforms to wall_restriction, but collision/avoidance is done later. The higher last_safe_step_movement_distance comes exactly from the fact that the collision will be subtracted later. + increased = safeOffsetInc(increased, overspeed, m_volumes.getWallRestriction(m_config.getCollisionRadius(*parent), layer_idx, parent->use_min_xy_dist), safe_movement_distance, safe_movement_distance + radius, 1); + } + if (settings.no_error && settings.move) + // as ClipperLib::jtRound has to be used for offsets this simplify is VERY important for performance. + polygons_simplify(increased, scaled(0.025)); + } else + // if no movement is done the areas keep parent area as no move == offset(0) + increased = *parent->area; + + if (mergelayer || current_elem.to_buildplate) { + to_bp_data = safeUnion(diff_clipped(increased, m_volumes.getAvoidance(radius, layer_idx - 1, settings.type, false, settings.use_min_distance))); + if (! current_elem.to_buildplate && area(to_bp_data) > tiny_area_threshold) { + // mostly happening in the tip, but with merges one should check every time, just to be sure. + current_elem.to_buildplate = true; // sometimes nodes that can reach the buildplate are marked as cant reach, tainting subtrees. This corrects it. + BOOST_LOG_TRIVIAL(debug) << "Corrected taint leading to a wrong to model value on layer " << layer_idx - 1 << " targeting " << current_elem.target_height << " with radius " << radius; + } + } + if (m_config.support_rests_on_model) { + if (mergelayer || current_elem.to_model_gracious) + to_model_data = safeUnion(diff_clipped(increased, m_volumes.getAvoidance(radius, layer_idx - 1, settings.type, true, settings.use_min_distance))); + + if (!current_elem.to_model_gracious) { + if (mergelayer && area(to_model_data) >= tiny_area_threshold) { + current_elem.to_model_gracious = true; + BOOST_LOG_TRIVIAL(debug) << "Corrected taint leading to a wrong non gracious value on layer " << layer_idx - 1 << " targeting " << current_elem.target_height << " with radius " << radius; + } else + to_model_data = safeUnion(diff_clipped(increased, m_volumes.getCollision(radius, layer_idx - 1, settings.use_min_distance))); + } + } + + check_layer_data = current_elem.to_buildplate ? to_bp_data : to_model_data; + + if (settings.increase_radius && area(check_layer_data) > tiny_area_threshold) { + auto validWithRadius = [&](coord_t next_radius) { + if (m_volumes.ceilRadius(next_radius, settings.use_min_distance) <= m_volumes.ceilRadius(radius, settings.use_min_distance)) + return true; + + Polygons to_bp_data_2; + if (current_elem.to_buildplate) + to_bp_data_2 = diff_clipped(increased, m_volumes.getAvoidance(next_radius, layer_idx - 1, settings.type, false, settings.use_min_distance)); // regular union as output will not be used later => this area should always be a subset of the safeUnion one (i think) + Polygons to_model_data_2; + if (m_config.support_rests_on_model && !current_elem.to_buildplate) + to_model_data_2 = diff_clipped(increased, + current_elem.to_model_gracious ? + m_volumes.getAvoidance(next_radius, layer_idx - 1, settings.type, true, settings.use_min_distance) : + m_volumes.getCollision(next_radius, layer_idx - 1, settings.use_min_distance)); + Polygons check_layer_data_2 = current_elem.to_buildplate ? to_bp_data_2 : to_model_data_2; + return area(check_layer_data_2) > tiny_area_threshold; + }; + coord_t ceil_radius_before = m_volumes.ceilRadius(radius, settings.use_min_distance); + + if (m_config.getCollisionRadius(current_elem) < m_config.increase_radius_until_radius && m_config.getCollisionRadius(current_elem) < m_config.getRadius(current_elem)) { + coord_t target_radius = std::min(m_config.getRadius(current_elem), m_config.increase_radius_until_radius); + coord_t current_ceil_radius = m_volumes.getRadiusNextCeil(radius, settings.use_min_distance); + + while (current_ceil_radius < target_radius && validWithRadius(m_volumes.getRadiusNextCeil(current_ceil_radius + 1, settings.use_min_distance))) + current_ceil_radius = m_volumes.getRadiusNextCeil(current_ceil_radius + 1, settings.use_min_distance); + size_t resulting_eff_dtt = current_elem.effective_radius_height; + while (resulting_eff_dtt + 1 < current_elem.distance_to_top && m_config.getRadius(resulting_eff_dtt + 1, current_elem.elephant_foot_increases) <= current_ceil_radius && m_config.getRadius(resulting_eff_dtt + 1, current_elem.elephant_foot_increases) <= m_config.getRadius(current_elem)) + resulting_eff_dtt++; + current_elem.effective_radius_height = resulting_eff_dtt; + } + radius = m_config.getCollisionRadius(current_elem); + + const coord_t foot_radius_increase = m_config.branch_radius * (std::max(m_config.diameter_scale_bp_radius - m_config.diameter_angle_scale_factor, 0.0)); + // Is nearly all of the time 1, but sometimes an increase of 1 could cause the radius to become bigger than recommendedMinRadius, which could cause the radius to become bigger than precalculated. + double planned_foot_increase = std::min(1.0, double(m_config.recommendedMinRadius(layer_idx - 1) - m_config.getRadius(current_elem)) / foot_radius_increase); +//FIXME + bool increase_bp_foot = planned_foot_increase > 0 && current_elem.to_buildplate; +// bool increase_bp_foot = false; + + if (increase_bp_foot && m_config.getRadius(current_elem) >= m_config.branch_radius && m_config.getRadius(current_elem) >= m_config.increase_radius_until_radius) + if (validWithRadius(m_config.getRadius(current_elem.effective_radius_height, current_elem.elephant_foot_increases + planned_foot_increase))) { + current_elem.elephant_foot_increases += planned_foot_increase; + radius = m_config.getCollisionRadius(current_elem); + } + + if (ceil_radius_before != m_volumes.ceilRadius(radius, settings.use_min_distance)) { + if (current_elem.to_buildplate) + to_bp_data = safeUnion(diff_clipped(increased, m_volumes.getAvoidance(radius, layer_idx - 1, settings.type, false, settings.use_min_distance))); + if (m_config.support_rests_on_model && (!current_elem.to_buildplate || mergelayer)) + to_model_data = safeUnion(diff_clipped(increased, + current_elem.to_model_gracious ? + m_volumes.getAvoidance(radius, layer_idx - 1, settings.type, true, settings.use_min_distance) : + m_volumes.getCollision(radius, layer_idx - 1, settings.use_min_distance) + )); + check_layer_data = current_elem.to_buildplate ? to_bp_data : to_model_data; + if (area(check_layer_data) < tiny_area_threshold) { + BOOST_LOG_TRIVIAL(error) << "Lost area by doing catch up from " << ceil_radius_before << " to radius " << m_volumes.ceilRadius(m_config.getCollisionRadius(current_elem), settings.use_min_distance); + TreeSupport::showError("Area lost catching up radius. May not cause visible malformation.", true); + } + } + } + + return area(check_layer_data) > tiny_area_threshold ? std::optional(current_elem) : std::optional(); +} + +void TreeSupport::increaseAreas(std::unordered_map& to_bp_areas, std::unordered_map& to_model_areas, std::map& influence_areas, std::vector& bypass_merge_areas, const std::vector& last_layer, const LayerIndex layer_idx, const bool mergelayer) +{ + std::mutex critical_sections; + tbb::parallel_for(tbb::blocked_range(0, last_layer.size()), + [&](const tbb::blocked_range &range) { + for (size_t idx = range.begin(); idx < range.end(); ++ idx) { + SupportElement* parent = last_layer[idx]; + + SupportElement elem(parent); // also increases dtt + + const Polygons &wall_restriction = m_volumes.getWallRestriction(m_config.getCollisionRadius(*parent), layer_idx, parent->use_min_xy_dist); // Abstract representation of the model outline. If an influence area would move through it, it could teleport through a wall. + + Polygons to_bp_data, to_model_data; + coord_t radius = m_config.getCollisionRadius(elem); + + // When the radius increases, the outer "support wall" of the branch will have been moved farther away from the center (as this is the definition of radius). + // As it is not specified that the support_tree_angle has to be one of the center of the branch, it is here seen as the smaller angle of the outer wall of the branch, to the outer wall of the same branch one layer above. + // As the branch may have become larger the distance between these 2 walls is smaller than the distance of the center points. + // These extra distance is added to the movement distance possible for this layer. + + coord_t extra_speed = 5; // The extra speed is added to both movement distances. Also move 5 microns faster than allowed to avoid rounding errors, this may cause issues at VERY VERY small layer heights. + coord_t extra_slow_speed = 0; // Only added to the slow movement distance. + const coord_t ceiled_parent_radius = m_volumes.ceilRadius(m_config.getCollisionRadius(*parent), parent->use_min_xy_dist); + coord_t projected_radius_increased = m_config.getRadius(parent->effective_radius_height + 1, parent->elephant_foot_increases); + coord_t projected_radius_delta = projected_radius_increased - m_config.getCollisionRadius(*parent); + + // When z distance is more than one layer up and down the Collision used to calculate the wall restriction will always include the wall (and not just the xy_min_distance) of the layer above and below like this (d = blocked area because of z distance): + /* + * layer z+1:dddddiiiiiioooo + * layer z+0:xxxxxdddddddddd + * layer z-1:dddddxxxxxxxxxx + * For more detailed visualisation see calculateWallRestrictions + */ + const coord_t safe_movement_distance = (elem.use_min_xy_dist ? m_config.xy_min_distance : m_config.xy_distance) + (std::min(m_config.z_distance_top_layers, m_config.z_distance_bottom_layers) > 0 ? m_config.min_feature_size : 0); + if (ceiled_parent_radius == m_volumes.ceilRadius(projected_radius_increased, parent->use_min_xy_dist) || projected_radius_increased < m_config.increase_radius_until_radius) + // If it is guaranteed possible to increase the radius, the maximum movement speed can be increased, as it is assumed that the maximum movement speed is the one of the slower moving wall + extra_speed += projected_radius_delta; + else + // if a guaranteed radius increase is not possible, only increase the slow speed + extra_slow_speed += std::min(projected_radius_delta, (m_config.maximum_move_distance + extra_speed) - (m_config.maximum_move_distance_slow + extra_slow_speed)); // Ensure that the slow movement distance can not become larger than the fast one. + + if (m_config.layer_start_bp_radius > layer_idx && m_config.recommendedMinRadius(layer_idx - 1) < m_config.getRadius(elem.effective_radius_height + 1, elem.elephant_foot_increases)) { + // can guarantee elephant foot radius increase + if (ceiled_parent_radius == m_volumes.ceilRadius(m_config.getRadius(parent->effective_radius_height + 1, parent->elephant_foot_increases + 1), parent->use_min_xy_dist)) + extra_speed += m_config.branch_radius * m_config.diameter_scale_bp_radius; + else + extra_slow_speed += std::min(coord_t(m_config.branch_radius * m_config.diameter_scale_bp_radius), m_config.maximum_move_distance - (m_config.maximum_move_distance_slow + extra_slow_speed)); + } + + const coord_t fast_speed = m_config.maximum_move_distance + extra_speed; + const coord_t slow_speed = m_config.maximum_move_distance_slow + extra_speed + extra_slow_speed; + + Polygons offset_slow, offset_fast; + + bool add = false; + bool bypass_merge = false; + constexpr bool increase_radius = true, no_error = true, use_min_radius = true, move = true; // aliases for better readability + + // Determine in which order configurations are checked if they result in a valid influence area. Check will stop if a valid area is found + std::vector order; + auto insertSetting = [&](AreaIncreaseSettings settings, bool back) { + if (std::find(order.begin(), order.end(), settings) == order.end()) { + if (back) + order.emplace_back(settings); + else + order.insert(order.begin(), settings); + } + }; + + const bool parent_moved_slow = elem.last_area_increase.increase_speed < m_config.maximum_move_distance; + const bool avoidance_speed_mismatch = parent_moved_slow && elem.last_area_increase.type != AvoidanceType::Slow; + if (elem.last_area_increase.move && elem.last_area_increase.no_error && elem.can_use_safe_radius && !mergelayer && !avoidance_speed_mismatch && (elem.distance_to_top >= m_config.tip_layers || parent_moved_slow)) + { + // assume that the avoidance type that was best for the parent is best for me. Makes this function about 7% faster. + insertSetting({ elem.last_area_increase.type, elem.last_area_increase.increase_speed < m_config.maximum_move_distance ? slow_speed : fast_speed, increase_radius, elem.last_area_increase.no_error, !use_min_radius, elem.last_area_increase.move }, true); + insertSetting({ elem.last_area_increase.type, elem.last_area_increase.increase_speed < m_config.maximum_move_distance ? slow_speed : fast_speed, !increase_radius, elem.last_area_increase.no_error, !use_min_radius, elem.last_area_increase.move }, true); + } + // branch may still go though a hole, so a check has to be done whether the hole was already passed, and the regular avoidance can be used. + if (!elem.can_use_safe_radius) + { + // if the radius until which it is always increased can not be guaranteed, move fast. This is to avoid holes smaller than the real branch radius. This does not guarantee the avoidance of such holes, but ensures they are avoided if possible. + // order.emplace_back(AvoidanceType::Slow,!increase_radius,no_error,!use_min_radius,move); + insertSetting({ AvoidanceType::Slow, slow_speed, increase_radius, no_error, !use_min_radius, !move }, true); // did we go through the hole + // in many cases the definition of hole is overly restrictive, so to avoid unnecessary fast movement in the tip, it is ignored there for a bit. This CAN cause a branch to go though a hole it otherwise may have avoided. + if (elem.distance_to_top < round_up_divide(m_config.tip_layers, size_t(2))) + insertSetting({ AvoidanceType::Fast, slow_speed, increase_radius, no_error, !use_min_radius, !move }, true); + insertSetting({ AvoidanceType::FastSafe, fast_speed, increase_radius, no_error, !use_min_radius, !move }, true); // did we manage to avoid the hole + insertSetting({ AvoidanceType::FastSafe, fast_speed, !increase_radius, no_error, !use_min_radius, move }, true); + insertSetting({ AvoidanceType::Fast, fast_speed, !increase_radius, no_error, !use_min_radius, move }, true); + } + else + { + insertSetting({ AvoidanceType::Slow, slow_speed, increase_radius, no_error, !use_min_radius, move }, true); + // while moving fast to be able to increase the radius (b) may seems preferable (over a) this can cause the a sudden skip in movement, which looks similar to a layer shift and can reduce stability. + // as such idx have chosen to only use the user setting for radius increases as a friendly recommendation. + insertSetting({ AvoidanceType::Slow, slow_speed, !increase_radius, no_error, !use_min_radius, move }, true); // a + if (elem.distance_to_top < m_config.tip_layers) + { + insertSetting({ AvoidanceType::FastSafe, slow_speed, increase_radius, no_error, !use_min_radius, move }, true); + } + insertSetting({ AvoidanceType::FastSafe, fast_speed, increase_radius, no_error, !use_min_radius, move }, true); // b + insertSetting({ AvoidanceType::FastSafe, fast_speed, !increase_radius, no_error, !use_min_radius, move }, true); + } + + if (elem.use_min_xy_dist) + { + std::vector new_order; + // if the branch currently has to use min_xy_dist check if the configuration would also be valid with the regular xy_distance before checking with use_min_radius (Only happens when Support Distance priority is z overrides xy ) + for (AreaIncreaseSettings settings : order) + { + new_order.emplace_back(settings); + new_order.push_back({ settings.type, settings.increase_speed, settings.increase_radius, settings.no_error, use_min_radius, settings.move }); + } + order = new_order; + } + if (elem.to_buildplate || (elem.to_model_gracious && intersection(*parent->area, m_volumes.getPlaceableAreas(radius, layer_idx)).empty())) // error case + { + // it is normal that we wont be able to find a new area at some point in time if we wont be able to reach layer 0 aka have to connect with the model + insertSetting({ AvoidanceType::Fast, fast_speed, !increase_radius, !no_error, elem.use_min_xy_dist, move }, true); + } + if (elem.distance_to_top < elem.dont_move_until && elem.can_use_safe_radius) // only do not move when holes would be avoided in every case. + // Only do not move when already in a no hole avoidance with the regular xy distance. + insertSetting({ AvoidanceType::Slow, 0, increase_radius, no_error, !use_min_radius, !move }, false); + + Polygons inc_wo_collision; + // Check whether it is faster to calculate the area increased with the fast speed independently from the slow area, or time could be saved by reusing the slow area to calculate the fast one. + // Calculated by comparing the steps saved when calcualting idependently with the saved steps when not. + bool offset_independant_faster = (radius / safe_movement_distance - (((m_config.maximum_move_distance + extra_speed) < (radius + safe_movement_distance)) ? 1 : 0)) > (round_up_divide((extra_speed + extra_slow_speed + m_config.maximum_move_distance_slow), safe_movement_distance)); + for (AreaIncreaseSettings settings : order) + { + if (settings.move) { + if (offset_slow.empty() && (settings.increase_speed == slow_speed || !offset_independant_faster)) { + // offsetting in 2 steps makes our offsetted area rounder preventing (rounding) errors created by to pointy areas. At this point one can see that the Polygons class + // was never made for precision in the single digit micron range. + offset_slow = safeOffsetInc(*parent->area, extra_speed + extra_slow_speed + m_config.maximum_move_distance_slow, wall_restriction, safe_movement_distance, offset_independant_faster ? safe_movement_distance + radius : 0, 2); + } + + if ((settings.increase_speed != slow_speed) && offset_fast.empty()) { + if (offset_independant_faster) + offset_fast = safeOffsetInc(*parent->area, extra_speed + m_config.maximum_move_distance, wall_restriction, safe_movement_distance, offset_independant_faster ? safe_movement_distance + radius : 0, 1); + else { + const coord_t delta_slow_fast = m_config.maximum_move_distance - (m_config.maximum_move_distance_slow + extra_slow_speed); + offset_fast = safeOffsetInc(offset_slow, delta_slow_fast, wall_restriction, safe_movement_distance, safe_movement_distance + radius, offset_independant_faster ? 2 : 1); + } + } + } + std::optional result; + if (!settings.no_error) { + // ERROR CASE + // if the area becomes for whatever reason something that clipper sees as a line, offset would stop working, so ensure that even if if wrongly would be a line, it still actually has an area that can be increased + Polygons lines_offset = offset(to_polylines(*parent->area), scaled(0.005), jtMiter, 1.2); + Polygons base_error_area = union_(*parent->area, lines_offset); + result = increaseSingleArea(settings, layer_idx, parent, base_error_area, to_bp_data, to_model_data, inc_wo_collision, (m_config.maximum_move_distance + extra_speed) * 1.5, mergelayer); +#ifdef TREE_SUPPORT_SHOW_ERRORS + BOOST_LOG_TRIVIAL(error) +#else // TREE_SUPPORT_SHOW_ERRORS + BOOST_LOG_TRIVIAL(warning) +#endif // TREE_SUPPORT_SHOW_ERRORS + << "Influence area could not be increased! Data about the Influence area: " + "Radius: " << radius << " at layer: " << layer_idx - 1 << " NextTarget: " << elem.next_height << " Distance to top: " << elem.distance_to_top << + " Elephant foot increases " << elem.elephant_foot_increases << " use_min_xy_dist " << elem.use_min_xy_dist << " to buildplate " << elem.to_buildplate << + " gracious " << elem.to_model_gracious << " safe " << elem.can_use_safe_radius << " until move " << elem.dont_move_until << " \n " + "Parent " << parent << ": Radius: " << m_config.getCollisionRadius(*parent) << " at layer: " << layer_idx << " NextTarget: " << parent->next_height << + " Distance to top: " << parent->distance_to_top << " Elephant foot increases " << parent->elephant_foot_increases << " use_min_xy_dist " << parent->use_min_xy_dist << + " to buildplate " << parent->to_buildplate << " gracious " << parent->to_model_gracious << " safe " << parent->can_use_safe_radius << " until move " << parent->dont_move_until; + showError("Potentially lost branch!", true); + } else + result = increaseSingleArea(settings, layer_idx, parent, settings.increase_speed == slow_speed ? offset_slow : offset_fast, to_bp_data, to_model_data, inc_wo_collision, 0, mergelayer); + + if (result) + { + elem = *result; + radius = m_config.getCollisionRadius(elem); + elem.last_area_increase = settings; + add = true; + bypass_merge = !settings.move || (settings.use_min_distance && elem.distance_to_top < m_config.tip_layers); // do not merge if the branch should not move or the priority has to be to get farther away from the model. + if (settings.move) + elem.dont_move_until = 0; + else + elem.result_on_layer = parent->result_on_layer; + + elem.can_use_safe_radius = settings.type != AvoidanceType::Fast; + + if (!settings.use_min_distance) + elem.use_min_xy_dist = false; + if (!settings.no_error) +#ifdef TREE_SUPPORT_SHOW_ERRORS + BOOST_LOG_TRIVIAL(error) +#else // TREE_SUPPORT_SHOW_ERRORS + BOOST_LOG_TRIVIAL(info) +#endif // TREE_SUPPORT_SHOW_ERRORS + << "Trying to keep area by moving faster than intended: Success"; + break; + } + else if (!settings.no_error) + BOOST_LOG_TRIVIAL(error) << "Trying to keep area by moving faster than intended: FAILURE! WRONG BRANCHES LIKLY!"; + } + + if (add) { + Polygons max_influence_area = safeUnion(diff_clipped(inc_wo_collision, m_volumes.getCollision(radius, layer_idx - 1, elem.use_min_xy_dist)), safeUnion(to_bp_data, to_model_data)); // union seems useless, but some rounding errors somewhere can cause to_bp_data to be slightly bigger than it should be + { + std::lock_guard critical_section_newLayer(critical_sections); + if (bypass_merge) { + validate_range(max_influence_area); + Polygons* new_area = new Polygons(max_influence_area); + SupportElement* next = new SupportElement(elem, new_area); + bypass_merge_areas.emplace_back(next); + } else { + influence_areas.emplace(elem, max_influence_area); + if (elem.to_buildplate) + to_bp_areas.emplace(elem, to_bp_data); + if (m_config.support_rests_on_model) + to_model_areas.emplace(elem, to_model_data); + } + } + } + else + parent->result_on_layer = Point(-1, -1); // If the bottom most point of a branch is set, later functions will assume that the position is valid, and ignore it. But as branches connecting with the model that are to small have to be culled, the bottom most point has to be not set. A point can be set on the top most tip layer (maybe more if it should not move for a few layers). + } + }); +} + + +void TreeSupport::createLayerPathing(std::vector>& move_bounds) +{ +#ifdef SLIC3R_TREESUPPORTS_PROGRESS + const double data_size_inverse = 1 / double(move_bounds.size()); + double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES; +#endif // SLIC3R_TREESUPPORTS_PROGRESS + + auto dur_inc = std::chrono::duration_values::zero(); + auto dur_merge = std::chrono::duration_values::zero(); + + LayerIndex last_merge = move_bounds.size(); + bool new_element = false; + + size_t max_merge_every_x_layers = std::min(std::min(5000 / (std::max(m_config.maximum_move_distance, coord_t(100))), 1000 / std::max(m_config.maximum_move_distance_slow, coord_t(20))), 3000 / m_config.layer_height); // Ensures at least one merge operation per 3mm height, 50 layers, 1 mm movement of slow speed or 5mm movement of fast speed (whatever is lowest). Values were guessed. + size_t merge_every_x_layers = 1; + // Calculate the influence areas for each layer below (Top down) + // This is done by first increasing the influence area by the allowed movement distance, and merging them with other influence areas if possible + for (int layer_idx = int(move_bounds.size()) - 1; layer_idx > 0; -- layer_idx) + { + // merging is expensive and only parallelized to a max speedup of 2. As such it may be useful in some cases to only merge every few layers to improve performance. + bool merge_this_layer = size_t(last_merge - layer_idx) >= merge_every_x_layers; + + if (new_element) + { + merge_this_layer = true; + merge_every_x_layers = 1; + } + + std::map influence_areas; // Over this map will be iterated when merging, as such it has to be ordered to ensure deterministic results. + std::unordered_map to_bp_areas, to_model_areas; // The area of these SupportElement is not set, to avoid to much allocation and deallocation on the heap + std::vector bypass_merge_areas; // Different to the other maps of SupportElements as these here have the area already set, as they are already to be inserted into move_bounds. + + auto ta = std::chrono::high_resolution_clock::now(); + + std::vector last_layer; + last_layer.insert(last_layer.begin(), move_bounds[layer_idx].begin(), move_bounds[layer_idx].end()); + + // ### Increase the influence areas by the allowed movement distance + increaseAreas(to_bp_areas, to_model_areas, influence_areas, bypass_merge_areas, last_layer, layer_idx, merge_this_layer); + + auto tb = std::chrono::high_resolution_clock::now(); + if (merge_this_layer) + { + bool reduced_by_merging = false; + size_t count_before_merge = influence_areas.size(); + // ### Calculate which influence areas overlap, and merge them into a new influence area (simplified: an intersection of influence areas that have such an intersection) + mergeInfluenceAreas(m_volumes, m_config, to_bp_areas, to_model_areas, influence_areas, layer_idx); + + last_merge = layer_idx; + reduced_by_merging = count_before_merge > influence_areas.size(); + if (!reduced_by_merging && !new_element) + { + merge_every_x_layers = std::min(max_merge_every_x_layers, merge_every_x_layers + 1); + } + } + auto tc = std::chrono::high_resolution_clock::now(); + + dur_inc += tb - ta; + dur_merge += tc - tb; + + new_element = !move_bounds[layer_idx - 1].empty(); + + // Save calculated elements to output, and allocate Polygons on heap, as they will not be changed again. + for (const std::pair &tup : influence_areas) { + const SupportElement &elem = tup.first; + validate_range(tup.second); + validate_range(safeUnion(tup.second)); + Polygons* new_area = new Polygons(safeUnion(tup.second)); + SupportElement* next = new SupportElement(elem, new_area); + move_bounds[layer_idx - 1].emplace(next); + + if (area(*new_area) < tiny_area_threshold) { + BOOST_LOG_TRIVIAL(error) << "Insert Error of Influence area on layer " << layer_idx - 1 << ". Origin of " << elem.parents.size() << " areas. Was to bp " << elem.to_buildplate; + TreeSupport::showError("Insert error of area after merge.\n", true); + } + } + + // Place already fully constructed elements in the output. + for (SupportElement* elem : bypass_merge_areas) { + if (area(*elem->area) < tiny_area_threshold) { + BOOST_LOG_TRIVIAL(error) << "Insert Error of Influence area bypass on layer " << layer_idx - 1; + TreeSupport::showError("Insert error of area after bypassing merge.\n", true); + } + move_bounds[layer_idx - 1].emplace(elem); + } + +#ifdef SLIC3R_TREESUPPORTS_PROGRESS + progress_total += data_size_inverse * TREE_PROGRESS_AREA_CALC; + Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL); +#endif + } + + BOOST_LOG_TRIVIAL(info) << "Time spent with creating influence areas' subtasks: Increasing areas " << dur_inc.count() / 1000000 << " ms merging areas: " << dur_merge.count() / 1000000 << " ms"; +} + + +void TreeSupport::setPointsOnAreas(const SupportElement* elem) +{ + // Based on the branch center point of the current layer, the point on the next (further up) layer is calculated. + + if (elem->result_on_layer == Point(-1, -1)) + { + BOOST_LOG_TRIVIAL(error) << "Uninitialized support element"; + TreeSupport::showError("Uninitialized support element. A branch may be missing.\n", true); + return; + } + + for (SupportElement* next_elem : elem->parents) + { + if (next_elem->result_on_layer != Point(-1, -1)) // if the value was set somewhere else it it kept. This happens when a branch tries not to move after being unable to create a roof. + continue; + + Point from = elem->result_on_layer; + if (! contains(*next_elem->area, from)) { + moveInside(*next_elem->area, from, 0); // Move inside has edgecases (see tests) so DONT use Polygons.inside to confirm correct move, Error with distance 0 is <= 1 + // it is not required to check if how far this move moved a point as is can be larger than maximum_movement_distance. While this seems like a problem it may for example occur after merges. + } + next_elem->result_on_layer = from; + // do not call recursive because then amount of layers would be restricted by the stack size + } +} + +bool TreeSupport::setToModelContact(std::vector>& move_bounds, SupportElement* first_elem, const LayerIndex layer_idx) +{ + if (first_elem->to_model_gracious) + { + SupportElement* check = first_elem; + + std::vector checked; + LayerIndex last_successfull_layer = layer_idx; + bool set = false; + + // check for every layer upwards, up to the point where this influence area was created (either by initial insert or merge) if the branch could be placed on it, and highest up layer index. + + for (LayerIndex layer_check = layer_idx; check->next_height >= layer_check; layer_check++) + { + if (! intersection(*check->area, m_volumes.getPlaceableAreas(m_config.getCollisionRadius(*check), layer_check)).empty()) { + set = true; + last_successfull_layer = layer_check; + } + checked.emplace_back(check); + if (check->parents.size() == 1) + { + check = check->parents[0]; + } + else + { + break; // reached merge point + } + } + + // Could not find valid placement, even though it should exist => error handling + if (!set) + { + if (SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL) + { + BOOST_LOG_TRIVIAL(warning) << "No valid placement found for to model gracious element on layer " << layer_idx << ": REMOVING BRANCH"; + TreeSupport::showError("Could not fine valid placement on model! Removing this branch...", true); + for (LayerIndex layer = layer_idx; layer <= first_elem->next_height; layer++) + { + move_bounds[layer].erase(checked[layer - layer_idx]); + delete checked[layer - layer_idx]->area; + delete checked[layer - layer_idx]; + } + } + else + { + BOOST_LOG_TRIVIAL(warning) << "No valid placement found for to model gracious element on layer " << layer_idx; + TreeSupport::showError("Could not fine valid placement on model! Just placing it down anyway. Could cause floating branches.", true); + first_elem->to_model_gracious = false; + return setToModelContact(move_bounds, first_elem, layer_idx); + } + } + + for (LayerIndex layer = layer_idx + 1; layer < last_successfull_layer - 1; layer++) + { + move_bounds[layer].erase(checked[layer - layer_idx]); + delete checked[layer - layer_idx]->area; + delete checked[layer - layer_idx]; + } + + // Guess a point inside the influence area, in which the branch will be placed in. + Point best = checked[last_successfull_layer - layer_idx]->next_position; + if (! contains(*checked[last_successfull_layer - layer_idx]->area, best)) + moveInside(*checked[last_successfull_layer - layer_idx]->area, best); + checked[last_successfull_layer - layer_idx]->result_on_layer = best; + + BOOST_LOG_TRIVIAL(debug) << "Added gracious Support On Model Point (" << best.x() << "," << best.y() << "). The current layer is " << last_successfull_layer; + + return last_successfull_layer != layer_idx; + } + else // can not add graceful => just place it here and hope for the best + { + Point best = first_elem->next_position; + if (! contains(*first_elem->area, best)) + moveInside(*first_elem->area, best); + first_elem->result_on_layer = best; + first_elem->to_model_gracious = false; + BOOST_LOG_TRIVIAL(debug) << "Added NON gracious Support On Model Point (" << best.x() << "," << best.y() << "). The current layer is " << layer_idx; + return false; + } +} + +void TreeSupport::createNodesFromArea(std::vector>& move_bounds) +{ + // Initialize points on layer 0, with a "random" point in the influence area. Point is chosen based on an inaccurate estimate where the branches will split into two, but every point inside the influence area would produce a valid result. + for (SupportElement* init : move_bounds[0]) { + Point p = init->next_position; + if (! contains(*init->area, p)) + moveInside(*init->area, p, 0); + init->result_on_layer = p; + setPointsOnAreas(init); // also set the parent nodes, as these will be required for the first iteration of the loop below + } + + for (LayerIndex layer_idx = 1; layer_idx < LayerIndex(move_bounds.size()); ++ layer_idx) { + std::unordered_set remove; + for (SupportElement* elem : move_bounds[layer_idx]) { + bool removed = false; + // check if the resulting center point is not yet set + if (elem->result_on_layer == Point(-1, -1)) { + if (elem->to_buildplate || (!elem->to_buildplate && elem->distance_to_top < m_config.min_dtt_to_model && !elem->supports_roof)) { + if (elem->to_buildplate) { + BOOST_LOG_TRIVIAL(error) << "Uninitialized Influence area targeting " << elem->target_position.x() << "," << elem->target_position.y() << ") " + "at target_height: " << elem->target_height << " layer: " << layer_idx; + TreeSupport::showError("Uninitialized support element! A branch could be missing or exist partially.", true); + } + remove.emplace(elem); // we dont need to remove yet the parents as they will have a lower dtt and also no result_on_layer set + removed = true; + for (SupportElement* parent : elem->parents) + parent->result_on_layer = Point(-1, -1); // When the roof was not able to generate downwards enough, the top elements may have not moved, and have result_on_layer already set. As this branch needs to be removed => all parents result_on_layer have to be invalidated. + continue; + } else { + // set the point where the branch will be placed on the model + removed = setToModelContact(move_bounds, elem, layer_idx); + if (removed) + remove.emplace(elem); + } + } + + if (!removed) + setPointsOnAreas(elem); // element is valid now setting points in the layer above + } + + // delete all not needed support elements + for (SupportElement* del : remove) { + move_bounds[layer_idx].erase(del); + delete del->area; + delete del; + } + remove.clear(); + } +} + +void TreeSupport::generateBranchAreas( + std::vector> &linear_data, + std::vector> &layer_tree_polygons, + const std::map &inverse_tree_order) +{ +#ifdef SLIC3R_TREESUPPORTS_PROGRESS + double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC; + constexpr int progress_report_steps = 10; +#endif // SLIC3R_TREESUPPORTS_PROGRESS + + // Pre-generate a circle with correct diameter so that we don't have to recompute those (co)sines every time. + const Polygon branch_circle = make_circle(m_config.branch_radius, SUPPORT_TREE_CIRCLE_RESOLUTION); + std::vector linear_inserts(linear_data.size()); + +#ifdef SLIC3R_TREESUPPORTS_PROGRESS + const size_t progress_inserts_check_interval = linear_data.size() / progress_report_steps; +#endif // SLIC3R_TREESUPPORTS_PROGRESS + + std::mutex critical_sections; + tbb::parallel_for(tbb::blocked_range(0, linear_data.size()), + [&](const tbb::blocked_range &range) { + for (size_t idx = range.begin(); idx < range.end(); ++ idx) { + const LayerIndex layer_idx = linear_data[idx].first; + const SupportElement *elem = linear_data[idx].second; + const auto it_elem = inverse_tree_order.find(const_cast(elem)); + const SupportElement* child_elem = it_elem == inverse_tree_order.end() ? nullptr : it_elem->second; + const coord_t radius = m_config.getRadius(*elem); + bool parent_uses_min = false; + + // Calculate multiple ovalized circles, to connect with every parent and child. Also generate regular circle for the current layer. Merge all these into one area. + std::vector> movement_directions{ std::pair(Point(0, 0), radius) }; + if (!elem->skip_ovalisation) { + if (child_elem != nullptr) { + const Point movement = child_elem->result_on_layer - elem->result_on_layer; + movement_directions.emplace_back(movement, radius); + } + for (SupportElement *parent : elem->parents) { + const Point movement = parent->result_on_layer - elem->result_on_layer; + movement_directions.emplace_back(movement, std::max(m_config.getRadius(*parent), m_config.support_line_width)); + parent_uses_min |= parent->use_min_xy_dist; + } + } + + double max_speed = 0; + auto generateArea = [&volumes = m_volumes, layer_idx, elem, &branch_circle, branch_radius = m_config.branch_radius, support_line_width = m_config.support_line_width, &movement_directions, &max_speed, parent_uses_min]( + coord_t aoffset) { + Polygons poly; + + for (std::pair movement : movement_directions) { + max_speed = std::max(max_speed, movement.first.cast().norm()); + + // Visualization: https://jsfiddle.net/0zvcq39L/2/ + // Ovalizes the circle to an ellipse, that contains both old center and new target position. + double used_scale = (movement.second + aoffset) / (1.0 * branch_radius); + Point center_position = elem->result_on_layer + movement.first / 2; + const double moveX = movement.first.x() / (used_scale * branch_radius); + const double moveY = movement.first.y() / (used_scale * branch_radius); + const double vsize_inv = 0.5 / (0.01 + std::sqrt(moveX * moveX + moveY * moveY)); + + double matrix[] = { + used_scale * (1 + moveX * moveX * vsize_inv), + used_scale * (0 + moveX * moveY * vsize_inv), + used_scale * (0 + moveX * moveY * vsize_inv), + used_scale * (1 + moveY * moveY * vsize_inv), + }; + Polygon circle; + for (Point vertex : branch_circle) + circle.points.emplace_back(center_position + Point(matrix[0] * vertex.x() + matrix[1] * vertex.y(), matrix[2] * vertex.x() + matrix[3] * vertex.y())); + poly.emplace_back(std::move(circle)); + } + + poly = diff_clipped(offset(union_(poly), std::min(coord_t(50), support_line_width / 4), jtMiter, 1.2), + volumes.getCollision(0, layer_idx, parent_uses_min || elem->use_min_xy_dist)); // There seem to be some rounding errors, causing a branch to be a tiny bit further away from the model that it has to be. This can cause the tip to be slightly further away front the overhang (x/y wise) than optimal. This fixes it, and for every other part, 0.05mm will not be noticed. + return poly; + }; + + bool fast_relative_movement = max_speed > radius * 0.75; + + // ensure branch area will not overlap with model/collision. This can happen because of e.g. ovalization or increase_until_radius. + linear_inserts[idx] = generateArea(0); + + if (fast_relative_movement || m_config.getRadius(*elem) - m_config.getCollisionRadius(*elem) > m_config.support_line_width) { + // simulate the path the nozzle will take on the outermost wall + // if multiple parts exist, the outer line will not go all around the support part potentially causing support material to be printed mid air + ExPolygons nozzle_path = offset_ex(linear_inserts[idx], -m_config.support_line_width / 2); + if (nozzle_path.size() > 1) { + // Just try to make the area a tiny bit larger. + linear_inserts[idx] = generateArea(m_config.support_line_width / 2); + nozzle_path = offset_ex(linear_inserts[idx], -m_config.support_line_width / 2); + + // if larger area did not fix the problem, all parts off the nozzle path that do not contain the center point are removed, hoping for the best + if (nozzle_path.size() > 1) { + Polygons polygons_with_correct_center; + for (ExPolygon &part : nozzle_path) { + if (part.contains(elem->result_on_layer)) + polygons_with_correct_center = union_(polygons_with_correct_center, part); + else { + // try a fuzzy inside as sometimes the point should be on the border, but is not because of rounding errors... + Point from = elem->result_on_layer; + Polygons to = to_polygons(std::move(part)); + moveInside(to, from, 0); + if ((elem->result_on_layer - from).cast().norm() < scaled(0.025)) + polygons_with_correct_center = union_(polygons_with_correct_center, to); + } + } + // Increase the area again, to ensure the nozzle path when calculated later is very similar to the one assumed above. + linear_inserts[idx] = offset(polygons_with_correct_center, m_config.support_line_width / 2, jtMiter, 1.2); + linear_inserts[idx] = diff_clipped(linear_inserts[idx], m_volumes.getCollision(0, linear_data[idx].first, parent_uses_min || elem->use_min_xy_dist)); + } + } + } + +#ifdef SLIC3R_TREESUPPORTS_PROGRESS + if (idx % progress_inserts_check_interval == 0) { + std::lock_guard critical_section_progress(critical_sections); + progress_total += TREE_PROGRESS_GENERATE_BRANCH_AREAS / progress_report_steps; + Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL); + } +#endif + } + }); + + // single threaded combining all elements to the right layers. ONLY COPYS DATA! + for (coord_t i = 0; i < static_cast(linear_data.size()); i++) + layer_tree_polygons[linear_data[i].first].emplace(linear_data[i].second, linear_inserts[i]); +} + +void TreeSupport::smoothBranchAreas(std::vector>& layer_tree_polygons) +{ +#ifdef SLIC3R_TREESUPPORTS_PROGRESS + double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC + TREE_PROGRESS_GENERATE_BRANCH_AREAS; +#endif // SLIC3R_TREESUPPORTS_PROGRESS + + const coord_t max_radius_change_per_layer = 1 + m_config.support_line_width / 2; // this is the upper limit a radius may change per layer. +1 to avoid rounding errors + + // smooth upwards + for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(layer_tree_polygons.size()) - 1; ++ layer_idx) { + std::vector> processing; + processing.insert(processing.end(), layer_tree_polygons[layer_idx].begin(), layer_tree_polygons[layer_idx].end()); + std::vector>> update_next(processing.size()); // with this a lock can be avoided + + tbb::parallel_for(tbb::blocked_range(0, processing.size()), + [&](const tbb::blocked_range &range) { + for (size_t processing_idx = range.begin(); processing_idx < range.end(); ++ processing_idx) { + std::pair data_pair = processing[processing_idx]; + double max_outer_wall_distance = 0; + bool do_something = false; + for (SupportElement* parent : data_pair.first->parents) + if (m_config.getRadius(*parent) != m_config.getCollisionRadius(*parent)) { + do_something = true; + max_outer_wall_distance = std::max(max_outer_wall_distance, (data_pair.first->result_on_layer - parent->result_on_layer).cast().norm() - (m_config.getRadius(*data_pair.first) - m_config.getRadius(*parent))); + } + max_outer_wall_distance += max_radius_change_per_layer; // As this change is a bit larger than what usually appears, lost radius can be slowly reclaimed over the layers. + if (do_something) { + Polygons max_allowed_area = offset(data_pair.second, float(max_outer_wall_distance), jtMiter, 1.2); + for (SupportElement* parent : data_pair.first->parents) + if (m_config.getRadius(*parent) != m_config.getCollisionRadius(*parent)) + update_next[processing_idx].emplace_back(std::pair(parent, intersection(layer_tree_polygons[layer_idx + 1][parent], max_allowed_area))); + } + } + }); + + for (std::vector> data_vector : update_next) + for (std::pair data_pair : data_vector) + layer_tree_polygons[layer_idx + 1][data_pair.first] = data_pair.second; + } + +#ifdef SLIC3R_TREESUPPORTS_PROGRESS + progress_total += TREE_PROGRESS_SMOOTH_BRANCH_AREAS / 2; + Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL); // It is just assumed that both smoothing loops together are one third of the time spent in this function. This was guessed. As the whole function is only 10%, and the smoothing is hard to predict a progress report in the loop may be not useful. +#endif + + // smooth downwards + std::unordered_set updated_last_iteration; + for (int layer_idx = int(layer_tree_polygons.size()) - 2; layer_idx >= 0; -- layer_idx) { + std::vector> processing; + processing.insert(processing.end(), layer_tree_polygons[layer_idx].begin(), layer_tree_polygons[layer_idx].end()); + std::vector> update_next(processing.size(), std::pair(nullptr, Polygons())); // with this a lock can be avoided + + tbb::parallel_for(tbb::blocked_range(0, processing.size()), + [&](const tbb::blocked_range &range) { + for (size_t processing_idx = range.begin(); processing_idx < range.end(); ++ processing_idx) { + std::pair data_pair = processing[processing_idx]; + bool do_something = false; + Polygons max_allowed_area; + for (size_t idx = 0; idx < data_pair.first->parents.size(); ++ idx) { + SupportElement* parent = data_pair.first->parents[idx]; + coord_t max_outer_line_increase = max_radius_change_per_layer; + Polygons result = offset(layer_tree_polygons[layer_idx + 1][parent], max_outer_line_increase, jtMiter, 1.2); + Point direction = data_pair.first->result_on_layer - parent->result_on_layer; + // move the polygons object + for (auto& outer : result) + for (Point& p : outer) + p += direction; + append(max_allowed_area, std::move(result)); + do_something = do_something || updated_last_iteration.count(parent) || m_config.getCollisionRadius(*parent) != m_config.getRadius(*parent); + } + + if (do_something) { + Polygons result = intersection(max_allowed_area, data_pair.second); + if (area(result) < area(data_pair.second)) + update_next[processing_idx] = std::pair(data_pair.first, result); + } + } + }); + + updated_last_iteration.clear(); + for (std::pair data_pair : update_next) + if (data_pair.first != nullptr) { + updated_last_iteration.emplace(data_pair.first); + layer_tree_polygons[layer_idx][data_pair.first] = data_pair.second; + } + } + +#ifdef SLIC3R_TREESUPPORTS_PROGRESS + progress_total += TREE_PROGRESS_SMOOTH_BRANCH_AREAS / 2; + Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL); +#endif +} + +void TreeSupport::dropNonGraciousAreas( + std::vector> &layer_tree_polygons, + const std::vector> &linear_data, + std::vector>> &dropped_down_areas, + const std::map &inverse_tree_order) +{ + tbb::parallel_for(tbb::blocked_range(0, linear_data.size()), + [&](const tbb::blocked_range &range) { + for (size_t idx = range.begin(); idx < range.end(); ++ idx) { + SupportElement* elem = linear_data[idx].second; + bool non_gracious_model_contact = !elem->to_model_gracious && !inverse_tree_order.count(elem); // if a element has no child, it connects to whatever is below as no support further down for it will exist. + if (non_gracious_model_contact) { + Polygons rest_support = layer_tree_polygons[linear_data[idx].first][elem]; + LayerIndex counter = 1; + while (area(rest_support) > tiny_area_threshold && counter < linear_data[idx].first) { + rest_support = diff_clipped(rest_support, m_volumes.getCollision(0, linear_data[idx].first - counter, false)); + dropped_down_areas[idx].emplace_back(linear_data[idx].first - counter, rest_support); + counter++; + } + } + } + }); +} + +void TreeSupport::finalizeInterfaceAndSupportAreas( + const PrintObject &print_object, + const std::vector &overhangs, + std::vector &support_layer_storage, + std::vector &support_roof_storage, + + SupportGeneratorLayersPtr &bottom_contacts, + SupportGeneratorLayersPtr &top_contacts, + SupportGeneratorLayersPtr &intermediate_layers, + SupportGeneratorLayerStorage &layer_storage) +{ + InterfacePreference interface_pref = m_config.interface_preference; // InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE; + +#ifdef SLIC3R_TREESUPPORTS_PROGRESS + double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC + TREE_PROGRESS_GENERATE_BRANCH_AREAS + TREE_PROGRESS_SMOOTH_BRANCH_AREAS; +#endif // SLIC3R_TREESUPPORTS_PROGRESS + + // Iterate over the generated circles in parallel and clean them up. Also add support floor. + tbb::spin_mutex layer_storage_mutex; + tbb::parallel_for(tbb::blocked_range(0, support_layer_storage.size()), + [&](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { + // Most of the time in this function is this union call. Can take 300+ ms when a lot of areas are to be unioned. + support_layer_storage[layer_idx] = smooth_outward(union_(support_layer_storage[layer_idx]), m_config.support_line_width); //FIXME was .smooth(50); + //smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) : + + // simplify a bit, to ensure the output does not contain outrageous amounts of vertices. Should not be necessary, just a precaution. + support_layer_storage[layer_idx] = polygons_simplify(support_layer_storage[layer_idx], std::min(scaled(0.03), double(m_config.resolution))); + // Subtract support lines of the branches from the roof + SupportGeneratorLayer*& support_roof = top_contacts[layer_idx]; + if (! support_roof_storage[layer_idx].empty() || support_roof != nullptr) { + if (support_roof == nullptr) { + support_roof = &layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::TopContact, print_object.slicing_parameters(), layer_idx); + support_roof->polygons = union_(support_roof_storage[layer_idx]); + } else + support_roof->polygons = union_(support_roof->polygons, support_roof_storage[layer_idx]); + + if (! support_roof->polygons.empty() && + area(intersection(support_layer_storage[layer_idx], support_roof->polygons)) > tiny_area_threshold) { + switch (interface_pref) { + case InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT: + support_layer_storage[layer_idx] = diff(support_layer_storage[layer_idx], support_roof->polygons); + break; + case InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE: + support_roof->polygons = diff(support_roof->polygons, support_layer_storage[layer_idx]); + break; + //FIXME + #if 1 + case InterfacePreference::INTERFACE_LINES_OVERWRITE_SUPPORT: + case InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE: + assert(false); + [[fallthrough]]; + #else + case InterfacePreference::INTERFACE_LINES_OVERWRITE_SUPPORT: + { + // Hatch the support roof interfaces, offset them by their line width and subtract them from support base. + Polygons interface_lines = offset(to_polylines( + generateSupportInfillLines(support_roof->polygons, true, layer_idx, m_config.support_roof_line_distance)), + m_config.support_roof_line_width / 2); + support_layer_storage[layer_idx] = diff(support_layer_storage[layer_idx], interface_lines); + break; + } + case InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE: + { + // Hatch the support roof interfaces, offset them by their line width and subtract them from support base. + Polygons tree_lines = union_(offset(to_polylines( + generateSupportInfillLines(support_layer_storage[layer_idx], false, layer_idx, m_config.support_line_distance, true)), + m_config.support_line_width / 2)); + // do not draw roof where the tree is. I prefer it this way as otherwise the roof may cut of a branch from its support below. + support_roof->polygons = diff(support_roof->polygons, tree_lines); + break; + } + #endif + case InterfacePreference::NOTHING: + break; + } + } + } + + // Subtract support floors from the support area and add them to the support floor instead. + if (m_config.support_bottom_layers > 0 && !support_layer_storage[layer_idx].empty()) { + SupportGeneratorLayer*& support_bottom = bottom_contacts[layer_idx]; + Polygons layer_outset = diff_clipped( + m_config.support_bottom_offset > 0 ? offset(support_layer_storage[layer_idx], m_config.support_bottom_offset, jtMiter, 1.2) : support_layer_storage[layer_idx], + m_volumes.getCollision(0, layer_idx, false)); + Polygons floor_layer; + size_t layers_below = 0; + while (layers_below <= m_config.support_bottom_layers) { + // one sample at 0 layers below, another at m_config.support_bottom_layers. In-between samples at m_config.performance_interface_skip_layers distance from each other. + const size_t sample_layer = static_cast(std::max(0, (static_cast(layer_idx) - static_cast(layers_below)) - static_cast(m_config.z_distance_bottom_layers))); + //FIXME subtract the wipe tower + append(floor_layer, intersection(layer_outset, overhangs[sample_layer])); + if (layers_below < m_config.support_bottom_layers) + layers_below = std::min(layers_below + m_config.performance_interface_skip_layers, m_config.support_bottom_layers); + else + break; + } + if (! floor_layer.empty()) { + if (support_bottom == nullptr) + support_bottom = &layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::BottomContact, print_object.slicing_parameters(), layer_idx); + support_bottom->polygons = union_(floor_layer, support_bottom->polygons); + support_layer_storage[layer_idx] = diff_clipped(support_layer_storage[layer_idx], offset(support_bottom->polygons, scaled(0.01), jtMiter, 1.2)); // Subtract the support floor from the normal support. + } + } + + if (! support_layer_storage[layer_idx].empty()) { + SupportGeneratorLayer *&l = intermediate_layers[layer_idx]; + if (l == nullptr) + l = &layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::Base, print_object.slicing_parameters(), layer_idx); + append(l->polygons, union_(support_layer_storage[layer_idx])); + } + +#ifdef SLIC3R_TREESUPPORTS_PROGRESS + { + std::lock_guard critical_section_progress(critical_sections); + progress_total += TREE_PROGRESS_FINALIZE_BRANCH_AREAS / support_layer_storage.size(); + Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL); + } +#endif +#if 0 + { + std::lock_guard lock(critical_sections); + if (!storage.support.supportLayers[layer_idx].support_infill_parts.empty() || !storage.support.supportLayers[layer_idx].support_roof.empty()) + storage.support.layer_nr_max_filled_layer = std::max(storage.support.layer_nr_max_filled_layer, static_cast(layer_idx)); + } +#endif + } + }); +} + +void TreeSupport::drawAreas( + PrintObject &print_object, + const std::vector &overhangs, + std::vector> &move_bounds, + + SupportGeneratorLayersPtr &bottom_contacts, + SupportGeneratorLayersPtr &top_contacts, + SupportGeneratorLayersPtr &intermediate_layers, + SupportGeneratorLayerStorage &layer_storage) +{ + std::vector support_layer_storage(move_bounds.size()); + std::vector support_roof_storage(move_bounds.size()); + std::map inverese_tree_order; // in the tree structure only the parents can be accessed. Inverse this to be able to access the children. + std::vector> linear_data; // All SupportElements are put into a layer independent storage to improve parallelization. Was added at a point in time where this function had performance issues. + // These were fixed by creating less initial points, but i do not see a good reason to remove a working performance optimization. + for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(move_bounds.size()); ++ layer_idx) { + for (SupportElement* elem : move_bounds[layer_idx]) { + if ((layer_idx > 0 && ((!inverese_tree_order.count(elem) && elem->target_height == layer_idx) || (inverese_tree_order.count(elem) && inverese_tree_order[elem]->result_on_layer == Point(-1, -1))))) // we either come from nowhere at the final layer or we had invalid parents 2. should never happen but just to be sure + continue; + for (SupportElement* par : elem->parents) + if (par->result_on_layer != Point(-1, -1)) + inverese_tree_order.emplace(par, elem); + linear_data.emplace_back(layer_idx, elem); + } + } + + std::vector> layer_tree_polygons(move_bounds.size()); // reorder the processed data by layers again. The map also could be a vector>. + auto t_start = std::chrono::high_resolution_clock::now(); + // Generate the circles that will be the branches. + generateBranchAreas(linear_data, layer_tree_polygons, inverese_tree_order); + auto t_generate = std::chrono::high_resolution_clock::now(); + // In some edgecases a branch may go though a hole, where the regular radius does not fit. This can result in an apparent jump in branch radius. As such this cases need to be caught and smoothed out. + smoothBranchAreas(layer_tree_polygons); + auto t_smooth = std::chrono::high_resolution_clock::now(); + // drop down all trees that connect non gracefully with the model + std::vector>> dropped_down_areas(linear_data.size()); + dropNonGraciousAreas(layer_tree_polygons, linear_data, dropped_down_areas, inverese_tree_order); + auto t_drop = std::chrono::high_resolution_clock::now(); + // single threaded combining all dropped down support areas to the right layers. ONLY COPYS DATA! + for (coord_t i = 0; i < static_cast(dropped_down_areas.size()); i++) + for (std::pair &pair : dropped_down_areas[i]) + append(support_layer_storage[pair.first], std::move(pair.second)); + + // single threaded combining all support areas to the right layers. ONLY COPYS DATA! + for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(layer_tree_polygons.size()); ++ layer_idx) { + auto &this_layer_tree_polygons = layer_tree_polygons[layer_idx]; + auto &this_roofs = support_roof_storage[layer_idx]; + auto &this_layers = support_layer_storage[layer_idx]; + size_t cnt_roofs = 0; + size_t cnt_layers = 0; + for (const std::pair &data_pair : this_layer_tree_polygons) + ++ (data_pair.first->missing_roof_layers > data_pair.first->distance_to_top ? cnt_roofs : cnt_layers); + this_roofs.reserve(this_roofs.size() + cnt_roofs); + this_layers.reserve(this_layers.size() + cnt_layers); + for (const std::pair &data_pair : this_layer_tree_polygons) { + auto &src = const_cast(data_pair.second); + std::move(std::begin(src), std::end(src), std::back_inserter(data_pair.first->missing_roof_layers > data_pair.first->distance_to_top ? this_roofs : this_layers)); + } + } + + finalizeInterfaceAndSupportAreas(print_object, overhangs, support_layer_storage, support_roof_storage, + bottom_contacts, top_contacts, intermediate_layers, layer_storage); + auto t_end = std::chrono::high_resolution_clock::now(); + + auto dur_gen_tips = 0.001 * std::chrono::duration_cast(t_generate - t_start).count(); + auto dur_smooth = 0.001 * std::chrono::duration_cast(t_smooth - t_generate).count(); + auto dur_drop = 0.001 * std::chrono::duration_cast(t_drop - t_smooth).count(); + auto dur_finalize = 0.001 * std::chrono::duration_cast(t_end - t_drop).count(); + + BOOST_LOG_TRIVIAL(info) << + "Time used for drawing subfuctions: generateBranchAreas: " << dur_gen_tips << " ms " + "smoothBranchAreas: " << dur_smooth << " ms " + "dropNonGraciousAreas: " << dur_drop << " ms " + "finalizeInterfaceAndSupportAreas " << dur_finalize << " ms"; +} + +} // namespace Slic3r diff --git a/src/libslic3r/TreeSupport.hpp b/src/libslic3r/TreeSupport.hpp new file mode 100644 index 000000000..75c32e54d --- /dev/null +++ b/src/libslic3r/TreeSupport.hpp @@ -0,0 +1,876 @@ +// Tree supports by Thomas Rahm, losely based on Tree Supports by CuraEngine. +// Original source of Thomas Rahm's tree supports: +// https://github.com/ThomasRahm/CuraEngine +// +// Original CuraEngine copyright: +// Copyright (c) 2021 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. + +#ifndef slic3r_TreeSupport_hpp +#define slic3r_TreeSupport_hpp + +#include "TreeModelVolumes.hpp" +#include "Point.hpp" + +#include // For combining hashes + +#include "BoundingBox.hpp" +#include "Utils.hpp" + + #define TREE_SUPPORT_SHOW_ERRORS + +#define SUPPORT_TREE_CIRCLE_RESOLUTION 25 // The number of vertices in each circle. + +#ifdef SLIC3R_TREESUPPORTS_PROGRESS + // The various stages of the process can be weighted differently in the progress bar. + // These weights are obtained experimentally using a small sample size. Sensible weights can differ drastically based on the assumed default settings and model. + #define TREE_PROGRESS_TOTAL 10000 + #define TREE_PROGRESS_PRECALC_COLL TREE_PROGRESS_TOTAL * 0.1 + #define TREE_PROGRESS_PRECALC_AVO TREE_PROGRESS_TOTAL * 0.4 + #define TREE_PROGRESS_GENERATE_NODES TREE_PROGRESS_TOTAL * 0.1 + #define TREE_PROGRESS_AREA_CALC TREE_PROGRESS_TOTAL * 0.3 + #define TREE_PROGRESS_DRAW_AREAS TREE_PROGRESS_TOTAL * 0.1 + #define TREE_PROGRESS_GENERATE_BRANCH_AREAS TREE_PROGRESS_DRAW_AREAS / 3 + #define TREE_PROGRESS_SMOOTH_BRANCH_AREAS TREE_PROGRESS_DRAW_AREAS / 3 + #define TREE_PROGRESS_FINALIZE_BRANCH_AREAS TREE_PROGRESS_DRAW_AREAS / 3 +#endif // SLIC3R_TREESUPPORTS_PROGRESS + +#define SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL false +#define SUPPORT_TREE_AVOID_SUPPORT_BLOCKER true + +namespace Slic3r +{ + +using LayerIndex = int; + +static constexpr const double SUPPORT_TREE_EXPONENTIAL_FACTOR = 1.5; +static constexpr const coord_t SUPPORT_TREE_EXPONENTIAL_THRESHOLD = scaled(1. * SUPPORT_TREE_EXPONENTIAL_FACTOR); +static constexpr const coord_t SUPPORT_TREE_COLLISION_RESOLUTION = scaled(0.5); + +//FIXME +class Print; +class PrintObject; +class SupportGeneratorLayer; +using SupportGeneratorLayerStorage = std::deque; +using SupportGeneratorLayersPtr = std::vector; +/*! + * \brief Generates a tree structure to support your models. + */ + +class TreeSupport +{ +public: + using AvoidanceType = TreeModelVolumes::AvoidanceType; + enum class InterfacePreference + { + INTERFACE_AREA_OVERWRITES_SUPPORT, + SUPPORT_AREA_OVERWRITES_INTERFACE, + INTERFACE_LINES_OVERWRITE_SUPPORT, + SUPPORT_LINES_OVERWRITE_INTERFACE, + NOTHING + }; + + /*! + * \brief Creates an instance of the tree support generator. + */ + TreeSupport() = default; + + /*! + * \brief Create the areas that need support. + * + * These areas are stored inside the given SliceDataStorage object. + * \param storage The data storage where the mesh data is gotten from and + * where the resulting support areas are stored. + */ + void generateSupportAreas(Print &print, const BuildVolume &build_volume, const std::vector& print_object_ids); + void generateSupportAreas(PrintObject &print_object); + + //todo Remove! Only relevant for public BETA! + static bool inline showed_critical=false; + static bool inline showed_performance=false; + static void showError(std::string message,bool critical); + + struct TreeSupportSettings; // forward declaration as we need some config values in the merge case + + struct AreaIncreaseSettings + { + AvoidanceType type { AvoidanceType::Fast }; + coord_t increase_speed { 0 }; + bool increase_radius { false }; + bool no_error { false }; + bool use_min_distance { false }; + bool move { false }; + bool operator==(const AreaIncreaseSettings& other) const + { + return increase_radius == other.increase_radius && increase_speed == other.increase_speed && type == other.type && + no_error == other.no_error && use_min_distance == other.use_min_distance && move == other.move; + } + }; + + struct SupportElement + { + explicit SupportElement( + coord_t distance_to_top, size_t target_height, Point target_position, bool to_buildplate, bool to_model_gracious, bool use_min_xy_dist, size_t dont_move_until, + bool supports_roof, bool can_use_safe_radius, bool force_tips_to_roof, bool skip_ovalisation) : + target_height(target_height), target_position(target_position), next_position(target_position), next_height(target_height), effective_radius_height(distance_to_top), + to_buildplate(to_buildplate), distance_to_top(distance_to_top), area(nullptr), result_on_layer(target_position), increased_to_model_radius(0), to_model_gracious(to_model_gracious), + elephant_foot_increases(0), use_min_xy_dist(use_min_xy_dist), supports_roof(supports_roof), dont_move_until(dont_move_until), can_use_safe_radius(can_use_safe_radius), + last_area_increase(AreaIncreaseSettings{ AvoidanceType::Fast, 0, false, false, false, false }), missing_roof_layers(force_tips_to_roof ? dont_move_until : 0), skip_ovalisation(skip_ovalisation) + { + } + + + explicit SupportElement(const SupportElement& elem, Polygons* newArea = nullptr) + : // copy constructor with possibility to set a new area + target_height(elem.target_height), + target_position(elem.target_position), + next_position(elem.next_position), + next_height(elem.next_height), + effective_radius_height(elem.effective_radius_height), + to_buildplate(elem.to_buildplate), + distance_to_top(elem.distance_to_top), + area(newArea != nullptr ? newArea : elem.area), + result_on_layer(elem.result_on_layer), + increased_to_model_radius(elem.increased_to_model_radius), + to_model_gracious(elem.to_model_gracious), + elephant_foot_increases(elem.elephant_foot_increases), + use_min_xy_dist(elem.use_min_xy_dist), + supports_roof(elem.supports_roof), + dont_move_until(elem.dont_move_until), + can_use_safe_radius(elem.can_use_safe_radius), + last_area_increase(elem.last_area_increase), + missing_roof_layers(elem.missing_roof_layers), + skip_ovalisation(elem.skip_ovalisation) + + { + parents.insert(parents.begin(), elem.parents.begin(), elem.parents.end()); + } + + /*! + * \brief Create a new Element for one layer below the element of the pointer supplied. + */ + + explicit SupportElement(SupportElement* element_above) + : target_height(element_above->target_height), + target_position(element_above->target_position), + next_position(element_above->next_position), + next_height(element_above->next_height), + effective_radius_height(element_above->effective_radius_height), + to_buildplate(element_above->to_buildplate), + distance_to_top(element_above->distance_to_top + 1), + area(element_above->area), + result_on_layer(Point(-1, -1)), // set to invalid as we are a new node on a new layer + increased_to_model_radius(element_above->increased_to_model_radius), + to_model_gracious(element_above->to_model_gracious), + elephant_foot_increases(element_above->elephant_foot_increases), + use_min_xy_dist(element_above->use_min_xy_dist), + supports_roof(element_above->supports_roof), + dont_move_until(element_above->dont_move_until), + can_use_safe_radius(element_above->can_use_safe_radius), + last_area_increase(element_above->last_area_increase), + missing_roof_layers(element_above->missing_roof_layers), + skip_ovalisation(false) + { + parents = { element_above }; + } + + // ONLY to be called in merge as it assumes a few assurances made by it. + explicit SupportElement( + const SupportElement& first, const SupportElement& second, size_t next_height, Point next_position, + coord_t increased_to_model_radius, const TreeSupportSettings& config) : + next_position(next_position), next_height(next_height), area(nullptr), increased_to_model_radius(increased_to_model_radius), + use_min_xy_dist(first.use_min_xy_dist || second.use_min_xy_dist), supports_roof(first.supports_roof || second.supports_roof), + dont_move_until(std::max(first.dont_move_until, second.dont_move_until)), can_use_safe_radius(first.can_use_safe_radius || second.can_use_safe_radius), + missing_roof_layers(std::min(first.missing_roof_layers, second.missing_roof_layers)), skip_ovalisation(false) + + { + if (first.target_height > second.target_height) { + target_height = first.target_height; + target_position = first.target_position; + } else { + target_height = second.target_height; + target_position = second.target_position; + } + effective_radius_height = std::max(first.effective_radius_height, second.effective_radius_height); + distance_to_top = std::max(first.distance_to_top, second.distance_to_top); + + to_buildplate = first.to_buildplate && second.to_buildplate; + to_model_gracious = first.to_model_gracious && second.to_model_gracious; // valid as we do not merge non-gracious with gracious + + AddParents(first.parents); + AddParents(second.parents); + + elephant_foot_increases = 0; + if (config.diameter_scale_bp_radius > 0) { + coord_t foot_increase_radius = std::abs(std::max(config.getCollisionRadius(second), config.getCollisionRadius(first)) - config.getCollisionRadius(*this)); + // elephant_foot_increases has to be recalculated, as when a smaller tree with a larger elephant_foot_increases merge with a larger branch + // the elephant_foot_increases may have to be lower as otherwise the radius suddenly increases. This results often in a non integer value. + elephant_foot_increases = foot_increase_radius / (config.branch_radius * (config.diameter_scale_bp_radius - config.diameter_angle_scale_factor)); + } + + // set last settings to the best out of both parents. If this is wrong, it will only cause a small performance penalty instead of weird behavior. + last_area_increase = { + std::min(first.last_area_increase.type, second.last_area_increase.type), + std::min(first.last_area_increase.increase_speed, second.last_area_increase.increase_speed), + first.last_area_increase.increase_radius || second.last_area_increase.increase_radius, + first.last_area_increase.no_error || second.last_area_increase.no_error, + first.last_area_increase.use_min_distance && second.last_area_increase.use_min_distance, + first.last_area_increase.move || second.last_area_increase.move }; + } + + /*! + * \brief The layer this support elements wants reach + */ + LayerIndex target_height; + + /*! + * \brief The position this support elements wants to support on layer=target_height + */ + Point target_position; + + /*! + * \brief The next position this support elements wants to reach. NOTE: This is mainly a suggestion regarding direction inside the influence area. + */ + Point next_position; + + + /*! + * \brief The next height this support elements wants to reach + */ + LayerIndex next_height; + + /*! + * \brief The Effective distance to top of this element regarding radius increases and collision calculations. + */ + + size_t effective_radius_height; + + /*! + * \brief The element trys to reach the buildplate + */ + + bool to_buildplate; + + /*! + * \brief All elements in the layer above the current one that are supported by this element + */ + std::vector parents; + + /*! + * \brief The amount of layers this element is below the topmost layer of this branch. + */ + size_t distance_to_top; + + /*! + * \brief The resulting influence area. + * Will only be set in the results of createLayerPathing, and will be nullptr inside! + */ + Polygons* area; + + /*! + * \brief The resulting center point around which a circle will be drawn later. + * Will be set by setPointsOnAreas + */ + Point result_on_layer = Point(-1, -1); + /*! + * \brief The amount of extra radius we got from merging branches that could have reached the buildplate, but merged with ones that can not. + */ + coord_t increased_to_model_radius; // how much to model we increased only relevant for merging + /*! + * \brief Will the branch be able to rest completely on a flat surface, be it buildplate or model ? + */ + bool to_model_gracious; + + /*! + * \brief Counter about the times the elephant foot was increased. Can be fractions for merge reasons. + */ + double elephant_foot_increases; + + /*! + * \brief Whether the min_xy_distance can be used to get avoidance or similar. Will only be true if support_xy_overrides_z=Z overrides X/Y. + */ + bool use_min_xy_dist; + + /*! + * \brief True if this Element or any parent provides support to a support roof. + */ + bool supports_roof; + + /*! + * \brief The element trys not to move until this dtt is reached, is set to 0 if the element had to move. + */ + size_t dont_move_until; + + /*! + * \brief An influence area is considered safe when it can use the holefree avoidance <=> It will not have to encounter holes on its way downward. + */ + bool can_use_safe_radius; + + /*! + * \brief Settings used to increase the influence area to its current state. + */ + AreaIncreaseSettings last_area_increase; + + /*! + * \brief Amount of roof layers that were not yet added, because the branch needed to move. + */ + size_t missing_roof_layers; + + /*! + * \brief Skip the ovalisation to parent and children when generating the final circles. + */ + bool skip_ovalisation; + + bool operator==(const SupportElement& other) const + { + return target_position == other.target_position && target_height == other.target_height; + } + + bool operator<(const SupportElement& other) const // true if me < other + { + return !(*this == other) && !(*this > other); + } + bool operator>(const SupportElement& other) const + { + // Doesn't really have to make sense, only required for ordering in maps to ensure deterministic behavior. + if (*this == other) + return false; + if (other.target_height != target_height) + return other.target_height < target_height; + return other.target_position.x() == target_position.x() ? other.target_position.y() < target_position.y() : other.target_position.x() < target_position.x(); + } + + void AddParents(const std::vector& adding) + { + for (SupportElement* ptr : adding) + { + parents.emplace_back(ptr); + } + } + }; + + /*! + * \brief This struct contains settings used in the tree support. Thanks to this most functions do not need to know of meshes etc. Also makes the code shorter. + */ + struct TreeSupportSettings + { + TreeSupportSettings() = default; // required for the definition of the config variable in the TreeSupport class. + + explicit TreeSupportSettings(const TreeSupportMeshGroupSettings& mesh_group_settings) + : angle(mesh_group_settings.support_tree_angle), + angle_slow(mesh_group_settings.support_tree_angle_slow), + support_line_width(mesh_group_settings.support_line_width), + layer_height(mesh_group_settings.layer_height), + branch_radius(mesh_group_settings.support_tree_branch_diameter / 2), + min_radius(mesh_group_settings.support_tree_tip_diameter / 2), // The actual radius is 50 microns larger as the resulting branches will be increased by 50 microns to avoid rounding errors effectively increasing the xydistance + maximum_move_distance((angle < M_PI / 2.) ? (coord_t)(tan(angle) * layer_height) : std::numeric_limits::max()), + maximum_move_distance_slow((angle_slow < M_PI / 2.) ? (coord_t)(tan(angle_slow) * layer_height) : std::numeric_limits::max()), + support_bottom_layers(mesh_group_settings.support_bottom_enable ? (mesh_group_settings.support_bottom_height + layer_height / 2) / layer_height : 0), + tip_layers(std::max((branch_radius - min_radius) / (support_line_width / 3), branch_radius / layer_height)), // Ensure lines always stack nicely even if layer height is large + diameter_angle_scale_factor(sin(mesh_group_settings.support_tree_branch_diameter_angle) * layer_height / branch_radius), + max_to_model_radius_increase(mesh_group_settings.support_tree_max_diameter_increase_by_merges_when_support_to_model / 2), + min_dtt_to_model(round_up_divide(mesh_group_settings.support_tree_min_height_to_model, layer_height)), + increase_radius_until_radius(mesh_group_settings.support_tree_branch_diameter / 2), + increase_radius_until_layer(increase_radius_until_radius <= branch_radius ? tip_layers * (increase_radius_until_radius / branch_radius) : (increase_radius_until_radius - branch_radius) / (branch_radius * diameter_angle_scale_factor)), + support_rests_on_model(! mesh_group_settings.support_material_buildplate_only), + xy_distance(mesh_group_settings.support_xy_distance), + xy_min_distance(std::min(mesh_group_settings.support_xy_distance, mesh_group_settings.support_xy_distance_overhang)), + bp_radius(mesh_group_settings.support_tree_bp_diameter / 2), + diameter_scale_bp_radius(std::min(sin(0.7) * layer_height / branch_radius, 1.0 / (branch_radius / (support_line_width / 2.0)))), // Either 40? or as much as possible so that 2 lines will overlap by at least 50%, whichever is smaller. + z_distance_top_layers(round_up_divide(mesh_group_settings.support_top_distance, layer_height)), + z_distance_bottom_layers(round_up_divide(mesh_group_settings.support_bottom_distance, layer_height)), + performance_interface_skip_layers(round_up_divide(mesh_group_settings.support_interface_skip_height, layer_height)), +// support_infill_angles(mesh_group_settings.support_infill_angles), + support_roof_angles(mesh_group_settings.support_roof_angles), + roof_pattern(mesh_group_settings.support_roof_pattern), + support_pattern(mesh_group_settings.support_pattern), + support_roof_line_width(mesh_group_settings.support_roof_line_width), + support_line_spacing(mesh_group_settings.support_line_spacing), + support_bottom_offset(mesh_group_settings.support_bottom_offset), + support_wall_count(mesh_group_settings.support_wall_count), + resolution(mesh_group_settings.resolution), + support_roof_line_distance(mesh_group_settings.support_roof_line_distance), // in the end the actual infill has to be calculated to subtract interface from support areas according to interface_preference. + settings(mesh_group_settings), + min_feature_size(mesh_group_settings.min_feature_size) + + + { + layer_start_bp_radius = (bp_radius - branch_radius) / (branch_radius * diameter_scale_bp_radius); + + if (TreeSupport::TreeSupportSettings::soluble) { + // safeOffsetInc can only work in steps of the size xy_min_distance in the worst case => xy_min_distance has to be a bit larger than 0 in this worst case and should be large enough for performance to not suffer extremely + // When for all meshes the z bottom and top distance is more than one layer though the worst case is xy_min_distance + min_feature_size + // This is not the best solution, but the only one to ensure areas can not lag though walls at high maximum_move_distance. + xy_min_distance = std::max(xy_min_distance, scaled(0.1)); + xy_distance = std::max(xy_distance, xy_min_distance); + } + + +// const std::unordered_map interface_map = { { "support_area_overwrite_interface_area", InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE }, { "interface_area_overwrite_support_area", InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT }, { "support_lines_overwrite_interface_area", InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE }, { "interface_lines_overwrite_support_area", InterfacePreference::INTERFACE_LINES_OVERWRITE_SUPPORT }, { "nothing", InterfacePreference::NOTHING } }; +// interface_preference = interface_map.at(mesh_group_settings.get("support_interface_priority")); +//FIXME this was the default +// interface_preference = InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE; + interface_preference = InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE; + } + + private: + double angle; + double angle_slow; + std::vector known_z; + + public: + // some static variables dependent on other meshes that are not currently processed. + // Has to be static because TreeSupportConfig will be used in TreeModelVolumes as this reduces redundancy. + inline static bool soluble = false; + /*! + * \brief Width of a single line of support. + */ + coord_t support_line_width; + /*! + * \brief Height of a single layer + */ + coord_t layer_height; + /*! + * \brief Radius of a branch when it has left the tip. + */ + coord_t branch_radius; + /*! + * \brief smallest allowed radius, required to ensure that even at DTT 0 every circle will still be printed + */ + coord_t min_radius; + /*! + * \brief How far an influence area may move outward every layer at most. + */ + coord_t maximum_move_distance; + /*! + * \brief How far every influence area will move outward every layer if possible. + */ + coord_t maximum_move_distance_slow; + /*! + * \brief Amount of bottom layers. 0 if disabled. + */ + size_t support_bottom_layers; + /*! + * \brief Amount of effectiveDTT increases are required to reach branch radius. + */ + size_t tip_layers; + /*! + * \brief Factor by which to increase the branch radius. + */ + double diameter_angle_scale_factor; + /*! + * \brief How much a branch resting on the model may grow in radius by merging with branches that can reach the buildplate. + */ + coord_t max_to_model_radius_increase; + /*! + * \brief If smaller (in layers) than that, all branches to model will be deleted + */ + size_t min_dtt_to_model; + /*! + * \brief Increase radius in the resulting drawn branches, even if the avoidance does not allow it. Will be cut later to still fit. + */ + coord_t increase_radius_until_radius; + /*! + * \brief Same as increase_radius_until_radius, but contains the DTT at which the radius will be reached. + */ + size_t increase_radius_until_layer; + /*! + * \brief True if the branches may connect to the model. + */ + bool support_rests_on_model; + /*! + * \brief How far should support be from the model. + */ + coord_t xy_distance; + /*! + * \brief Radius a branch should have when reaching the buildplate. + */ + coord_t bp_radius; + /*! + * \brief The layer index at which an increase in radius may be required to reach the bp_radius. + */ + coord_t layer_start_bp_radius; + /*! + * \brief Factor by which to increase the branch radius to reach the required bp_radius at layer 0. Note that this radius increase will not happen in the tip, to ensure the tip is structurally sound. + */ + double diameter_scale_bp_radius; + /*! + * \brief minimum xy_distance. Only relevant when Z overrides XY, otherwise equal to xy_distance- + */ + coord_t xy_min_distance; + /*! + * \brief Amount of layers distance required the top of the support to the model + */ + size_t z_distance_top_layers; + /*! + * \brief Amount of layers distance required from the top of the model to the bottom of a support structure. + */ + size_t z_distance_bottom_layers; + /*! + * \brief used for performance optimization at the support floor. Should have no impact on the resulting tree. + */ + size_t performance_interface_skip_layers; + /*! + * \brief User specified angles for the support infill. + */ +// std::vector support_infill_angles; + /*! + * \brief User specified angles for the support roof infill. + */ + std::vector support_roof_angles; + /*! + * \brief Pattern used in the support roof. May contain non relevant data if support roof is disabled. + */ + SupportMaterialInterfacePattern roof_pattern; + /*! + * \brief Pattern used in the support infill. + */ + SupportMaterialPattern support_pattern; + /*! + * \brief Line width of the support roof. + */ + coord_t support_roof_line_width; + /*! + * \brief Distance between support infill lines. + */ + coord_t support_line_spacing; + /*! + * \brief Offset applied to the support floor area. + */ + coord_t support_bottom_offset; + /* + * \brief Amount of walls the support area will have. + */ + int support_wall_count; + /* + * \brief Maximum allowed deviation when simplifying. + */ + coord_t resolution; + /* + * \brief Distance between the lines of the roof. + */ + coord_t support_roof_line_distance; + /* + * \brief How overlaps of an interface area with a support area should be handled. + */ + InterfacePreference interface_preference; + + /* + * \brief The infill class wants a settings object. This one will be the correct one for all settings it uses. + */ + TreeSupportMeshGroupSettings settings; + + /* + * \brief Minimum thickness of any model features. + */ + coord_t min_feature_size; + + public: + bool operator==(const TreeSupportSettings& other) const + { + return branch_radius == other.branch_radius && tip_layers == other.tip_layers && diameter_angle_scale_factor == other.diameter_angle_scale_factor && layer_start_bp_radius == other.layer_start_bp_radius && bp_radius == other.bp_radius && diameter_scale_bp_radius == other.diameter_scale_bp_radius && min_radius == other.min_radius && xy_min_distance == other.xy_min_distance && // as a recalculation of the collision areas is required to set a new min_radius. + xy_distance - xy_min_distance == other.xy_distance - other.xy_min_distance && // if the delta of xy_min_distance and xy_distance is different the collision areas have to be recalculated. + support_rests_on_model == other.support_rests_on_model && increase_radius_until_layer == other.increase_radius_until_layer && min_dtt_to_model == other.min_dtt_to_model && max_to_model_radius_increase == other.max_to_model_radius_increase && maximum_move_distance == other.maximum_move_distance && maximum_move_distance_slow == other.maximum_move_distance_slow && z_distance_bottom_layers == other.z_distance_bottom_layers && support_line_width == other.support_line_width && + support_line_spacing == other.support_line_spacing && support_roof_line_width == other.support_roof_line_width && // can not be set on a per-mesh basis currently, so code to enable processing different roof line width in the same iteration seems useless. + support_bottom_offset == other.support_bottom_offset && support_wall_count == other.support_wall_count && support_pattern == other.support_pattern && roof_pattern == other.roof_pattern && // can not be set on a per-mesh basis currently, so code to enable processing different roof patterns in the same iteration seems useless. + support_roof_angles == other.support_roof_angles && + //support_infill_angles == other.support_infill_angles && + increase_radius_until_radius == other.increase_radius_until_radius && support_bottom_layers == other.support_bottom_layers && layer_height == other.layer_height && z_distance_top_layers == other.z_distance_top_layers && resolution == other.resolution && // Infill generation depends on deviation and resolution. + support_roof_line_distance == other.support_roof_line_distance && interface_preference == other.interface_preference + && min_feature_size == other.min_feature_size // interface_preference should be identical to ensure the tree will correctly interact with the roof. + // The infill class now wants the settings object and reads a lot of settings, and as the infill class is used to calculate support roof lines for interface-preference. Not all of these may be required to be identical, but as I am not sure, better safe than sorry +#if 0 + && (interface_preference == InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT || interface_preference == InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE + // Perimeter generator parameters + || + (settings.get("fill_outline_gaps") == other.settings.get("fill_outline_gaps") && + settings.get("min_bead_width") == other.settings.get("min_bead_width") && + settings.get("wall_transition_angle") == other.settings.get("wall_transition_angle") && + settings.get("wall_transition_length") == other.settings.get("wall_transition_length") && + settings.get("wall_split_middle_threshold") == other.settings.get("wall_split_middle_threshold") && + settings.get("wall_add_middle_threshold") == other.settings.get("wall_add_middle_threshold") && + settings.get("wall_distribution_count") == other.settings.get("wall_distribution_count") && + settings.get("wall_transition_filter_distance") == other.settings.get("wall_transition_filter_distance") && + settings.get("wall_transition_filter_deviation") == other.settings.get("wall_transition_filter_deviation") && + settings.get("wall_line_width_x") == other.settings.get("wall_line_width_x") && + settings.get("meshfix_maximum_extrusion_area_deviation") == other.settings.get("meshfix_maximum_extrusion_area_deviation")) + ) +#endif + ; + } + + + /*! + * \brief Get the Distance to top regarding the real radius this part will have. This is different from distance_to_top, which is can be used to calculate the top most layer of the branch. + * \param elem[in] The SupportElement one wants to know the effectiveDTT + * \return The Effective DTT. + */ + [[nodiscard]] inline size_t getEffectiveDTT(const TreeSupport::SupportElement& elem) const + { + return elem.effective_radius_height < increase_radius_until_layer ? (elem.distance_to_top < increase_radius_until_layer ? elem.distance_to_top : increase_radius_until_layer) : elem.effective_radius_height; + } + + /*! + * \brief Get the Radius part will have based on numeric values. + * \param distance_to_top[in] The effective distance_to_top of the element + * \param elephant_foot_increases[in] The elephant_foot_increases of the element. + * \return The radius an element with these attributes would have. + */ + [[nodiscard]] inline coord_t getRadius(size_t distance_to_top, const double elephant_foot_increases = 0) const + { + return (distance_to_top <= tip_layers ? min_radius + (branch_radius - min_radius) * distance_to_top / tip_layers : // tip + branch_radius + // base + branch_radius * (distance_to_top - tip_layers) * diameter_angle_scale_factor) + + // gradual increase + branch_radius * elephant_foot_increases * (std::max(diameter_scale_bp_radius - diameter_angle_scale_factor, 0.0)); + } + + /*! + * \brief Get the Radius, that this element will have. + * \param elem[in] The Element. + * \return The radius the element has. + */ + [[nodiscard]] inline coord_t getRadius(const TreeSupport::SupportElement& elem) const + { + return getRadius(getEffectiveDTT(elem), elem.elephant_foot_increases); + } + + /*! + * \brief Get the collision Radius of this Element. This can be smaller then the actual radius, as the drawAreas will cut off areas that may collide with the model. + * \param elem[in] The Element. + * \return The collision radius the element has. + */ + [[nodiscard]] inline coord_t getCollisionRadius(const TreeSupport::SupportElement& elem) const + { + return getRadius(elem.effective_radius_height, elem.elephant_foot_increases); + } + + /*! + * \brief Get the Radius an element should at least have at a given layer. + * \param layer_idx[in] The layer. + * \return The radius every element should aim to achieve. + */ + [[nodiscard]] inline coord_t recommendedMinRadius(LayerIndex layer_idx) const + { + double scale = (layer_start_bp_radius - int(layer_idx)) * diameter_scale_bp_radius; + return scale > 0 ? branch_radius + branch_radius * scale : 0; + } + + /*! + * \brief Return on which z in microns the layer will be printed. Used only for support infill line generation. + * \param layer_idx[in] The layer. + * \return The radius every element should aim to achieve. + */ + [[nodiscard]] inline coord_t getActualZ(LayerIndex layer_idx) + { + return layer_idx < coord_t(known_z.size()) ? known_z[layer_idx] : (layer_idx - known_z.size()) * layer_height + known_z.size() ? known_z.back() : 0; + } + + /*! + * \brief Set the z every Layer is printed at. Required for getActualZ to work + * \param z[in] The z every LayerIndex is printed. Vector is used as a map with the index of each element being the corresponding LayerIndex + * \return The radius every element should aim to achieve. + */ + void setActualZ(std::vector& z) + { + known_z = z; + } + }; + +private: + /*! + * \brief Creates the initial influence areas (that can later be propagated down) by placing them below the overhang. + * + * Generates Points where the Model should be supported and creates the areas where these points have to be placed. + * + * \param mesh[in] The mesh that is currently processed. + * \param move_bounds[out] Storage for the influence areas. + * \param storage[in] Background storage, required for adding roofs. + */ + void generateInitialAreas(const PrintObject &print_object, + const std::vector &overhangs, + std::vector> &move_bounds, + SupportGeneratorLayersPtr &top_contacts, + SupportGeneratorLayersPtr &top_interface_layers, + SupportGeneratorLayerStorage &layer_storage); + + /*! + * \brief Checks if an influence area contains a valid subsection and returns the corresponding metadata and the new Influence area. + * + * Calculates an influence areas of the layer below, based on the influence area of one element on the current layer. + * Increases every influence area by maximum_move_distance_slow. If this is not enough, as in we would change our gracious or to_buildplate status the influence areas are instead increased by maximum_move_distance_slow. + * Also ensures that increasing the radius of a branch, does not cause it to change its status (like to_buildplate ). If this were the case, the radius is not increased instead. + * + * Warning: The used format inside this is different as the SupportElement does not have a valid area member. Instead this area is saved as value of the dictionary. This was done to avoid not needed heap allocations. + * + * \param settings[in] Which settings have to be used to check validity. + * \param layer_idx[in] Number of the current layer. + * \param parent[in] The metadata of the parents influence area. + * \param relevant_offset[in] The maximal possible influence area. No guarantee regarding validity with current layer collision required, as it is ensured in-function! + * \param to_bp_data[out] The part of the Influence area that can reach the buildplate. + * \param to_model_data[out] The part of the Influence area that do not have to reach the buildplate. This has overlap with new_layer_data. + * \param increased[out] Area than can reach all further up support points. No assurance is made that the buildplate or the model can be reached in accordance to the user-supplied settings. + * \param overspeed[in] How much should the already offset area be offset again. Usually this is 0. + * \param mergelayer[in] Will the merge method be called on this layer. This information is required as some calculation can be avoided if they are not required for merging. + * \return A valid support element for the next layer regarding the calculated influence areas. Empty if no influence are can be created using the supplied influence area and settings. + */ + std::optional increaseSingleArea(AreaIncreaseSettings settings, LayerIndex layer_idx, SupportElement* parent, const Polygons& relevant_offset, Polygons& to_bp_data, Polygons& to_model_data, Polygons& increased, const coord_t overspeed, const bool mergelayer); + /*! + * \brief Increases influence areas as far as required. + * + * Calculates influence areas of the layer below, based on the influence areas of the current layer. + * Increases every influence area by maximum_move_distance_slow. If this is not enough, as in it would change the gracious or to_buildplate status, the influence areas are instead increased by maximum_move_distance. + * Also ensures that increasing the radius of a branch, does not cause it to change its status (like to_buildplate ). If this were the case, the radius is not increased instead. + * + * Warning: The used format inside this is different as the SupportElement does not have a valid area member. Instead this area is saved as value of the dictionary. This was done to avoid not needed heap allocations. + * + * \param to_bp_areas[out] Influence areas that can reach the buildplate + * \param to_model_areas[out] Influence areas that do not have to reach the buildplate. This has overlap with new_layer_data, as areas that can reach the buildplate are also considered valid areas to the model. + * This redundancy is required if a to_buildplate influence area is allowed to merge with a to model influence area. + * \param influence_areas[out] Area than can reach all further up support points. No assurance is made that the buildplate or the model can be reached in accordance to the user-supplied settings. + * \param bypass_merge_areas[out] Influence areas ready to be added to the layer below that do not need merging. + * \param last_layer[in] Influence areas of the current layer. + * \param layer_idx[in] Number of the current layer. + * \param mergelayer[in] Will the merge method be called on this layer. This information is required as some calculation can be avoided if they are not required for merging. + */ + void increaseAreas(std::unordered_map& to_bp_areas, std::unordered_map& to_model_areas, std::map& influence_areas, std::vector& bypass_merge_areas, const std::vector& last_layer, const LayerIndex layer_idx, const bool mergelayer); + + /*! + * \brief Propagates influence downwards, and merges overlapping ones. + * + * \param move_bounds[in,out] All currently existing influence areas + */ + void createLayerPathing(std::vector>& move_bounds); + + + /*! + * \brief Sets the result_on_layer for all parents based on the SupportElement supplied. + * + * \param elem[in] The SupportElements, which parent's position should be determined. + */ + void setPointsOnAreas(const SupportElement* elem); + /*! + * \brief Get the best point to connect to the model and set the result_on_layer of the relevant SupportElement accordingly. + * + * \param move_bounds[in,out] All currently existing influence areas + * \param first_elem[in,out] SupportElement that did not have its result_on_layer set meaning that it does not have a child element. + * \param layer_idx[in] The current layer. + * \return Should elem be deleted. + */ + bool setToModelContact(std::vector>& move_bounds, SupportElement* first_elem, const LayerIndex layer_idx); + + /*! + * \brief Set the result_on_layer point for all influence areas + * + * \param move_bounds[in,out] All currently existing influence areas + */ + void createNodesFromArea(std::vector>& move_bounds); + + /*! + * \brief Draws circles around result_on_layer points of the influence areas + * + * \param linear_data[in] All currently existing influence areas with the layer they are on + * \param layer_tree_polygons[out] Resulting branch areas with the layerindex they appear on. layer_tree_polygons.size() has to be at least linear_data.size() as each Influence area in linear_data will save have at least one (that's why it's a vector) corresponding branch area in layer_tree_polygons. + * \param inverse_tree_order[in] A mapping that returns the child of every influence area. + */ + void generateBranchAreas(std::vector>& linear_data, std::vector>& layer_tree_polygons, const std::map& inverse_tree_order); + + /*! + * \brief Applies some smoothing to the outer wall, intended to smooth out sudden jumps as they can happen when a branch moves though a hole. + * + * \param layer_tree_polygons[in,out] Resulting branch areas with the layerindex they appear on. + */ + void smoothBranchAreas(std::vector>& layer_tree_polygons); + + /*! + * \brief Drop down areas that do rest non-gracefully on the model to ensure the branch actually rests on something. + * + * \param layer_tree_polygons[in] Resulting branch areas with the layerindex they appear on. + * \param linear_data[in] All currently existing influence areas with the layer they are on + * \param dropped_down_areas[out] Areas that have to be added to support all non-graceful areas. + * \param inverse_tree_order[in] A mapping that returns the child of every influence area. + */ + void dropNonGraciousAreas(std::vector>& layer_tree_polygons, const std::vector>& linear_data, std::vector>>& dropped_down_areas, const std::map& inverse_tree_order); + + /*! + * \brief Generates Support Floor, ensures Support Roof can not cut of branches, and saves the branches as support to storage + * + * \param support_layer_storage[in] Areas where support should be generated. + * \param support_roof_storage[in] Areas where support was replaced with roof. + * \param storage[in,out] The storage where the support should be stored. + */ + void finalizeInterfaceAndSupportAreas( + const PrintObject &print_object, + const std::vector &overhangs, + std::vector &support_layer_storage, + std::vector &support_roof_storage, + + SupportGeneratorLayersPtr &bottom_contacts, + SupportGeneratorLayersPtr &top_contacts, + SupportGeneratorLayersPtr &intermediate_layers, + SupportGeneratorLayerStorage &layer_storage); + + /*! + * \brief Draws circles around result_on_layer points of the influence areas and applies some post processing. + * + * \param move_bounds[in] All currently existing influence areas + * \param storage[in,out] The storage where the support should be stored. + */ + void drawAreas( + PrintObject &print_object, + const std::vector &overhangs, + std::vector> &move_bounds, + + SupportGeneratorLayersPtr &bottom_contacts, + SupportGeneratorLayersPtr &top_contacts, + SupportGeneratorLayersPtr &intermediate_layers, + SupportGeneratorLayerStorage &layer_storage); + + /*! + * \brief Settings with the indexes of meshes that use these settings. + * + */ + std::vector>> m_grouped_meshes; + + /*! + * \brief Generator for model collision, avoidance and internal guide volumes. + * + */ + TreeModelVolumes m_volumes; + + /*! + * \brief Contains config settings to avoid loading them in every function. This was done to improve readability of the code. + */ + TreeSupportSettings m_config; + + /*! + * \brief The progress multiplier of all values added progress bar. + * Required for the progress bar the behave as expected when areas have to be calculated multiple times + */ + double m_progress_multiplier = 1; + + /*! + * \brief The progress offset added to all values communicated to the progress bar. + * Required for the progress bar the behave as expected when areas have to be calculated multiple times + */ + double m_progress_offset = 0; +}; + + +} // namespace Slic3r + +namespace std +{ +template <> +struct hash +{ + size_t operator()(const Slic3r::TreeSupport::SupportElement& node) const + { + size_t hash_node = Slic3r::PointHash{}(node.target_position); + boost::hash_combine(hash_node, size_t(node.target_height)); + return hash_node; + } +}; +} // namespace std + +#endif /* slic3r_TreeSupport_hpp */ diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 6a09e7fbf..73bb418af 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -192,6 +192,14 @@ inline INDEX_TYPE next_idx_modulo(INDEX_TYPE idx, const INDEX_TYPE count) return idx; } + +// Return dividend divided by divisor rounded to the nearest integer +template +inline INDEX_TYPE round_up_divide(const INDEX_TYPE dividend, const INDEX_TYPE divisor) +{ + return (dividend + divisor - 1) / divisor; +} + template inline typename CONTAINER_TYPE::size_type prev_idx_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container) { diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 6f71930ab..2285c29a6 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -345,6 +345,25 @@ constexpr T NaN = std::numeric_limits::quiet_NaN(); constexpr float NaNf = NaN; constexpr double NaNd = NaN; +// Rounding up. +// 1.5 is rounded to 2 +// 1.49 is rounded to 1 +// 0.5 is rounded to 1, +// 0.49 is rounded to 0 +// -0.5 is rounded to 0, +// -0.51 is rounded to -1, +// -1.5 is rounded to -1. +// -1.51 is rounded to -2. +// If input is not a valid float (it is infinity NaN or if it does not fit) +// the float to int conversion produces a max int on Intel and +-max int on ARM. +template +inline IntegerOnly fast_round_up(double a) +{ + // Why does Java Math.round(0.49999999999999994) return 1? + // https://stackoverflow.com/questions/9902968/why-does-math-round0-49999999999999994-return-1 + return a == 0.49999999999999994 ? I(0) : I(floor(a + 0.5)); +} + } // namespace Slic3r #endif diff --git a/src/libslic3r/pchheader.hpp b/src/libslic3r/pchheader.hpp index aeb79e3d8..9017a5dea 100644 --- a/src/libslic3r/pchheader.hpp +++ b/src/libslic3r/pchheader.hpp @@ -133,6 +133,4 @@ #include "libslic3r.h" #include "libslic3r_version.h" -#include - #include diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 371b8eb11..d9ca1d532 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -127,9 +127,11 @@ static struct RunOnInit { void disable_multi_threading() { - // Disable parallelization so the Shiny profiler works + // Disable parallelization to simplify debugging. #ifdef TBB_HAS_GLOBAL_CONTROL - tbb::global_control(tbb::global_control::max_allowed_parallelism, 1); + { + static tbb::global_control gc(tbb::global_control::max_allowed_parallelism, 1); + } #else // TBB_HAS_GLOBAL_CONTROL static tbb::task_scheduler_init *tbb_init = new tbb::task_scheduler_init(1); UNUSED(tbb_init); diff --git a/src/occt_wrapper/CMakeLists.txt b/src/occt_wrapper/CMakeLists.txt index ed75531a9..8ab9fb4be 100644 --- a/src/occt_wrapper/CMakeLists.txt +++ b/src/occt_wrapper/CMakeLists.txt @@ -50,6 +50,8 @@ set(OCCT_LIBS TKernel ) +slic3r_remap_configs("${OCCT_LIBS}" RelWithDebInfo Release) + target_include_directories(OCCTWrapper PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_include_directories(OCCTWrapper PUBLIC ${OpenCASCADE_INCLUDE_DIR}) target_link_libraries(OCCTWrapper ${OCCT_LIBS}) diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 71a4941a4..5a5e2bb5a 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -244,7 +244,6 @@ set(SLIC3R_GUI_SOURCES Utils/PresetUpdater.hpp Utils/Process.cpp Utils/Process.hpp - Utils/Profile.hpp Utils/UndoRedo.cpp Utils/UndoRedo.hpp Utils/HexFile.cpp diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 32b21e4f9..9945b4047 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1648,6 +1648,13 @@ static void thick_lines_to_geometry( double width_initial = 0.0; double bottom_z_initial = 0.0; + // Reserve for a smooth path. Likley the path will not be that smooth, but better than nothing. + // Allocated 1.5x more data than minimum. + // Number of indices, not triangles. + geometry.reserve_more_indices((lines.size() * 8 * 3) * 3 / 2); + // Number of vertices, not floats. + geometry.reserve_more_vertices(((lines.size() + 1) * 4) * 3 / 2); + // loop once more in case of closed loops const size_t lines_end = closed ? (lines.size() + 1) : lines.size(); for (size_t ii = 0; ii < lines_end; ++ii) { diff --git a/src/slic3r/GUI/AboutDialog.cpp b/src/slic3r/GUI/AboutDialog.cpp index bfd6b55dc..4b8b5caa4 100644 --- a/src/slic3r/GUI/AboutDialog.cpp +++ b/src/slic3r/GUI/AboutDialog.cpp @@ -114,7 +114,6 @@ void CopyrightsDialog::fill_entries() { "Expat" , "1998-2000 Thai Open Source Software Center Ltd and Clark Cooper" "2001-2016 Expat maintainers" , "http://www.libexpat.org/" }, { "AVRDUDE" , "2018 Free Software Foundation, Inc." , "http://savannah.nongnu.org/projects/avrdude" }, - { "Shinyprofiler" , "2007-2010 Aidin Abedi" , "http://code.google.com/p/shinyprofiler/" }, { "Real-Time DXT1/DXT5 C compression library" , "Based on original by fabian \"ryg\" giesen v1.04. " "Custom version, modified by Yann Collet" , "https://github.com/Cyan4973/RygsDXTc" }, diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 37e527d64..53790e076 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -23,6 +23,7 @@ #include "libslic3r/Format/SL1.hpp" #include "libslic3r/Thread.hpp" #include "libslic3r/libslic3r.h" +#include "libslic3r/BuildVolume.hpp" #include #include @@ -144,7 +145,21 @@ std::string BackgroundSlicingProcess::output_filepath_for_project(const boost::f void BackgroundSlicingProcess::process_fff() { assert(m_print == m_fff_print); - m_print->process(); + + // Checks that the print does not exceed the max print height + const BuildVolume& build_volume = GUI::wxGetApp().mainframe->m_plater->build_volume(); + auto objects = m_fff_print->objects(); + for (auto obj : objects) { + std::vector layer_height_profile; + PrintObject::update_layer_height_profile(*obj->model_object(), obj->slicing_parameters(), layer_height_profile); + auto layers = generate_object_layers(obj->slicing_parameters(), layer_height_profile); + if (!layers.empty() && layers.back() > build_volume.max_print_height()) { + throw Slic3r::SlicingError("The print is taller than the maximum allowed height. You might want to reduce the size of your model" + " or change current print settings and retry."); + } + } + + m_print->process(); wxCommandEvent evt(m_event_slicing_completed_id); // Post the Slicing Finished message for the G-code viewer to update. // Passing the timestamp diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 8c5bfffc6..1a4eb5cc8 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -777,7 +777,7 @@ void SpinCtrl::BUILD() { break; } - const int min_val = m_opt.min == INT_MIN + const int min_val = m_opt.min == -FLT_MAX #ifdef __WXOSX__ // We will forcibly set the input value for SpinControl, since the value // inserted from the keyboard is not updated under OSX. @@ -786,8 +786,8 @@ void SpinCtrl::BUILD() { // less then min_val. || m_opt.min > 0 #endif - ? 0 : m_opt.min; - const int max_val = m_opt.max < 2147483647 ? m_opt.max : 2147483647; + ? (int)0 : (int)m_opt.min; + const int max_val = m_opt.max < FLT_MAX ? (int)m_opt.max : INT_MAX; auto temp = new wxSpinCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, wxTE_PROCESS_ENTER | wxSP_ARROW_KEYS @@ -848,7 +848,7 @@ void SpinCtrl::BUILD() { if (!parsed || value < INT_MIN || value > INT_MAX) tmp_value = UNDEF_VALUE; else { - tmp_value = std::min(std::max((int)value, m_opt.min), m_opt.max); + tmp_value = std::min(std::max((int)value, temp->GetMin()), temp->GetMax()); #ifdef __WXOSX__ // Forcibly set the input value for SpinControl, since the value // inserted from the keyboard or clipboard is not updated under OSX @@ -1616,8 +1616,8 @@ void SliderCtrl::BUILD() auto temp = new wxBoxSizer(wxHORIZONTAL); auto def_val = m_opt.get_default_value()->value; - auto min = m_opt.min == INT_MIN ? 0 : m_opt.min; - auto max = m_opt.max == INT_MAX ? 100 : m_opt.max; + auto min = m_opt.min == -FLT_MAX ? 0 : (int)m_opt.min; + auto max = m_opt.max == FLT_MAX ? 100 : INT_MAX; m_slider = new wxSlider(m_parent, wxID_ANY, def_val * m_scale, min * m_scale, max * m_scale, diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 3e7939749..54b4eb1ec 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -51,7 +51,7 @@ static EMoveType buffer_type(unsigned char id) { // Equivalent to conversion to string with sprintf(buf, "%.2g", value) and conversion back to float, but faster. static float round_to_bin(const float value) { -// assert(value > 0); +// assert(value >= 0); constexpr float const scale [5] = { 100.f, 1000.f, 10000.f, 100000.f, 1000000.f }; constexpr float const invscale [5] = { 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f }; constexpr float const threshold[5] = { 0.095f, 0.0095f, 0.00095f, 0.000095f, 0.0000095f }; @@ -59,7 +59,12 @@ static float round_to_bin(const float value) int i = 0; // While the scaling factor is not yet large enough to get two integer digits after scaling and rounding: for (; value < threshold[i] && i < 4; ++ i) ; - return std::round(value * scale[i]) * invscale[i]; + // At least on MSVC std::round() calls a complex function, which is pretty expensive. + // our fast_round_up is much cheaper and it could be inlined. +// return std::round(value * scale[i]) * invscale[i]; + double a = value * scale[i]; + assert(std::abs(a) < double(std::numeric_limits::max())); + return fast_round_up(a) * invscale[i]; } void GCodeViewer::VBuffer::reset() @@ -3283,6 +3288,7 @@ void GCodeViewer::render_toolpaths() shader->set_uniform("emission_factor", 0.0f); } else { + shader->set_uniform("emission_factor", 0.15f); #if ENABLE_LEGACY_OPENGL_REMOVAL const int position_id = shader->get_attrib_location("v_position"); const int normal_id = shader->get_attrib_location("v_normal"); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 5815c6b33..dde881e44 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3311,9 +3311,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) mouse_up_cleanup(); m_mouse.set_start_position_3D_as_invalid(); -#if ENABLE_OBJECT_MANIPULATOR_FOCUS - handle_sidebar_focus_event("", false); -#endif // ENABLE_OBJECT_MANIPULATOR_FOCUS return; } @@ -3321,9 +3318,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) mouse_up_cleanup(); m_mouse.set_start_position_3D_as_invalid(); -#if ENABLE_OBJECT_MANIPULATOR_FOCUS - handle_sidebar_focus_event("", false); -#endif // ENABLE_OBJECT_MANIPULATOR_FOCUS return; } @@ -3331,9 +3325,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) mouse_up_cleanup(); m_mouse.set_start_position_3D_as_invalid(); -#if ENABLE_OBJECT_MANIPULATOR_FOCUS - handle_sidebar_focus_event("", false); -#endif // ENABLE_OBJECT_MANIPULATOR_FOCUS return; } @@ -3341,9 +3332,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) mouse_up_cleanup(); m_mouse.set_start_position_3D_as_invalid(); -#if ENABLE_OBJECT_MANIPULATOR_FOCUS - handle_sidebar_focus_event("", false); -#endif // ENABLE_OBJECT_MANIPULATOR_FOCUS return; } @@ -3388,9 +3376,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } -#if ENABLE_OBJECT_MANIPULATOR_FOCUS - handle_sidebar_focus_event("", false); -#endif // ENABLE_OBJECT_MANIPULATOR_FOCUS return; } @@ -3405,39 +3390,27 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_mouse.set_move_start_threshold_position_2D_as_invalid(); } -#if ENABLE_OBJECT_MANIPULATOR_FOCUS - if (evt.ButtonDown()) { - std::string curr_sidebar_field = m_sidebar_field; - handle_sidebar_focus_event("", false); - if (boost::algorithm::istarts_with(curr_sidebar_field, "layer")) - // restore visibility of layers hints after left clicking on the 3D scene - m_sidebar_field = curr_sidebar_field; - if (wxWindow::FindFocus() != m_canvas) - // Grab keyboard focus on any mouse click event. - m_canvas->SetFocus(); - } -#else if (evt.ButtonDown() && wxWindow::FindFocus() != m_canvas) // Grab keyboard focus on any mouse click event. m_canvas->SetFocus(); -#endif // ENABLE_OBJECT_MANIPULATOR_FOCUS if (evt.Entering()) { //#if defined(__WXMSW__) || defined(__linux__) // // On Windows and Linux needs focus in order to catch key events -#if !ENABLE_OBJECT_MANIPULATOR_FOCUS - // Set focus in order to remove it from sidebar fields -#endif // !ENABLE_OBJECT_MANIPULATOR_FOCUS + // Set focus in order to remove it from object list if (m_canvas != nullptr) { -#if !ENABLE_OBJECT_MANIPULATOR_FOCUS - // Only set focus, if the top level window of this canvas is active. + // Only set focus, if the top level window of this canvas is active + // and ObjectList has a focus auto p = dynamic_cast(evt.GetEventObject()); while (p->GetParent()) p = p->GetParent(); - auto *top_level_wnd = dynamic_cast(p); - if (top_level_wnd && top_level_wnd->IsActive()) +#ifdef __WIN32__ + wxWindow* const obj_list = wxGetApp().obj_list()->GetMainWindow(); +#else + wxWindow* const obj_list = wxGetApp().obj_list(); +#endif + if (obj_list == p->FindFocus()) m_canvas->SetFocus(); -#endif // !ENABLE_OBJECT_MANIPULATOR_FOCUS m_mouse.position = pos.cast(); m_tooltip_enabled = false; // 1) forces a frame render to ensure that m_hover_volume_idxs is updated even when the user right clicks while @@ -7135,6 +7108,11 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c return volume; }; const size_t volumes_cnt_initial = m_volumes.volumes.size(); + // Limit the number of threads as the code below does not scale well due to memory pressure. + // (most of the time is spent in malloc / free / memmove) + // Not using all the threads leaves some of the threads to G-code generator. + tbb::task_arena limited_arena(std::min(tbb::this_task_arena::max_concurrency(), 4)); + limited_arena.execute([&ctxt, grain_size, &new_volume, is_selected_separate_extruder, this]{ tbb::parallel_for( tbb::blocked_range(0, ctxt.layers.size(), grain_size), [&ctxt, &new_volume, is_selected_separate_extruder, this](const tbb::blocked_range& range) { @@ -7309,6 +7287,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c vol->indexed_vertex_array.shrink_to_fit(); #endif // ENABLE_LEGACY_OPENGL_REMOVAL }); + }); // task arena BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results" << m_volumes.log_memory_info() << log_memory_info(); // Remove empty volumes from the newly added volumes. diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 5ae7f9fbf..2d6a66d43 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -94,22 +94,8 @@ void GLModel::Geometry::add_vertex(const Vec3f& position) void GLModel::Geometry::add_vertex(const Vec3f& position, const Vec2f& tex_coord) { assert(format.vertex_layout == EVertexLayout::P3T2); - vertices.emplace_back(position.x()); - vertices.emplace_back(position.y()); - vertices.emplace_back(position.z()); - vertices.emplace_back(tex_coord.x()); - vertices.emplace_back(tex_coord.y()); -} - -void GLModel::Geometry::add_vertex(const Vec3f& position, const Vec3f& normal) -{ - assert(format.vertex_layout == EVertexLayout::P3N3); - vertices.emplace_back(position.x()); - vertices.emplace_back(position.y()); - vertices.emplace_back(position.z()); - vertices.emplace_back(normal.x()); - vertices.emplace_back(normal.y()); - vertices.emplace_back(normal.z()); + vertices.insert(vertices.end(), position.data(), position.data() + 3); + vertices.insert(vertices.end(), tex_coord.data(), tex_coord.data() + 2); } void GLModel::Geometry::add_vertex(const Vec3f& position, const Vec3f& normal, const Vec2f& tex_coord) @@ -161,13 +147,6 @@ void GLModel::Geometry::add_line(unsigned int id1, unsigned int id2) indices.emplace_back(id2); } -void GLModel::Geometry::add_triangle(unsigned int id1, unsigned int id2, unsigned int id3) -{ - indices.emplace_back(id1); - indices.emplace_back(id2); - indices.emplace_back(id3); -} - Vec2f GLModel::Geometry::extract_position_2(size_t id) const { const size_t p_stride = position_stride_floats(format); diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index f3c62d786..c2845c885 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -4,6 +4,7 @@ #include "libslic3r/Point.hpp" #include "libslic3r/BoundingBox.hpp" #include "libslic3r/Color.hpp" +#include "libslic3r/Utils.hpp" #include #include @@ -90,13 +91,19 @@ namespace GUI { ColorRGBA color{ ColorRGBA::BLACK() }; void reserve_vertices(size_t vertices_count) { vertices.reserve(vertices_count * vertex_stride_floats(format)); } + void reserve_more_vertices(size_t vertices_count) { vertices.reserve(next_highest_power_of_2(vertices.size() + vertices_count * vertex_stride_floats(format))); } void reserve_indices(size_t indices_count) { indices.reserve(indices_count); } + void reserve_more_indices(size_t indices_count) { indices.reserve(next_highest_power_of_2(indices.size() + indices_count)); } void add_vertex(const Vec2f& position); // EVertexLayout::P2 void add_vertex(const Vec2f& position, const Vec2f& tex_coord); // EVertexLayout::P2T2 void add_vertex(const Vec3f& position); // EVertexLayout::P3 void add_vertex(const Vec3f& position, const Vec2f& tex_coord); // EVertexLayout::P3T2 - void add_vertex(const Vec3f& position, const Vec3f& normal); // EVertexLayout::P3N3 + void add_vertex(const Vec3f& position, const Vec3f& normal) { // EVertexLayout::P3N3 + assert(format.vertex_layout == EVertexLayout::P3N3); + vertices.insert(vertices.end(), position.data(), position.data() + 3); + vertices.insert(vertices.end(), normal.data(), normal.data() + 3); + } void add_vertex(const Vec3f& position, const Vec3f& normal, const Vec2f& tex_coord); // EVertexLayout::P3N3T2 #if ENABLE_OPENGL_ES void add_vertex(const Vec3f& position, const Vec3f& normal, const Vec3f& extra); // EVertexLayout::P3N3E3 @@ -109,7 +116,11 @@ namespace GUI { void add_index(unsigned int id); void add_line(unsigned int id1, unsigned int id2); - void add_triangle(unsigned int id1, unsigned int id2, unsigned int id3); + void add_triangle(unsigned int id1, unsigned int id2, unsigned int id3){ + indices.emplace_back(id1); + indices.emplace_back(id2); + indices.emplace_back(id3); + } Vec2f extract_position_2(size_t id) const; Vec3f extract_position_3(size_t id) const; diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp index a3b22d020..2374b088b 100644 --- a/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -603,7 +603,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo else glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data())); - if (use_mipmaps) { + if (use_mipmaps && OpenGLManager::use_manually_generated_mipmaps()) { // we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards int lod_w = m_width; int lod_h = m_height; @@ -632,8 +632,9 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level)); glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)); } - } - else { + } else if (use_mipmaps && !OpenGLManager::use_manually_generated_mipmaps()) { + glGenerateMipmap(GL_TEXTURE_2D); + } else { glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0)); } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index ca0b82c9a..8638d4aae 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -896,6 +896,7 @@ static boost::optional parse_semver_from_ini(std::string path) void GUI_App::init_app_config() { // Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release. + // SetAppName(SLIC3R_APP_KEY); SetAppName(SLIC3R_APP_KEY "-alpha"); // SetAppName(SLIC3R_APP_KEY "-beta"); @@ -2828,6 +2829,11 @@ NotificationManager * GUI_App::notification_manager() return plater_->get_notification_manager(); } +GalleryDialog* GUI_App::gallery_dialog() +{ + return mainframe->gallery_dialog(); +} + // extruders count from selected printer preset int GUI_App::extruders_cnt() const { diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index a5b998fda..a329995e0 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -47,6 +47,7 @@ class ObjectLayers; class Plater; class NotificationManager; struct GUI_InitParams; +class GalleryDialog; @@ -296,6 +297,7 @@ public: const Plater* plater() const; Model& model(); NotificationManager * notification_manager(); + GalleryDialog * gallery_dialog(); // Parameters extracted from the command line to be passed to GUI after initialization. GUI_InitParams* init_params { nullptr }; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 6b315bbeb..b8c04f8cf 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -976,7 +976,7 @@ void ObjectList::show_context_menu(const bool evt_context_menu) void ObjectList::extruder_editing() { wxDataViewItem item = GetSelection(); - if (!item || !(m_objects_model->GetItemType(item) & (itVolume | itObject))) + if (!item || !(m_objects_model->GetItemType(item) & (itVolume | itObject | itLayer))) return; wxRect rect = this->GetItemRect(item, GetColumn(colExtruder)); @@ -1001,6 +1001,7 @@ void ObjectList::extruder_editing() m_extruder_editor->Hide(); update_extruder_in_config(item); + Refresh(); }; // to avoid event propagation to other sidebar items @@ -1409,9 +1410,8 @@ void ObjectList::load_subobject(ModelVolumeType type, bool from_galery/* = false wxArrayString input_files; if (from_galery) { - GalleryDialog dlg(this); - if (dlg.ShowModal() != wxID_CLOSE) - dlg.get_input_files(input_files); + if (wxGetApp().gallery_dialog()->show() != wxID_CLOSE) + wxGetApp().gallery_dialog()->get_input_files(input_files); } else wxGetApp().import_model(wxGetApp().tab_panel()->GetPage(0), input_files); @@ -1737,10 +1737,10 @@ void ObjectList::load_shape_object_from_gallery() return;// Add nothing if something is selected on 3DScene wxArrayString input_files; - GalleryDialog gallery_dlg(this); - if (gallery_dlg.ShowModal() == wxID_CLOSE) + GalleryDialog* gallery_dlg = wxGetApp().gallery_dialog(); + if (gallery_dlg->show() == wxID_CLOSE) return; - gallery_dlg.get_input_files(input_files); + gallery_dlg->get_input_files(input_files); if (input_files.IsEmpty()) return; load_shape_object_from_gallery(input_files); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index a538f2b33..62de19811 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -257,9 +257,9 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : // axis_name->SetForegroundColour(wxColour(axes_color_text[axis_idx])); sizer = new wxBoxSizer(wxHORIZONTAL); - // Under OSX we use font, smaller than default font, so + // Under OSX or Linux with GTK3 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) + if (wxOSX || wxGTK3) sizer->SetMinSize(-1, m_word_local_combo->GetBestHeight(-1)); sizer->Add(axis_name, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, border); @@ -590,7 +590,10 @@ void ObjectManipulation::update_ui_from_settings() for (int i = 0; i < 3; ++i) { auto update = [this, i](/*ManipulationEditorKey*/int key_id, const Vec3d& new_value) { - wxString new_text = double_to_string(m_imperial_units ? new_value(i) * mm_to_in : new_value(i), 2); + double value = new_value(i); + if (m_imperial_units) + value *= mm_to_in; + wxString new_text = double_to_string(value, m_imperial_units && key_id == 3/*meSize*/ ? 4 : 2); const int id = key_id * 3 + i; if (id >= 0) m_editors[id]->set_value(new_text); }; @@ -776,14 +779,22 @@ void ObjectManipulation::update_if_dirty() 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); + wxString new_text = double_to_string(new_value(i), m_imperial_units && key_id == meSize ? 4 : 2); double new_rounded; new_text.ToDouble(&new_rounded); if (std::abs(cached_rounded(i) - new_rounded) > EPSILON) { cached_rounded(i) = new_rounded; const int id = key_id*3+i; - if (m_imperial_units && (key_id == mePosition || key_id == meSize)) - new_text = double_to_string(new_value(i)*mm_to_in, 2); + if (m_imperial_units) { + double inch_value = new_value(i) * mm_to_in; + if (key_id == mePosition) + new_text = double_to_string(inch_value, 2); + if (key_id == meSize) { + if(std::abs(m_cache.size_inches(i) - inch_value) > EPSILON) + m_cache.size_inches(i) = inch_value; + new_text = double_to_string(inch_value, 4); + } + } if (id >= 0) m_editors[id]->set_value(new_text); } cached(i) = new_value(i); @@ -1137,6 +1148,13 @@ void ObjectManipulation::change_size_value(int axis, double value) if (value <= 0.0) return; + if (m_imperial_units) { + if (std::abs(m_cache.size_inches(axis) - value) < EPSILON) + return; + m_cache.size_inches(axis) = value; + value *= in_to_mm; + } + if (std::abs(m_cache.size_rounded(axis) - value) < EPSILON) return; @@ -1237,11 +1255,11 @@ void ObjectManipulation::on_change(const std::string& opt_key, int axis, double if (!m_cache.is_valid()) return; - if (m_imperial_units && (opt_key == "position" || opt_key == "size")) - new_value *= in_to_mm; - - if (opt_key == "position") + if (opt_key == "position") { + if (m_imperial_units) + new_value *= in_to_mm; change_position_value(axis, new_value); + } else if (opt_key == "rotation") change_rotation_value(axis, new_value); else if (opt_key == "scale") { @@ -1328,6 +1346,15 @@ void ObjectManipulation::set_coordinates_type(ECoordinatesType type) canvas->set_as_dirty(); canvas->request_extra_frame(); } + +ECoordinatesType ObjectManipulation::get_coordinates_type() const +{ + const wxString og_name = get_og()->get_name(); + if (og_name.Contains(_L("Group manipulation"))) + return ECoordinatesType::World; + + return m_coordinates_type; +} #endif // ENABLE_WORLD_COORDINATE void ObjectManipulation::msw_rescale() @@ -1433,12 +1460,7 @@ ManipulationEditor::ManipulationEditor(ObjectManipulation* parent, { parent->set_focused_editor(nullptr); -#if ENABLE_OBJECT_MANIPULATOR_FOCUS - // if the widgets loosing focus is a manipulator field, call kill_focus - if (dynamic_cast(e.GetEventObject()) != nullptr) -#else if (!m_enter_pressed) -#endif // ENABLE_OBJECT_MANIPULATOR_FOCUS kill_focus(parent); e.Skip(); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index cfa43b43a..e9ffe804a 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -85,6 +85,7 @@ private: Vec3d scale; Vec3d scale_rounded; Vec3d size; + Vec3d size_inches; Vec3d size_rounded; wxString move_label_string; @@ -207,7 +208,7 @@ public: bool get_uniform_scaling() const { return m_uniform_scale; } #if ENABLE_WORLD_COORDINATE void set_coordinates_type(ECoordinatesType type); - ECoordinatesType get_coordinates_type() const { return m_coordinates_type; } + ECoordinatesType get_coordinates_type() const; bool is_world_coordinates() const { return m_coordinates_type == ECoordinatesType::World; } bool is_instance_coordinates() const { return m_coordinates_type == ECoordinatesType::Instance; } bool is_local_coordinates() const { return m_coordinates_type == ECoordinatesType::Local; } diff --git a/src/slic3r/GUI/GUI_ObjectSettings.hpp b/src/slic3r/GUI/GUI_ObjectSettings.hpp index 5d0be1308..677c6ab82 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.hpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.hpp @@ -30,6 +30,7 @@ public: virtual wxSizer* get_sizer(); ConfigOptionsGroup* get_og() { return m_og.get(); } + const ConfigOptionsGroup* get_og() const { return m_og.get(); } wxWindow* parent() const {return m_parent; } }; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 9e2fe46f6..4989d12ee 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -705,8 +705,18 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee m_layers_slider->SetLayersTimes(print_mode_stat.layers_times, print_mode_stat.time); } + // check if ticks_info_from_model contains ColorChange g-code + bool color_change_already_exists = false; + for (const CustomGCode::Item& gcode: ticks_info_from_model.gcodes) + if (gcode.type == CustomGCode::Type::ColorChange) { + color_change_already_exists = true; + break; + } + // Suggest the auto color change, if model looks like sign - if (m_layers_slider->IsNewPrint()) + if (!color_change_already_exists && + wxGetApp().app_config->get("allow_auto_color_change") == "1" && + m_layers_slider->IsNewPrint()) { const Print& print = wxGetApp().plater()->fff_print(); @@ -803,6 +813,7 @@ void Preview::update_layers_slider_mode() for (const auto& range : object->layer_config_ranges) if (range.second.has("extruder") && + range.second.option("extruder")->getInt() != 0 && // extruder isn't default range.second.option("extruder")->getInt() != extruder) return false; } diff --git a/src/slic3r/GUI/GalleryDialog.cpp b/src/slic3r/GUI/GalleryDialog.cpp index 6be6a94fe..8265cb75e 100644 --- a/src/slic3r/GUI/GalleryDialog.cpp +++ b/src/slic3r/GUI/GalleryDialog.cpp @@ -65,7 +65,7 @@ bool GalleryDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& f } -GalleryDialog::GalleryDialog(wxWindow* parent, bool modify_gallery/* = false*/) : +GalleryDialog::GalleryDialog(wxWindow* parent) : DPIDialog(parent, wxID_ANY, _L("Shape Gallery"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { #ifndef _WIN32 @@ -94,12 +94,9 @@ GalleryDialog::GalleryDialog(wxWindow* parent, bool modify_gallery/* = false*/) #endif wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxOK | wxCLOSE); - wxButton* ok_btn = static_cast(FindWindowById(wxID_OK, this)); - ok_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_selected_items.empty()); }); - if (modify_gallery) { - ok_btn->SetLabel(_L("Add to bed")); - ok_btn->SetToolTip(_L("Add selected shape(s) to the bed")); - } + m_ok_btn = static_cast(FindWindowById(wxID_OK, this)); + m_ok_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_selected_items.empty()); }); + static_cast(FindWindowById(wxID_CLOSE, this))->Bind(wxEVT_BUTTON, [this](wxCommandEvent&){ this->EndModal(wxID_CLOSE); }); this->SetEscapeId(wxID_CLOSE); auto add_btn = [this, buttons]( size_t pos, int& ID, wxString title, wxString tooltip, @@ -140,6 +137,14 @@ GalleryDialog::~GalleryDialog() { } +int GalleryDialog::show(bool show_from_menu) +{ + m_ok_btn->SetLabel( show_from_menu ? _L("Add to bed") : _L("OK")); + m_ok_btn->SetToolTip(show_from_menu ? _L("Add selected shape(s) to the bed") : ""); + + return this->ShowModal(); +} + bool GalleryDialog::can_delete() { if (m_selected_items.empty()) @@ -314,7 +319,7 @@ static void generate_thumbnail_from_model(const std::string& filename) fs::path out_path = fs::path(filename); out_path.replace_extension("png"); - image.SaveFile(out_path.string(), wxBITMAP_TYPE_PNG); + image.SaveFile(from_u8(out_path.string()), wxBITMAP_TYPE_PNG); } void GalleryDialog::load_label_icon_list() @@ -361,8 +366,6 @@ void GalleryDialog::load_label_icon_list() m_image_list = new wxImageList(px_cnt, px_cnt); #endif - std::string ext = ".png"; - for (const auto& item : list_items) { fs::path model_path = fs::path((item.is_system ? m_sys_dir_path : m_cust_dir_path) + item.name); std::string model_name = model_path.string(); diff --git a/src/slic3r/GUI/GalleryDialog.hpp b/src/slic3r/GUI/GalleryDialog.hpp index 2aa04ffa2..7d56d1af1 100644 --- a/src/slic3r/GUI/GalleryDialog.hpp +++ b/src/slic3r/GUI/GalleryDialog.hpp @@ -20,6 +20,7 @@ class GalleryDialog : public DPIDialog { wxListCtrl* m_list_ctrl { nullptr }; wxImageList* m_image_list { nullptr }; + wxButton* m_ok_btn { nullptr }; struct Item { std::string name; @@ -48,9 +49,10 @@ class GalleryDialog : public DPIDialog void update(); public: - GalleryDialog(wxWindow* parent, bool modify_gallery = false); + GalleryDialog(wxWindow* parent); ~GalleryDialog(); + int show(bool show_from_menu = false); void get_input_files(wxArrayString& input_files); bool load_files(const wxArrayString& input_files); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp index 0c2fb9c39..f1813c4bd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp @@ -55,7 +55,6 @@ private: #endif // ENABLE_RAYCAST_PICKING bool m_mouse_left_down = false; // for detection left_up of this gizmo const ModelObject* m_old_model_object = nullptr; - std::vector instances_matrices; void update_planes(); bool is_plane_update_necessary() const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp index 5f6cd7a95..418f180de 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp @@ -110,8 +110,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit) window_width = std::max(window_width, cursor_type_radio_left + cursor_type_radio_sphere + cursor_type_radio_circle); auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) { - static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); - m_imgui->text_colored(ORANGE, caption); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, caption); ImGui::SameLine(caption_max); m_imgui->text(text); }; diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 0a716c91c..04b95da46 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -622,6 +622,13 @@ void MainFrame::shutdown() wxGetApp().plater_ = nullptr; } +GalleryDialog* MainFrame::gallery_dialog() +{ + if (!m_gallery_dialog) + m_gallery_dialog = new GalleryDialog(this); + return m_gallery_dialog; +} + void MainFrame::update_title() { wxString title = wxEmptyString; @@ -1413,11 +1420,10 @@ void MainFrame::init_menubar_as_editor() windowMenu->AppendSeparator(); append_menu_item(windowMenu, wxID_ANY, _L("Shape Gallery"), _L("Open the dialog to modify shape gallery"), - [this](wxCommandEvent&) { - GalleryDialog dlg(this, true); - if (dlg.ShowModal() == wxID_OK) { + [this](wxCommandEvent&) { + if (gallery_dialog()->show(true) == wxID_OK) { wxArrayString input_files; - dlg.get_input_files(input_files); + m_gallery_dialog->get_input_files(input_files); if (!input_files.IsEmpty()) m_plater->sidebar().obj_list()->load_shape_object_from_gallery(input_files); } diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index f385ee8f8..893febf4b 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -33,6 +33,7 @@ class PrintHostQueueDialog; class Plater; class MainFrame; class PreferencesDialog; +class GalleryDialog; enum QuickSlice { @@ -146,6 +147,7 @@ public: void shutdown(); Plater* plater() { return m_plater; } + GalleryDialog* gallery_dialog(); void update_title(); @@ -207,6 +209,7 @@ public: PreferencesDialog* preferences_dialog { nullptr }; PrintHostQueueDialog* m_printhost_queue_dlg; // std::shared_ptr m_statusbar; + GalleryDialog* m_gallery_dialog{ nullptr }; #ifdef __APPLE__ std::unique_ptr m_taskbar_icon; diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index 5df41d59a..2924470e0 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -254,6 +254,7 @@ std::vector OpenGLManager::GLInfo::get_extensions_list() const OpenGLManager::GLInfo OpenGLManager::s_gl_info; bool OpenGLManager::s_compressed_textures_supported = false; +bool OpenGLManager::m_use_manually_generated_mipmaps = true; OpenGLManager::EMultisampleState OpenGLManager::s_multisample = OpenGLManager::EMultisampleState::Unknown; OpenGLManager::EFramebufferType OpenGLManager::s_framebuffers_type = OpenGLManager::EFramebufferType::Unknown; @@ -411,6 +412,34 @@ bool OpenGLManager::init_gl() } #endif // ENABLE_OPENGL_DEBUG_OPTION } + +#ifdef _WIN32 + // Since AMD driver version 22.7.1, there is probably some bug in the driver that causes the issue with the missing + // texture of the bed. It seems that this issue only triggers when mipmaps are generated manually + // (combined with a texture compression) and when mipmaps are generated through OpenGL glGenerateMipmap is working. + // So, for newer drivers than 22.6.1, the last working driver version, we use mipmaps generated through OpenGL. + if (const auto gl_info = OpenGLManager::get_gl_info(); boost::contains(gl_info.get_vendor(), "ATI Technologies Inc.")) { + // WHQL drivers seem to have one more version number at the end besides non-WHQL drivers. + // WHQL: 4.6.14800 Compatibility Profile Context 22.6.1 30.0.21023.1015 + // Non-WHQL: 4.6.0 Compatibility Profile Context 22.8.1.220810 + std::regex version_rgx(R"(Compatibility\sProfile\sContext\s(\d+)\.(\d+)\.(\d+))"); + if (std::smatch matches; std::regex_search(gl_info.get_version(), matches, version_rgx) && matches.size() == 4) { + int version_major = std::stoi(matches[1].str()); + int version_minor = std::stoi(matches[2].str()); + int version_patch = std::stoi(matches[3].str()); + BOOST_LOG_TRIVIAL(debug) << "Found AMD driver version: " << version_major << "." << version_minor << "." << version_patch; + + if (version_major > 22 || (version_major == 22 && version_minor > 6) || (version_major == 22 && version_minor == 6 && version_patch > 1)) { + m_use_manually_generated_mipmaps = false; + BOOST_LOG_TRIVIAL(debug) << "Mipmapping through OpenGL was enabled."; + } + } else { + BOOST_LOG_TRIVIAL(error) << "Not recognized format of version."; + } + } else { + BOOST_LOG_TRIVIAL(error) << "Unable to parse version of AMD driver."; + } +#endif } return true; diff --git a/src/slic3r/GUI/OpenGLManager.hpp b/src/slic3r/GUI/OpenGLManager.hpp index d8ca7eebb..5c810d83e 100644 --- a/src/slic3r/GUI/OpenGLManager.hpp +++ b/src/slic3r/GUI/OpenGLManager.hpp @@ -108,6 +108,7 @@ private: static EMultisampleState s_multisample; static EFramebufferType s_framebuffers_type; + static bool m_use_manually_generated_mipmaps; public: OpenGLManager() = default; ~OpenGLManager(); @@ -132,6 +133,7 @@ public: static EFramebufferType get_framebuffers_type() { return s_framebuffers_type; } static wxGLCanvas* create_wxglcanvas(wxWindow& parent); static const GLInfo& get_gl_info() { return s_gl_info; } + static bool use_manually_generated_mipmaps() { return m_use_manually_generated_mipmaps; } private: #if ENABLE_GL_CORE_PROFILE || ENABLE_OPENGL_ES diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 6055a8e78..1855b6a4e 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -597,6 +597,12 @@ void ConfigOptionsGroup::back_to_sys_value(const std::string& opt_key) void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config, const std::string& opt_key) { boost::any value; + if (opt_key == "bed_shape") { + for (const std::string& key : {"bed_custom_texture", "bed_custom_model"}) { + value = config.opt_string(key); + this->change_opt_value(key, value); + } + } if (opt_key == "extruders_count") { auto *nozzle_diameter = dynamic_cast(config.option("nozzle_diameter")); value = int(nozzle_diameter->values.size()); diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index f3efd0680..f74da354d 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -18,6 +18,11 @@ #define wxOSX true #else #define wxOSX false +#endif +#ifdef __WXGTK3__ + #define wxGTK3 true +#else + #define wxGTK3 false #endif #define BORDER(a, b) ((wxOSX ? a : b)) @@ -171,9 +176,8 @@ public: void show_field(const t_config_option_key& opt_key, bool show = true); void hide_field(const t_config_option_key& opt_key) { show_field(opt_key, false); } - void set_name(const wxString& new_name) { - stb->SetLabel(new_name); - } + void set_name(const wxString& new_name) { stb->SetLabel(new_name); } + wxString get_name() const { return stb->GetLabel(); } inline void enable() { for (auto& field : m_fields) field.second->enable(); } inline void disable() { for (auto& field : m_fields) field.second->disable(); } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 36f2f3861..69902571a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -788,7 +788,9 @@ Sidebar::Sidebar(Plater *parent) p->sizer_filaments = new wxBoxSizer(wxVERTICAL); - auto init_combo = [this](PlaterPresetComboBox **combo, wxString label, Preset::Type preset_type, bool filament) { + const int margin_5 = int(0.5 * wxGetApp().em_unit());// 5; + + auto init_combo = [this, margin_5](PlaterPresetComboBox **combo, wxString label, Preset::Type preset_type, bool filament) { auto *text = new wxStaticText(p->presets_panel, wxID_ANY, label + " :"); text->SetFont(wxGetApp().small_font()); *combo = new PlaterPresetComboBox(p->presets_panel, preset_type); @@ -803,9 +805,19 @@ Sidebar::Sidebar(Plater *parent) auto *sizer_filaments = this->p->sizer_filaments; sizer_presets->Add(text, 0, wxALIGN_LEFT | wxEXPAND | wxRIGHT, 4); if (! filament) { - sizer_presets->Add(combo_and_btn_sizer, 0, wxEXPAND | wxBOTTOM, 1); + sizer_presets->Add(combo_and_btn_sizer, 0, wxEXPAND | +#ifdef __WXGTK3__ + wxRIGHT, margin_5); +#else + wxBOTTOM, 1); +#endif // __WXGTK3__ } else { - sizer_filaments->Add(combo_and_btn_sizer, 0, wxEXPAND | wxBOTTOM, 1); + sizer_filaments->Add(combo_and_btn_sizer, 0, wxEXPAND | +#ifdef __WXGTK3__ + wxRIGHT, margin_5); +#else + wxBOTTOM, 1); +#endif // __WXGTK3__ (*combo)->set_extruder_idx(0); sizer_presets->Add(sizer_filaments, 1, wxEXPAND); } @@ -818,13 +830,15 @@ Sidebar::Sidebar(Plater *parent) init_combo(&p->combo_sla_material, _L("SLA material"), Preset::TYPE_SLA_MATERIAL, false); init_combo(&p->combo_printer, _L("Printer"), Preset::TYPE_PRINTER, false); - const int margin_5 = int(0.5*wxGetApp().em_unit());// 5; - p->sizer_params = new wxBoxSizer(wxVERTICAL); // Frequently changed parameters p->frequently_changed_parameters = new FreqChangedParams(p->scrolled); - p->sizer_params->Add(p->frequently_changed_parameters->get_sizer(), 0, wxEXPAND | wxTOP | wxBOTTOM, wxOSX ? 1 : margin_5); + p->sizer_params->Add(p->frequently_changed_parameters->get_sizer(), 0, wxEXPAND | wxTOP | wxBOTTOM +#ifdef __WXGTK3__ + | wxRIGHT +#endif // __WXGTK3__ + , wxOSX ? 1 : margin_5); // Object List p->object_list = new ObjectList(p->scrolled); @@ -852,12 +866,15 @@ Sidebar::Sidebar(Plater *parent) // Sizer in the scrolled area if (p->mode_sizer) scrolled_sizer->Add(p->mode_sizer, 0, wxALIGN_CENTER_HORIZONTAL); + + int size_margin = wxGTK3 ? wxLEFT | wxRIGHT : wxLEFT; + is_msw ? - scrolled_sizer->Add(p->presets_panel, 0, wxEXPAND | wxLEFT, margin_5) : - scrolled_sizer->Add(p->sizer_presets, 0, wxEXPAND | wxLEFT, margin_5); - scrolled_sizer->Add(p->sizer_params, 1, wxEXPAND | wxLEFT, margin_5); - scrolled_sizer->Add(p->object_info, 0, wxEXPAND | wxTOP | wxLEFT, margin_5); - scrolled_sizer->Add(p->sliced_info, 0, wxEXPAND | wxTOP | wxLEFT, margin_5); + scrolled_sizer->Add(p->presets_panel, 0, wxEXPAND | size_margin, margin_5) : + scrolled_sizer->Add(p->sizer_presets, 0, wxEXPAND | size_margin, margin_5); + scrolled_sizer->Add(p->sizer_params, 1, wxEXPAND | size_margin, margin_5); + scrolled_sizer->Add(p->object_info, 0, wxEXPAND | wxTOP | size_margin, margin_5); + scrolled_sizer->Add(p->sliced_info, 0, wxEXPAND | wxTOP | size_margin, margin_5); // Buttons underneath the scrolled area @@ -975,7 +992,12 @@ void Sidebar::init_filament_combo(PlaterPresetComboBox **combo, const int extr_i int(0.3*wxGetApp().em_unit())); auto /***/sizer_filaments = this->p->sizer_filaments; - sizer_filaments->Add(combo_and_btn_sizer, 1, wxEXPAND | wxBOTTOM, 1); + sizer_filaments->Add(combo_and_btn_sizer, 1, wxEXPAND | +#ifdef __WXGTK3__ + wxRIGHT, int(0.5 * wxGetApp().em_unit())); +#else + wxBOTTOM, 1); +#endif // __WXGTK3__ } void Sidebar::remove_unused_filament_combos(const size_t current_extruder_count) @@ -3317,6 +3339,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool sidebar->set_btn_label(ActionButtonType::abExport, _(label_btn_export)); sidebar->set_btn_label(ActionButtonType::abSendGCode, _(label_btn_send)); + dirty_state.update_from_preview(); const wxString slice_string = background_process.running() && wxGetApp().get_mode() == comSimple ? _L("Slicing") + dots : _L("Slice now"); diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 20fcedd05..9e526cc3d 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -459,6 +459,11 @@ void PreferencesDialog::build() L("If enabled, shows non-manifold edges."), app_config->get("non_manifold_edges") == "1"); + append_bool_option(m_optgroup_gui, "allow_auto_color_change", + L("Allow automatically color change"), + L("If enabled, related notification will be shown, when sliced object looks like a logo or a sign."), + app_config->get("allow_auto_color_change") == "1"); + #ifdef _MSW_DARK_MODE append_bool_option(m_optgroup_gui, "tabs_as_menu", L("Set settings tabs as menu items (experimental)"), diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index 89c13b640..d30e5d510 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -7,6 +7,8 @@ #include "I18N.hpp" #include "Plater.hpp" +#include "libslic3r/Model.hpp" + #include #include @@ -38,12 +40,18 @@ void ProjectDirtyStateManager::update_from_presets() app.mainframe->update_title(); } +void ProjectDirtyStateManager::update_from_preview() +{ + m_custom_gcode_per_print_z_dirty = m_initial_custom_gcode_per_print_z != wxGetApp().model().custom_gcode_per_print_z; +} + void ProjectDirtyStateManager::reset_after_save() { this->reset_initial_presets(); m_plater_dirty = false; m_presets_dirty = false; m_project_config_dirty = false; + m_custom_gcode_per_print_z_dirty = false; wxGetApp().mainframe->update_title(); } @@ -54,6 +62,7 @@ void ProjectDirtyStateManager::reset_initial_presets() for (const PresetCollection *preset_collection : app.get_active_preset_collections()) m_initial_presets[preset_collection->type()] = preset_collection->get_selected_preset_name(); m_initial_project_config = app.preset_bundle->project_config; + m_initial_custom_gcode_per_print_z = app.model().custom_gcode_per_print_z; } #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.hpp b/src/slic3r/GUI/ProjectDirtyStateManager.hpp index 6841c89c6..e4e2668fd 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.hpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.hpp @@ -2,6 +2,7 @@ #define slic3r_ProjectDirtyStateManager_hpp_ #include "libslic3r/Preset.hpp" +#include "libslic3r/CustomGCode.hpp" namespace Slic3r { namespace GUI { @@ -11,10 +12,11 @@ class ProjectDirtyStateManager public: void update_from_undo_redo_stack(bool dirty); void update_from_presets(); + void update_from_preview(); void reset_after_save(); void reset_initial_presets(); - bool is_dirty() const { return m_plater_dirty || m_project_config_dirty || m_presets_dirty; } + bool is_dirty() const { return m_plater_dirty || m_project_config_dirty || m_presets_dirty || m_custom_gcode_per_print_z_dirty; } bool is_presets_dirty() const { return m_presets_dirty; } #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW @@ -28,9 +30,12 @@ private: bool m_presets_dirty { false }; // Is the project config dirty? bool m_project_config_dirty { false }; + // Is the custom_gcode_per_print_z dirty? + bool m_custom_gcode_per_print_z_dirty { false }; // Keeps track of preset names selected at the time of last project save. std::array m_initial_presets; DynamicPrintConfig m_initial_project_config; + CustomGCode::Info m_initial_custom_gcode_per_print_z; }; } // namespace GUI diff --git a/src/slic3r/GUI/SavePresetDialog.cpp b/src/slic3r/GUI/SavePresetDialog.cpp index 2a2e3bb10..974d771b9 100644 --- a/src/slic3r/GUI/SavePresetDialog.cpp +++ b/src/slic3r/GUI/SavePresetDialog.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include "libslic3r/PresetBundle.hpp" @@ -54,27 +55,42 @@ SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBox values.push_back(preset.name); } - wxStaticText* label_top = new wxStaticText(m_parent, wxID_ANY, from_u8((boost::format(_utf8(L("Save %s as:"))) % into_u8(tab->title())).str())); + std::string label_str = m_parent->is_for_rename() ?_utf8(L("Rename %s to:")) : _utf8(L("Save %s as:")); + wxStaticText* label_top = new wxStaticText(m_parent, wxID_ANY, from_u8((boost::format(label_str) % into_u8(tab->title())).str())); m_valid_bmp = new wxStaticBitmap(m_parent, wxID_ANY, *get_bmp_bundle("tick_mark")); - m_combo = new wxComboBox(m_parent, wxID_ANY, from_u8(preset_name), wxDefaultPosition, wxSize(35 * wxGetApp().em_unit(), -1)); - for (const std::string& value : values) - m_combo->Append(from_u8(value)); + if (m_parent->is_for_rename()) { +#ifdef _WIN32 + long style = wxBORDER_SIMPLE; +#else + long style = 0L; +#endif + m_text_ctrl = new wxTextCtrl(m_parent, wxID_ANY, from_u8(preset_name), wxDefaultPosition, wxSize(35 * wxGetApp().em_unit(), -1), style); + m_text_ctrl->Bind(wxEVT_TEXT, [this](wxCommandEvent&) { update(); }); + } + else { + m_combo = new wxComboBox(m_parent, wxID_ANY, from_u8(preset_name), wxDefaultPosition, wxSize(35 * wxGetApp().em_unit(), -1)); + for (const std::string& value : values) + m_combo->Append(from_u8(value)); - m_combo->Bind(wxEVT_TEXT, [this](wxCommandEvent&) { update(); }); + m_combo->Bind(wxEVT_TEXT, [this](wxCommandEvent&) { update(); }); #ifdef __WXOSX__ - // Under OSX wxEVT_TEXT wasn't invoked after change selection in combobox, - // So process wxEVT_COMBOBOX too - m_combo->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { update(); }); + // Under OSX wxEVT_TEXT wasn't invoked after change selection in combobox, + // So process wxEVT_COMBOBOX too + m_combo->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { update(); }); #endif //__WXOSX__ + } m_valid_label = new wxStaticText(m_parent, wxID_ANY, ""); m_valid_label->SetFont(wxGetApp().bold_font()); wxBoxSizer* combo_sizer = new wxBoxSizer(wxHORIZONTAL); - combo_sizer->Add(m_valid_bmp, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, BORDER_W); - combo_sizer->Add(m_combo, 1, wxEXPAND, BORDER_W); + combo_sizer->Add(m_valid_bmp, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, BORDER_W); + if (m_parent->is_for_rename()) + combo_sizer->Add(m_text_ctrl,1, wxEXPAND, BORDER_W); + else + combo_sizer->Add(m_combo, 1, wxEXPAND, BORDER_W); sizer->Add(label_top, 0, wxEXPAND | wxTOP| wxBOTTOM, BORDER_W); sizer->Add(combo_sizer, 0, wxEXPAND | wxBOTTOM, BORDER_W); @@ -88,7 +104,8 @@ SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBox void SavePresetDialog::Item::update() { - m_preset_name = into_u8(m_combo->GetValue()); + bool rename = m_parent->is_for_rename(); + m_preset_name = into_u8(rename ? m_text_ctrl->GetValue() : m_combo->GetValue()); m_valid_type = Valid; wxString info_line; @@ -119,23 +136,34 @@ void SavePresetDialog::Item::update() const Preset* existing = m_presets->find_preset(m_preset_name, false); if (m_valid_type == Valid && existing && (existing->is_default || existing->is_system)) { - info_line = _L("Cannot overwrite a system profile."); + info_line = rename ? _L("The supplied name is used for a system profile.") : + _L("Cannot overwrite a system profile."); m_valid_type = NoValid; } if (m_valid_type == Valid && existing && (existing->is_external)) { - info_line = _L("Cannot overwrite an external profile."); + info_line = rename ? _L("The supplied name is used for a external profile.") : + _L("Cannot overwrite an external profile."); m_valid_type = NoValid; } - if (m_valid_type == Valid && existing && m_preset_name != m_presets->get_selected_preset_name()) + if (m_valid_type == Valid && existing) { - if (existing->is_compatible) - info_line = from_u8((boost::format(_u8L("Preset with name \"%1%\" already exists.")) % m_preset_name).str()); - else - info_line = from_u8((boost::format(_u8L("Preset with name \"%1%\" already exists and is incompatible with selected printer.")) % m_preset_name).str()); - info_line += "\n" + _L("Note: This preset will be replaced after saving"); - m_valid_type = Warning; + if (m_preset_name == m_presets->get_selected_preset_name()) { + if (!rename && m_presets->get_edited_preset().is_dirty) + info_line = _L("Just save preset modifications"); + else + info_line = _L("Nothing changed"); + m_valid_type = Valid; + } + else { + if (existing->is_compatible) + info_line = from_u8((boost::format(_u8L("Preset with name \"%1%\" already exists.")) % m_preset_name).str()); + else + info_line = from_u8((boost::format(_u8L("Preset with name \"%1%\" already exists and is incompatible with selected printer.")) % m_preset_name).str()); + info_line += "\n" + _L("Note: This preset will be replaced after saving"); + m_valid_type = Warning; + } } if (m_valid_type == Valid && m_preset_name.empty()) { @@ -158,6 +186,9 @@ void SavePresetDialog::Item::update() m_valid_type = NoValid; } + if (!m_parent->get_info_line_extention().IsEmpty() && m_valid_type != NoValid) + info_line += "\n\n" + m_parent->get_info_line_extention(); + m_valid_label->SetLabel(info_line); m_valid_label->Show(!info_line.IsEmpty()); @@ -194,11 +225,18 @@ SavePresetDialog::SavePresetDialog(wxWindow* parent, Preset::Type type, std::str } SavePresetDialog::SavePresetDialog(wxWindow* parent, std::vector types, std::string suffix) - : DPIDialog(parent, wxID_ANY, _L("Save preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER) + : DPIDialog(parent, wxID_ANY, _L("Save presets"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER) { build(types, suffix); } +SavePresetDialog::SavePresetDialog(wxWindow* parent, Preset::Type type, bool rename, const wxString& info_line_extention) + : DPIDialog(parent, wxID_ANY, _L("Rename preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER), + m_use_for_rename(rename), + m_info_line_extention(info_line_extention) +{ + build(std::vector{type}); +} SavePresetDialog::~SavePresetDialog() { for (auto item : m_items) { diff --git a/src/slic3r/GUI/SavePresetDialog.hpp b/src/slic3r/GUI/SavePresetDialog.hpp index cc1ea1f24..1ecda7b7f 100644 --- a/src/slic3r/GUI/SavePresetDialog.hpp +++ b/src/slic3r/GUI/SavePresetDialog.hpp @@ -10,6 +10,7 @@ class wxString; class wxStaticText; class wxComboBox; +class wxTextCtrl; class wxStaticBitmap; namespace Slic3r { @@ -52,6 +53,7 @@ class SavePresetDialog : public DPIDialog SavePresetDialog* m_parent {nullptr}; wxStaticBitmap* m_valid_bmp {nullptr}; wxComboBox* m_combo {nullptr}; + wxTextCtrl* m_text_ctrl {nullptr}; wxStaticText* m_valid_label {nullptr}; PresetCollection* m_presets {nullptr}; @@ -68,11 +70,16 @@ class SavePresetDialog : public DPIDialog std::string m_ph_printer_name; std::string m_old_preset_name; + bool m_use_for_rename{false}; + wxString m_info_line_extention{wxEmptyString}; public: + const wxString& get_info_line_extention() { return m_info_line_extention; } + SavePresetDialog(wxWindow* parent, Preset::Type type, std::string suffix = ""); SavePresetDialog(wxWindow* parent, std::vector types, std::string suffix = ""); + SavePresetDialog(wxWindow* parent, Preset::Type type, bool rename, const wxString& info_line_extention); ~SavePresetDialog(); void AddItem(Preset::Type type, const std::string& suffix); @@ -84,6 +91,7 @@ public: void add_info_for_edit_ph_printer(wxBoxSizer *sizer); void update_info_for_edit_ph_printer(const std::string &preset_name); void layout(); + bool is_for_rename() { return m_use_for_rename; } protected: void on_dpi_changed(const wxRect& suggested_rect) override; diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 0b2a13040..a2d85bf51 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -2981,7 +2981,6 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_ const Geometry::Transformation& curr_inst_trafo_i = volume_i->get_instance_transformation(); const Vec3d curr_inst_rotation_i = curr_inst_trafo_i.get_rotation(); const Vec3d& curr_inst_scaling_factor_i = curr_inst_trafo_i.get_scaling_factor(); - const Vec3d& curr_inst_mirror_i = curr_inst_trafo_i.get_mirror(); const Vec3d old_inst_rotation_i = m_cache.volumes_data[i].get_instance_transform().get_rotation(); #else const Vec3d& rotation = volume_i->get_instance_rotation(); @@ -3054,7 +3053,7 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_ #if ENABLE_WORLD_COORDINATE volume_j->set_instance_transformation(Geometry::assemble_transform(new_inst_offset_j, new_inst_rotation_j, - curr_inst_scaling_factor_i, curr_inst_mirror_i)); + curr_inst_scaling_factor_i)); #else volume_j->set_instance_scaling_factor(scaling_factor); volume_j->set_instance_mirror(mirror); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index d422745a0..8c6b349b4 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -29,6 +29,7 @@ #include #include +#include #include "wxExtensions.hpp" #include "PresetComboBoxes.hpp" #include @@ -150,6 +151,7 @@ void Tab::create_preset_tab() add_scaled_button(panel, &m_btn_compare_preset, "compare"); add_scaled_button(panel, &m_btn_save_preset, "save"); + add_scaled_button(panel, &m_btn_rename_preset, "edit"); add_scaled_button(panel, &m_btn_delete_preset, "cross"); if (m_type == Preset::Type::TYPE_PRINTER) add_scaled_button(panel, &m_btn_edit_ph_printer, "cog"); @@ -161,6 +163,8 @@ void Tab::create_preset_tab() m_btn_compare_preset->SetToolTip(_L("Compare this preset with some another")); // TRN "Save current Settings" m_btn_save_preset->SetToolTip(from_u8((boost::format(_utf8(L("Save current %s"))) % m_title).str())); + m_btn_rename_preset->SetToolTip(from_u8((boost::format(_utf8(L("Rename current %s"))) % m_title).str())); + m_btn_rename_preset->Hide(); m_btn_delete_preset->SetToolTip(_(L("Delete this preset"))); m_btn_delete_preset->Hide(); @@ -211,6 +215,8 @@ void Tab::create_preset_tab() m_hsizer->Add(m_presets_choice, 0, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3); m_hsizer->AddSpacer(int(4*scale_factor)); m_hsizer->Add(m_btn_save_preset, 0, wxALIGN_CENTER_VERTICAL); + m_hsizer->AddSpacer(int(4*scale_factor)); + m_hsizer->Add(m_btn_rename_preset, 0, wxALIGN_CENTER_VERTICAL); m_hsizer->AddSpacer(int(4 * scale_factor)); m_hsizer->Add(m_btn_delete_preset, 0, wxALIGN_CENTER_VERTICAL); if (m_btn_edit_ph_printer) { @@ -300,6 +306,7 @@ void Tab::create_preset_tab() m_btn_compare_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { compare_preset(); })); m_btn_save_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { save_preset(); })); + m_btn_rename_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { rename_preset(); })); m_btn_delete_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { delete_preset(); })); m_btn_hide_incompatible_presets->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { toggle_show_hide_incompatible(); @@ -593,12 +600,26 @@ void Tab::update_changed_ui() const bool deep_compare = (m_type == Slic3r::Preset::TYPE_PRINTER || m_type == Slic3r::Preset::TYPE_SLA_MATERIAL); auto dirty_options = m_presets->current_dirty_options(deep_compare); auto nonsys_options = m_presets->current_different_from_parent_options(deep_compare); - if (m_type == Preset::TYPE_PRINTER && static_cast(this)->m_printer_technology == ptFFF) { - TabPrinter* tab = static_cast(this); - if (tab->m_initial_extruders_count != tab->m_extruders_count) - dirty_options.emplace_back("extruders_count"); - if (tab->m_sys_extruders_count != tab->m_extruders_count) - nonsys_options.emplace_back("extruders_count"); + if (m_type == Preset::TYPE_PRINTER) { + { + auto check_bed_custom_options = [](std::vector& keys) { + size_t old_keys_size = keys.size(); + keys.erase(std::remove_if(keys.begin(), keys.end(), [](const std::string& key) { + return key == "bed_custom_texture" || key == "bed_custom_model"; }), keys.end()); + if (old_keys_size != keys.size() && std::find(keys.begin(), keys.end(), "bed_shape") == keys.end()) + keys.emplace_back("bed_shape"); + }; + check_bed_custom_options(dirty_options); + check_bed_custom_options(nonsys_options); + } + + if (static_cast(this)->m_printer_technology == ptFFF) { + TabPrinter* tab = static_cast(this); + if (tab->m_initial_extruders_count != tab->m_extruders_count) + dirty_options.emplace_back("extruders_count"); + if (tab->m_sys_extruders_count != tab->m_extruders_count) + nonsys_options.emplace_back("extruders_count"); + } } for (auto& it : m_options_list) @@ -816,19 +837,11 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/) if (m_type != Preset::TYPE_PRINTER && (m_options_list["compatible_printers"] & os) == 0) { to_sys ? group->back_to_sys_value("compatible_printers") : group->back_to_initial_value("compatible_printers"); load_key_value("compatible_printers", true/*some value*/, true); - - bool is_empty = m_config->option("compatible_printers")->values.empty(); - m_compatible_printers.checkbox->SetValue(is_empty); - is_empty ? m_compatible_printers.btn->Disable() : m_compatible_printers.btn->Enable(); } // "compatible_prints" option exists only in Filament Settimgs and Materials Tabs if ((m_type == Preset::TYPE_FILAMENT || m_type == Preset::TYPE_SLA_MATERIAL) && (m_options_list["compatible_prints"] & os) == 0) { to_sys ? group->back_to_sys_value("compatible_prints") : group->back_to_initial_value("compatible_prints"); load_key_value("compatible_prints", true/*some value*/, true); - - bool is_empty = m_config->option("compatible_prints")->values.empty(); - m_compatible_prints.checkbox->SetValue(is_empty); - is_empty ? m_compatible_prints.btn->Disable() : m_compatible_prints.btn->Enable(); } } for (const auto &kvp : group->opt_map()) { @@ -1055,6 +1068,11 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) return; } + if (opt_key == "compatible_prints") + this->compatible_widget_reload(m_compatible_prints); + if (opt_key == "compatible_printers") + this->compatible_widget_reload(m_compatible_printers); + const bool is_fff = supports_printer_technology(ptFFF); ConfigOptionsGroup* og_freq_chng_params = wxGetApp().sidebar().og_freq_chng_params(is_fff); if (opt_key == "fill_density" || opt_key == "pad_enable") @@ -1671,13 +1689,6 @@ void TabPrint::build() build_preset_description_line(optgroup.get()); } -// Reload current config (aka presets->edited_preset->config) into the UI fields. -void TabPrint::reload_config() -{ - this->compatible_widget_reload(m_compatible_printers); - Tab::reload_config(); -} - void TabPrint::update_description_lines() { Tab::update_description_lines(); @@ -2066,14 +2077,6 @@ void TabFilament::build() build_preset_description_line(optgroup.get()); } -// Reload current config (aka presets->edited_preset->config) into the UI fields. -void TabFilament::reload_config() -{ - this->compatible_widget_reload(m_compatible_printers); - this->compatible_widget_reload(m_compatible_prints); - Tab::reload_config(); -} - void TabFilament::update_volumetric_flow_preset_hints() { wxString text; @@ -3181,6 +3184,7 @@ void Tab::update_btns_enabling() const Preset& preset = m_presets->get_edited_preset(); m_btn_delete_preset->Show((m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection()) || (!preset.is_default && !preset.is_system)); + m_btn_rename_preset->Show(!preset.is_default && !preset.is_system && !m_presets_choice->is_selected_physical_printer()); if (m_btn_edit_ph_printer) m_btn_edit_ph_printer->SetToolTip( m_preset_bundle->physical_printers.has_selection() ? @@ -3460,6 +3464,14 @@ void Tab::activate_selected_page(std::function throw_if_canceled) return; m_active_page->activate(m_mode, throw_if_canceled); + + if (m_active_page->title() == "Dependencies") { + if (m_compatible_printers.checkbox) + this->compatible_widget_reload(m_compatible_printers); + if (m_compatible_prints.checkbox) + this->compatible_widget_reload(m_compatible_prints); + } + update_changed_ui(); update_description_lines(); toggle_options(); @@ -3605,8 +3617,9 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach) update_tab_ui(); // Update the selection boxes at the plater. on_presets_changed(); - // If current profile is saved, "delete preset" button have to be enabled + // If current profile is saved, "delete/rename preset" buttons have to be shown m_btn_delete_preset->Show(); + m_btn_rename_preset->Show(!m_presets_choice->is_selected_physical_printer()); m_btn_delete_preset->GetParent()->Layout(); if (m_type == Preset::TYPE_PRINTER) @@ -3655,6 +3668,60 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach) update_description_lines(); } +void Tab::rename_preset() +{ + if (m_presets_choice->is_selected_physical_printer()) + return; + + Preset& selected_preset = m_presets->get_selected_preset(); + wxString msg; + + if (m_type == Preset::TYPE_PRINTER && !m_preset_bundle->physical_printers.empty()) { + // Check preset for rename in physical printers + std::vector ph_printers = m_preset_bundle->physical_printers.get_printers_with_preset(selected_preset.name); + if (!ph_printers.empty()) { + msg += _L_PLURAL("The physical printer below is based on the preset, you are going to rename.", + "The physical printers below are based on the preset, you are going to rename.", ph_printers.size()); + for (const std::string& printer : ph_printers) + msg += "\n \"" + from_u8(printer) + "\","; + msg.RemoveLast(); + msg += "\n" + _L_PLURAL("Note, that the selected preset will be renamed in this printer too.", + "Note, that the selected preset will be renamed in these printers too.", ph_printers.size()) + "\n\n"; + } + } + + // get new name + + SavePresetDialog dlg(m_parent, m_type, true, msg); + if (dlg.ShowModal() != wxID_OK) + return; + std::string new_name = into_u8(dlg.get_name()); + + if (new_name.empty() || new_name == m_presets->get_selected_preset().name) + return; + + // rename selected and edited presets + + std::string old_name = selected_preset.name; + std::string old_file_name = selected_preset.file; + + selected_preset.name = new_name; + boost::replace_last(selected_preset.file, old_name, new_name); + + Preset& edited_preset = m_presets->get_edited_preset(); + edited_preset.name = new_name; + boost::replace_last(edited_preset.file, old_name, new_name); + + // rename file with renamed preset configuration + boost::filesystem::rename(old_file_name, selected_preset.file); + + // rename selected preset in printers, if it's needed + if (!msg.IsEmpty()) + m_preset_bundle->physical_printers.rename_preset_in_printers(old_name, new_name); + + m_presets_choice->update(); +} + // Called for a currently selected preset. void Tab::delete_preset() { @@ -3682,7 +3749,7 @@ void Tab::delete_preset() { // Check preset for delete in physical printers // Ask a customer about next action, if there is a printer with just one preset and this preset is equal to delete - std::vector ph_printers = physical_printers.get_printers_with_preset(current_preset.name); + std::vector ph_printers = physical_printers.get_printers_with_preset(current_preset.name, false); std::vector ph_printers_only = physical_printers.get_printers_with_only_preset(current_preset.name); if (!ph_printers.empty()) { @@ -4589,14 +4656,6 @@ void TabSLAMaterial::build() optgroup->append_single_option_line(option); } -// Reload current config (aka presets->edited_preset->config) into the UI fields. -void TabSLAMaterial::reload_config() -{ - this->compatible_widget_reload(m_compatible_printers); - this->compatible_widget_reload(m_compatible_prints); - Tab::reload_config(); -} - void TabSLAMaterial::toggle_options() { const Preset ¤t_printer = wxGetApp().preset_bundle->printers.get_edited_preset(); @@ -4723,13 +4782,6 @@ void TabSLAPrint::build() build_preset_description_line(optgroup.get()); } -// Reload current config (aka presets->edited_preset->config) into the UI fields. -void TabSLAPrint::reload_config() -{ - this->compatible_widget_reload(m_compatible_printers); - Tab::reload_config(); -} - void TabSLAPrint::update_description_lines() { Tab::update_description_lines(); diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 6625f71f6..8ed11bf9d 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -168,6 +168,7 @@ protected: ScalableButton* m_search_btn; ScalableButton* m_btn_compare_preset; ScalableButton* m_btn_save_preset; + ScalableButton* m_btn_rename_preset; ScalableButton* m_btn_delete_preset; ScalableButton* m_btn_edit_ph_printer {nullptr}; ScalableButton* m_btn_hide_incompatible_presets; @@ -322,6 +323,7 @@ public: void compare_preset(); void save_preset(std::string name = std::string(), bool detach = false); + void rename_preset(); void delete_preset(); void toggle_show_hide_incompatible(); void update_show_hide_incompatible_button(); @@ -408,7 +410,6 @@ public: ~TabPrint() {} void build() override; - void reload_config() override; void update_description_lines() override; void toggle_options() override; void update() override; @@ -442,7 +443,6 @@ public: ~TabFilament() {} void build() override; - void reload_config() override; void update_description_lines() override; void toggle_options() override; void update() override; @@ -515,7 +515,6 @@ public: ~TabSLAMaterial() {} void build() override; - void reload_config() override; void toggle_options() override; void update() override; void init_options_list() override; @@ -532,7 +531,6 @@ public: ogStaticText* m_support_object_elevation_description_line = nullptr; void build() override; - void reload_config() override; void update_description_lines() override; void toggle_options() override; void update() override; diff --git a/src/slic3r/Utils/Profile.hpp b/src/slic3r/Utils/Profile.hpp deleted file mode 100644 index 5fb1e3116..000000000 --- a/src/slic3r/Utils/Profile.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef slic3r_GUI_Profile_hpp_ -#define slic3r_GUI_Profile_hpp_ - -// Profiling support using the Shiny intrusive profiler -//#define SLIC3R_PROFILE_GUI -#if defined(SLIC3R_PROFILE) && defined(SLIC3R_PROFILE_GUI) - #include - #define SLIC3R_GUI_PROFILE_FUNC() PROFILE_FUNC() - #define SLIC3R_GUI_PROFILE_BLOCK(name) PROFILE_BLOCK(name) - #define SLIC3R_GUI_PROFILE_UPDATE() PROFILE_UPDATE() - #define SLIC3R_GUI_PROFILE_OUTPUT(x) PROFILE_OUTPUT(x) -#else - #define SLIC3R_GUI_PROFILE_FUNC() - #define SLIC3R_GUI_PROFILE_BLOCK(name) - #define SLIC3R_GUI_PROFILE_UPDATE() - #define SLIC3R_GUI_PROFILE_OUTPUT(x) -#endif - -#endif // slic3r_GUI_Profile_hpp_ diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index 770e990a5..a78720807 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -22,6 +22,7 @@ add_executable(${_TEST_NAME}_tests test_meshboolean.cpp test_marchingsquares.cpp test_timeutils.cpp + test_utils.cpp test_voronoi.cpp test_optimizers.cpp test_png_io.cpp diff --git a/tests/libslic3r/test_clipper_utils.cpp b/tests/libslic3r/test_clipper_utils.cpp index 048ebba10..a1230ac03 100644 --- a/tests/libslic3r/test_clipper_utils.cpp +++ b/tests/libslic3r/test_clipper_utils.cpp @@ -294,7 +294,7 @@ SCENARIO("Various Clipper operations - t/clipper.t", "[ClipperUtils]") { WHEN("clipping a line") { auto line = Polyline::new_scale({ { 152.742,288.086671142818 }, { 152.742,34.166466971035 } }); - Polylines intersection = intersection_pl(line, Polygons{ circle_with_hole }); + Polylines intersection = intersection_pl(line, to_polygons(circle_with_hole)); THEN("clipped to two pieces") { REQUIRE(intersection.front().length() == Approx((Vec2d(152742000, 215178843) - Vec2d(152742000, 288086661)).norm())); REQUIRE(intersection[1].length() == Approx((Vec2d(152742000, 35166477) - Vec2d(152742000, 108087507)).norm())); diff --git a/tests/libslic3r/test_utils.cpp b/tests/libslic3r/test_utils.cpp new file mode 100644 index 000000000..74d409496 --- /dev/null +++ b/tests/libslic3r/test_utils.cpp @@ -0,0 +1,35 @@ +#include + +#include "libslic3r/libslic3r.h" + +SCENARIO("Test fast_round_up()") { + using namespace Slic3r; + + THEN("fast_round_up(1.5) is 2") { + REQUIRE(fast_round_up(1.5) == 2); + } + THEN("fast_round_up(1.499999999999999) is 1") { + REQUIRE(fast_round_up(1.499999999999999) == 1); + } + THEN("fast_round_up(0.5) is 1") { + REQUIRE(fast_round_up(0.5) == 1); + } + THEN("fast_round_up(0.49999999999999994) is 0") { + REQUIRE(fast_round_up(0.49999999999999994) == 0); + } + THEN("fast_round_up(-0.5) is 0") { + REQUIRE(fast_round_up(-0.5) == 0); + } + THEN("fast_round_up(-0.51) is -1") { + REQUIRE(fast_round_up(-0.51) == -1); + } + THEN("fast_round_up(-0.51) is -1") { + REQUIRE(fast_round_up(-0.51) == -1); + } + THEN("fast_round_up(-1.5) is -1") { + REQUIRE(fast_round_up(-1.5) == -1); + } + THEN("fast_round_up(-1.51) is -2") { + REQUIRE(fast_round_up(-1.51) == -2); + } +}