diff --git a/CMakeLists.txt b/CMakeLists.txt index 62c545fe1..fbad4ca23 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ option(SLIC3R_GUI "Compile Slic3r with GUI components (OpenGL, wxWidgets)" option(SLIC3R_PROFILE "Compile Slic3r with an invasive Shiny profiler" 0) 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) +option(SLIC3R_PERL_XS "Compile XS Perl module and enable Perl unit and integration tests" 0) if (MSVC AND SLIC3R_MSVC_COMPILE_PARALLEL) add_compile_options(/MP) @@ -154,6 +155,7 @@ endif() # The Intel TBB library will use the std::exception_ptr feature of C++11. add_definitions(-DTBB_USE_CAPTURED_EXCEPTION=0) +#set(CURL_DEBUG 1) find_package(CURL REQUIRED) include_directories(${CURL_INCLUDE_DIRS}) @@ -233,7 +235,9 @@ set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT s # Perl bindings, currently only used for the unit / integration tests of libslic3r. # Also runs the unit / integration tests. #FIXME Port the tests into C++ to finally get rid of the Perl! -add_subdirectory(xs) +if (SLIC3R_PERL_XS) + add_subdirectory(xs) +endif () file(GLOB MyVar var/*.png) install(FILES ${MyVar} DESTINATION share/slic3r-prusa3d) diff --git a/cmake/modules/FindCURL.cmake b/cmake/modules/FindCURL.cmake index b8724858c..e0deafa45 100644 --- a/cmake/modules/FindCURL.cmake +++ b/cmake/modules/FindCURL.cmake @@ -5,34 +5,62 @@ # FindCURL # -------- # -# Find curl -# # Find the native CURL headers and libraries. # -# :: +# IMPORTED Targets +# ^^^^^^^^^^^^^^^^ # -# CURL_INCLUDE_DIRS - where to find curl/curl.h, etc. -# CURL_LIBRARIES - List of libraries when using curl. -# CURL_FOUND - True if curl found. -# CURL_VERSION_STRING - the version of curl found (since CMake 2.8.8) +# This module defines :prop_tgt:`IMPORTED` target ``CURL::libcurl``, if +# curl has been found. +# +# Result Variables +# ^^^^^^^^^^^^^^^^ +# +# This module defines the following variables: +# +# ``CURL_FOUND`` +# True if curl found. +# +# ``CURL_INCLUDE_DIRS`` +# where to find curl/curl.h, etc. +# +# ``CURL_LIBRARIES`` +# List of libraries when using curl. +# +# ``CURL_VERSION_STRING`` +# The version of curl found. # Look for the header file. find_path(CURL_INCLUDE_DIR NAMES curl/curl.h) mark_as_advanced(CURL_INCLUDE_DIR) -# Look for the library (sorted from most current/relevant entry to least). -find_library(CURL_LIBRARY NAMES - curl - # Windows MSVC Makefile: - libcurl_a - # Windows MSVC prebuilts: - curllib - libcurl_imp - curllib_static - # Windows older "Win32 - MSVC" prebuilts (libcurl.lib, e.g. libcurl-7.15.5-win32-msvc.zip): - libcurl -) -mark_as_advanced(CURL_LIBRARY) +if(NOT CURL_LIBRARY) + # Look for the library (sorted from most current/relevant entry to least). + find_library(CURL_LIBRARY_RELEASE NAMES + curl + # Windows MSVC prebuilts: + curllib + libcurl_imp + curllib_static + # Windows older "Win32 - MSVC" prebuilts (libcurl.lib, e.g. libcurl-7.15.5-win32-msvc.zip): + libcurl + # Static library on Windows + libcurl_a + ) + mark_as_advanced(CURL_LIBRARY_RELEASE) + + find_library(CURL_LIBRARY_DEBUG NAMES + # Windows MSVC CMake builds in debug configuration on vcpkg: + libcurl-d_imp + libcurl-d + # Static library on Windows, compiled in debug mode + libcurl_a_debug + ) + mark_as_advanced(CURL_LIBRARY_DEBUG) + + include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations_SLIC3R.cmake) + select_library_configurations_SLIC3R(CURL) +endif() if(CURL_INCLUDE_DIR) foreach(_curl_version_header curlver.h curl.h) @@ -46,7 +74,8 @@ if(CURL_INCLUDE_DIR) endforeach() endif() -find_package_handle_standard_args(CURL +include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs_SLIC3R.cmake) +FIND_PACKAGE_HANDLE_STANDARD_ARGS_SLIC3R(CURL REQUIRED_VARS CURL_LIBRARY CURL_INCLUDE_DIR VERSION_VAR CURL_VERSION_STRING) @@ -54,6 +83,29 @@ if(CURL_FOUND) set(CURL_LIBRARIES ${CURL_LIBRARY}) set(CURL_INCLUDE_DIRS ${CURL_INCLUDE_DIR}) - message(STATUS " Curl libraries: = ${CURL_LIBRARIES}") - message(STATUS " Curl include dirs: = ${CURL_INCLUDE_DIRS}") + if(NOT TARGET CURL::libcurl) + add_library(CURL::libcurl UNKNOWN IMPORTED) + set_target_properties(CURL::libcurl PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${CURL_INCLUDE_DIRS}") + + if(EXISTS "${CURL_LIBRARY}") + set_target_properties(CURL::libcurl PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${CURL_LIBRARY}") + endif() + if(CURL_LIBRARY_RELEASE) + set_property(TARGET CURL::libcurl APPEND PROPERTY + IMPORTED_CONFIGURATIONS RELEASE) + set_target_properties(CURL::libcurl PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION_RELEASE "${CURL_LIBRARY_RELEASE}") + endif() + if(CURL_LIBRARY_DEBUG) + set_property(TARGET CURL::libcurl APPEND PROPERTY + IMPORTED_CONFIGURATIONS DEBUG) + set_target_properties(CURL::libcurl PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION_DEBUG "${CURL_LIBRARY_DEBUG}") + endif() + endif() endif() diff --git a/cmake/modules/FindPackageHandleStandardArgs_SLIC3R.cmake b/cmake/modules/FindPackageHandleStandardArgs_SLIC3R.cmake new file mode 100644 index 000000000..eddfd001e --- /dev/null +++ b/cmake/modules/FindPackageHandleStandardArgs_SLIC3R.cmake @@ -0,0 +1,392 @@ +# Modified from the CMake github master, +# required by the bundled FindCURL.cmake + + + + +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindPackageHandleStandardArgs +----------------------------- + +This module provides a function intended to be used in :ref:`Find Modules` +implementing :command:`find_package()` calls. It handles the +``REQUIRED``, ``QUIET`` and version-related arguments of ``find_package``. +It also sets the ``_FOUND`` variable. The package is +considered found if all variables listed contain valid results, e.g. +valid filepaths. + +.. command:: find_package_handle_standard_args + + There are two signatures:: + + find_package_handle_standard_args( + (DEFAULT_MSG|) + ... + ) + + find_package_handle_standard_args( + [FOUND_VAR ] + [REQUIRED_VARS ...] + [VERSION_VAR ] + [HANDLE_COMPONENTS] + [CONFIG_MODE] + [FAIL_MESSAGE ] + ) + + The ``_FOUND`` variable will be set to ``TRUE`` if all + the variables ``...`` are valid and any optional + constraints are satisfied, and ``FALSE`` otherwise. A success or + failure message may be displayed based on the results and on + whether the ``REQUIRED`` and/or ``QUIET`` option was given to + the :command:`find_package` call. + + The options are: + + ``(DEFAULT_MSG|)`` + In the simple signature this specifies the failure message. + Use ``DEFAULT_MSG`` to ask for a default message to be computed + (recommended). Not valid in the full signature. + + ``FOUND_VAR `` + Obsolete. Specifies either ``_FOUND`` or + ``_FOUND`` as the result variable. This exists only + for compatibility with older versions of CMake and is now ignored. + Result variables of both names are always set for compatibility. + + ``REQUIRED_VARS ...`` + Specify the variables which are required for this package. + These may be named in the generated failure message asking the + user to set the missing variable values. Therefore these should + typically be cache entries such as ``FOO_LIBRARY`` and not output + variables like ``FOO_LIBRARIES``. + + ``VERSION_VAR `` + Specify the name of a variable that holds the version of the package + that has been found. This version will be checked against the + (potentially) specified required version given to the + :command:`find_package` call, including its ``EXACT`` option. + The default messages include information about the required + version and the version which has been actually found, both + if the version is ok or not. + + ``HANDLE_COMPONENTS`` + Enable handling of package components. In this case, the command + will report which components have been found and which are missing, + and the ``_FOUND`` variable will be set to ``FALSE`` + if any of the required components (i.e. not the ones listed after + the ``OPTIONAL_COMPONENTS`` option of :command:`find_package`) are + missing. + + ``CONFIG_MODE`` + Specify that the calling find module is a wrapper around a + call to ``find_package( NO_MODULE)``. This implies + a ``VERSION_VAR`` value of ``_VERSION``. The command + will automatically check whether the package configuration file + was found. + + ``FAIL_MESSAGE `` + Specify a custom failure message instead of using the default + generated message. Not recommended. + +Example for the simple signature: + +.. code-block:: cmake + + find_package_handle_standard_args(LibXml2 DEFAULT_MSG + LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR) + +The ``LibXml2`` package is considered to be found if both +``LIBXML2_LIBRARY`` and ``LIBXML2_INCLUDE_DIR`` are valid. +Then also ``LibXml2_FOUND`` is set to ``TRUE``. If it is not found +and ``REQUIRED`` was used, it fails with a +:command:`message(FATAL_ERROR)`, independent whether ``QUIET`` was +used or not. If it is found, success will be reported, including +the content of the first ````. On repeated CMake runs, +the same message will not be printed again. + +Example for the full signature: + +.. code-block:: cmake + + find_package_handle_standard_args(LibArchive + REQUIRED_VARS LibArchive_LIBRARY LibArchive_INCLUDE_DIR + VERSION_VAR LibArchive_VERSION) + +In this case, the ``LibArchive`` package is considered to be found if +both ``LibArchive_LIBRARY`` and ``LibArchive_INCLUDE_DIR`` are valid. +Also the version of ``LibArchive`` will be checked by using the version +contained in ``LibArchive_VERSION``. Since no ``FAIL_MESSAGE`` is given, +the default messages will be printed. + +Another example for the full signature: + +.. code-block:: cmake + + find_package(Automoc4 QUIET NO_MODULE HINTS /opt/automoc4) + find_package_handle_standard_args(Automoc4 CONFIG_MODE) + +In this case, a ``FindAutmoc4.cmake`` module wraps a call to +``find_package(Automoc4 NO_MODULE)`` and adds an additional search +directory for ``automoc4``. Then the call to +``find_package_handle_standard_args`` produces a proper success/failure +message. +#]=======================================================================] + +include(${CMAKE_CURRENT_LIST_DIR}/FindPackageMessage_SLIC3R.cmake) + +# internal helper macro +macro(_FPHSA_FAILURE_MESSAGE _msg) + if (${_NAME}_FIND_REQUIRED) + message(FATAL_ERROR "${_msg}") + else () + if (NOT ${_NAME}_FIND_QUIETLY) + message(STATUS "${_msg}") + endif () + endif () +endmacro() + + +# internal helper macro to generate the failure message when used in CONFIG_MODE: +macro(_FPHSA_HANDLE_FAILURE_CONFIG_MODE) + # _CONFIG is set, but FOUND is false, this means that some other of the REQUIRED_VARS was not found: + if(${_NAME}_CONFIG) + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: missing:${MISSING_VARS} (found ${${_NAME}_CONFIG} ${VERSION_MSG})") + else() + # If _CONSIDERED_CONFIGS is set, the config-file has been found, but no suitable version. + # List them all in the error message: + if(${_NAME}_CONSIDERED_CONFIGS) + set(configsText "") + list(LENGTH ${_NAME}_CONSIDERED_CONFIGS configsCount) + math(EXPR configsCount "${configsCount} - 1") + foreach(currentConfigIndex RANGE ${configsCount}) + list(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename) + list(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version) + string(APPEND configsText " ${filename} (version ${version})\n") + endforeach() + if (${_NAME}_NOT_FOUND_MESSAGE) + string(APPEND configsText " Reason given by package: ${${_NAME}_NOT_FOUND_MESSAGE}\n") + endif() + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:\n${configsText}") + + else() + # Simple case: No Config-file was found at all: + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: found neither ${_NAME}Config.cmake nor ${_NAME_LOWER}-config.cmake ${VERSION_MSG}") + endif() + endif() +endmacro() + + +function(FIND_PACKAGE_HANDLE_STANDARD_ARGS_SLIC3R _NAME _FIRST_ARG) + +# Set up the arguments for `cmake_parse_arguments`. + set(options CONFIG_MODE HANDLE_COMPONENTS) + set(oneValueArgs FAIL_MESSAGE VERSION_VAR FOUND_VAR) + set(multiValueArgs REQUIRED_VARS) + +# Check whether we are in 'simple' or 'extended' mode: + set(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} ) + list(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX) + + if(${INDEX} EQUAL -1) + set(FPHSA_FAIL_MESSAGE ${_FIRST_ARG}) + set(FPHSA_REQUIRED_VARS ${ARGN}) + set(FPHSA_VERSION_VAR) + else() + cmake_parse_arguments(FPHSA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${_FIRST_ARG} ${ARGN}) + + if(FPHSA_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${FPHSA_UNPARSED_ARGUMENTS}\"") + endif() + + if(NOT FPHSA_FAIL_MESSAGE) + set(FPHSA_FAIL_MESSAGE "DEFAULT_MSG") + endif() + + # In config-mode, we rely on the variable _CONFIG, which is set by find_package() + # when it successfully found the config-file, including version checking: + if(FPHSA_CONFIG_MODE) + list(INSERT FPHSA_REQUIRED_VARS 0 ${_NAME}_CONFIG) + list(REMOVE_DUPLICATES FPHSA_REQUIRED_VARS) + set(FPHSA_VERSION_VAR ${_NAME}_VERSION) + endif() + + if(NOT FPHSA_REQUIRED_VARS) + message(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()") + endif() + endif() + +# now that we collected all arguments, process them + + if("x${FPHSA_FAIL_MESSAGE}" STREQUAL "xDEFAULT_MSG") + set(FPHSA_FAIL_MESSAGE "Could NOT find ${_NAME}") + endif() + + list(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR) + + string(TOUPPER ${_NAME} _NAME_UPPER) + string(TOLOWER ${_NAME} _NAME_LOWER) + + if(FPHSA_FOUND_VAR) + if(FPHSA_FOUND_VAR MATCHES "^${_NAME}_FOUND$" OR FPHSA_FOUND_VAR MATCHES "^${_NAME_UPPER}_FOUND$") + set(_FOUND_VAR ${FPHSA_FOUND_VAR}) + else() + message(FATAL_ERROR "The argument for FOUND_VAR is \"${FPHSA_FOUND_VAR}\", but only \"${_NAME}_FOUND\" and \"${_NAME_UPPER}_FOUND\" are valid names.") + endif() + else() + set(_FOUND_VAR ${_NAME_UPPER}_FOUND) + endif() + + # collect all variables which were not found, so they can be printed, so the + # user knows better what went wrong (#6375) + set(MISSING_VARS "") + set(DETAILS "") + # check if all passed variables are valid + set(FPHSA_FOUND_${_NAME} TRUE) + foreach(_CURRENT_VAR ${FPHSA_REQUIRED_VARS}) + if(NOT ${_CURRENT_VAR}) + set(FPHSA_FOUND_${_NAME} FALSE) + string(APPEND MISSING_VARS " ${_CURRENT_VAR}") + else() + string(APPEND DETAILS "[${${_CURRENT_VAR}}]") + endif() + endforeach() + if(FPHSA_FOUND_${_NAME}) + set(${_NAME}_FOUND TRUE) + set(${_NAME_UPPER}_FOUND TRUE) + else() + set(${_NAME}_FOUND FALSE) + set(${_NAME_UPPER}_FOUND FALSE) + endif() + + # component handling + unset(FOUND_COMPONENTS_MSG) + unset(MISSING_COMPONENTS_MSG) + + if(FPHSA_HANDLE_COMPONENTS) + foreach(comp ${${_NAME}_FIND_COMPONENTS}) + if(${_NAME}_${comp}_FOUND) + + if(NOT DEFINED FOUND_COMPONENTS_MSG) + set(FOUND_COMPONENTS_MSG "found components: ") + endif() + string(APPEND FOUND_COMPONENTS_MSG " ${comp}") + + else() + + if(NOT DEFINED MISSING_COMPONENTS_MSG) + set(MISSING_COMPONENTS_MSG "missing components: ") + endif() + string(APPEND MISSING_COMPONENTS_MSG " ${comp}") + + if(${_NAME}_FIND_REQUIRED_${comp}) + set(${_NAME}_FOUND FALSE) + string(APPEND MISSING_VARS " ${comp}") + endif() + + endif() + endforeach() + set(COMPONENT_MSG "${FOUND_COMPONENTS_MSG} ${MISSING_COMPONENTS_MSG}") + string(APPEND DETAILS "[c${COMPONENT_MSG}]") + endif() + + # version handling: + set(VERSION_MSG "") + set(VERSION_OK TRUE) + + # check with DEFINED here as the requested or found version may be "0" + if (DEFINED ${_NAME}_FIND_VERSION) + if(DEFINED ${FPHSA_VERSION_VAR}) + set(_FOUND_VERSION ${${FPHSA_VERSION_VAR}}) + + if(${_NAME}_FIND_VERSION_EXACT) # exact version required + # count the dots in the version string + string(REGEX REPLACE "[^.]" "" _VERSION_DOTS "${_FOUND_VERSION}") + # add one dot because there is one dot more than there are components + string(LENGTH "${_VERSION_DOTS}." _VERSION_DOTS) + if (_VERSION_DOTS GREATER ${_NAME}_FIND_VERSION_COUNT) + # Because of the C++ implementation of find_package() ${_NAME}_FIND_VERSION_COUNT + # is at most 4 here. Therefore a simple lookup table is used. + if (${_NAME}_FIND_VERSION_COUNT EQUAL 1) + set(_VERSION_REGEX "[^.]*") + elseif (${_NAME}_FIND_VERSION_COUNT EQUAL 2) + set(_VERSION_REGEX "[^.]*\\.[^.]*") + elseif (${_NAME}_FIND_VERSION_COUNT EQUAL 3) + set(_VERSION_REGEX "[^.]*\\.[^.]*\\.[^.]*") + else () + set(_VERSION_REGEX "[^.]*\\.[^.]*\\.[^.]*\\.[^.]*") + endif () + string(REGEX REPLACE "^(${_VERSION_REGEX})\\..*" "\\1" _VERSION_HEAD "${_FOUND_VERSION}") + unset(_VERSION_REGEX) + if (NOT ${_NAME}_FIND_VERSION VERSION_EQUAL _VERSION_HEAD) + set(VERSION_MSG "Found unsuitable version \"${_FOUND_VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"") + set(VERSION_OK FALSE) + else () + set(VERSION_MSG "(found suitable exact version \"${_FOUND_VERSION}\")") + endif () + unset(_VERSION_HEAD) + else () + if (NOT ${_NAME}_FIND_VERSION VERSION_EQUAL _FOUND_VERSION) + set(VERSION_MSG "Found unsuitable version \"${_FOUND_VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"") + set(VERSION_OK FALSE) + else () + set(VERSION_MSG "(found suitable exact version \"${_FOUND_VERSION}\")") + endif () + endif () + unset(_VERSION_DOTS) + + else() # minimum version specified: + if (${_NAME}_FIND_VERSION VERSION_GREATER _FOUND_VERSION) + set(VERSION_MSG "Found unsuitable version \"${_FOUND_VERSION}\", but required is at least \"${${_NAME}_FIND_VERSION}\"") + set(VERSION_OK FALSE) + else () + set(VERSION_MSG "(found suitable version \"${_FOUND_VERSION}\", minimum required is \"${${_NAME}_FIND_VERSION}\")") + endif () + endif() + + else() + + # if the package was not found, but a version was given, add that to the output: + if(${_NAME}_FIND_VERSION_EXACT) + set(VERSION_MSG "(Required is exact version \"${${_NAME}_FIND_VERSION}\")") + else() + set(VERSION_MSG "(Required is at least version \"${${_NAME}_FIND_VERSION}\")") + endif() + + endif() + else () + # Check with DEFINED as the found version may be 0. + if(DEFINED ${FPHSA_VERSION_VAR}) + set(VERSION_MSG "(found version \"${${FPHSA_VERSION_VAR}}\")") + endif() + endif () + + if(VERSION_OK) + string(APPEND DETAILS "[v${${FPHSA_VERSION_VAR}}(${${_NAME}_FIND_VERSION})]") + else() + set(${_NAME}_FOUND FALSE) + endif() + + + # print the result: + if (${_NAME}_FOUND) + FIND_PACKAGE_MESSAGE_SLIC3R(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG} ${COMPONENT_MSG}" "${DETAILS}") + else () + + if(FPHSA_CONFIG_MODE) + _FPHSA_HANDLE_FAILURE_CONFIG_MODE() + else() + if(NOT VERSION_OK) + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: ${VERSION_MSG} (found ${${_FIRST_REQUIRED_VAR}})") + else() + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} (missing:${MISSING_VARS}) ${VERSION_MSG}") + endif() + endif() + + endif () + + set(${_NAME}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) + set(${_NAME_UPPER}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) +endfunction() diff --git a/cmake/modules/FindPackageMessage_SLIC3R.cmake b/cmake/modules/FindPackageMessage_SLIC3R.cmake new file mode 100644 index 000000000..a7a5eb98f --- /dev/null +++ b/cmake/modules/FindPackageMessage_SLIC3R.cmake @@ -0,0 +1,54 @@ +# Modified from the CMake github master. +# required by the bundled FindCURL.cmake + + + + + +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#.rst: +# FindPackageMessage +# ------------------ +# +# +# +# FIND_PACKAGE_MESSAGE( "message for user" "find result details") +# +# This macro is intended to be used in FindXXX.cmake modules files. It +# will print a message once for each unique find result. This is useful +# for telling the user where a package was found. The first argument +# specifies the name (XXX) of the package. The second argument +# specifies the message to display. The third argument lists details +# about the find result so that if they change the message will be +# displayed again. The macro also obeys the QUIET argument to the +# find_package command. +# +# Example: +# +# :: +# +# if(X11_FOUND) +# FIND_PACKAGE_MESSAGE(X11 "Found X11: ${X11_X11_LIB}" +# "[${X11_X11_LIB}][${X11_INCLUDE_DIR}]") +# else() +# ... +# endif() + +function(FIND_PACKAGE_MESSAGE_SLIC3R pkg msg details) + # Avoid printing a message repeatedly for the same find result. + if(NOT ${pkg}_FIND_QUIETLY) + string(REPLACE "\n" "" details "${details}") + set(DETAILS_VAR FIND_PACKAGE_MESSAGE_DETAILS_${pkg}) + if(NOT "${details}" STREQUAL "${${DETAILS_VAR}}") + # The message has not yet been printed. + message(STATUS "${msg}") + + # Save the find details in the cache to avoid printing the same + # message again. + set("${DETAILS_VAR}" "${details}" + CACHE INTERNAL "Details about finding ${pkg}") + endif() + endif() +endfunction() diff --git a/cmake/modules/FindTBB.cmake b/cmake/modules/FindTBB.cmake index 8b498d3ab..e5115ab44 100644 --- a/cmake/modules/FindTBB.cmake +++ b/cmake/modules/FindTBB.cmake @@ -280,7 +280,7 @@ if(NOT TBB_FOUND) ################################## if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND) - add_library(tbb SHARED IMPORTED) + add_library(tbb UNKNOWN IMPORTED) set_target_properties(tbb PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS} IMPORTED_LOCATION ${TBB_LIBRARIES}) @@ -288,7 +288,7 @@ if(NOT TBB_FOUND) set_target_properties(tbb PROPERTIES INTERFACE_COMPILE_DEFINITIONS "$<$,$>:TBB_USE_DEBUG=1>" IMPORTED_LOCATION_DEBUG ${TBB_LIBRARIES_DEBUG} - IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_LIBRARIES_DEBUG} + IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_LIBRARIES_RELEASE} IMPORTED_LOCATION_RELEASE ${TBB_LIBRARIES_RELEASE} IMPORTED_LOCATION_MINSIZEREL ${TBB_LIBRARIES_RELEASE} ) @@ -310,6 +310,7 @@ if(NOT TBB_FOUND) unset(TBB_DEFAULT_SEARCH_DIR) if(TBB_DEBUG) + message(STATUS " TBB_FOUND = ${TBB_FOUND}") message(STATUS " TBB_INCLUDE_DIRS = ${TBB_INCLUDE_DIRS}") message(STATUS " TBB_DEFINITIONS = ${TBB_DEFINITIONS}") message(STATUS " TBB_LIBRARIES = ${TBB_LIBRARIES}") diff --git a/cmake/modules/SelectLibraryConfigurations_SLIC3R.cmake b/cmake/modules/SelectLibraryConfigurations_SLIC3R.cmake new file mode 100644 index 000000000..d25ac8640 --- /dev/null +++ b/cmake/modules/SelectLibraryConfigurations_SLIC3R.cmake @@ -0,0 +1,77 @@ +# Modified from the CMake github master. +# required by the bundled FindCURL.cmake + + + + +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#.rst: +# SelectLibraryConfigurations +# --------------------------- +# +# +# +# select_library_configurations( basename ) +# +# This macro takes a library base name as an argument, and will choose +# good values for basename_LIBRARY, basename_LIBRARIES, +# basename_LIBRARY_DEBUG, and basename_LIBRARY_RELEASE depending on what +# has been found and set. If only basename_LIBRARY_RELEASE is defined, +# basename_LIBRARY will be set to the release value, and +# basename_LIBRARY_DEBUG will be set to basename_LIBRARY_DEBUG-NOTFOUND. +# If only basename_LIBRARY_DEBUG is defined, then basename_LIBRARY will +# take the debug value, and basename_LIBRARY_RELEASE will be set to +# basename_LIBRARY_RELEASE-NOTFOUND. +# +# If the generator supports configuration types, then basename_LIBRARY +# and basename_LIBRARIES will be set with debug and optimized flags +# specifying the library to be used for the given configuration. If no +# build type has been set or the generator in use does not support +# configuration types, then basename_LIBRARY and basename_LIBRARIES will +# take only the release value, or the debug value if the release one is +# not set. + +# This macro was adapted from the FindQt4 CMake module and is maintained by Will +# Dicharry . + +macro( select_library_configurations_SLIC3R basename ) + if(NOT ${basename}_LIBRARY_RELEASE) + set(${basename}_LIBRARY_RELEASE "${basename}_LIBRARY_RELEASE-NOTFOUND" CACHE FILEPATH "Path to a library.") + endif() + if(NOT ${basename}_LIBRARY_DEBUG) + set(${basename}_LIBRARY_DEBUG "${basename}_LIBRARY_DEBUG-NOTFOUND" CACHE FILEPATH "Path to a library.") + endif() + + get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if( ${basename}_LIBRARY_DEBUG AND ${basename}_LIBRARY_RELEASE AND + NOT ${basename}_LIBRARY_DEBUG STREQUAL ${basename}_LIBRARY_RELEASE AND + ( _isMultiConfig OR CMAKE_BUILD_TYPE ) ) + # if the generator is multi-config or if CMAKE_BUILD_TYPE is set for + # single-config generators, set optimized and debug libraries + set( ${basename}_LIBRARY "" ) + foreach( _libname IN LISTS ${basename}_LIBRARY_RELEASE ) + list( APPEND ${basename}_LIBRARY optimized "${_libname}" ) + endforeach() + foreach( _libname IN LISTS ${basename}_LIBRARY_DEBUG ) + list( APPEND ${basename}_LIBRARY debug "${_libname}" ) + endforeach() + elseif( ${basename}_LIBRARY_RELEASE ) + set( ${basename}_LIBRARY ${${basename}_LIBRARY_RELEASE} ) + elseif( ${basename}_LIBRARY_DEBUG ) + set( ${basename}_LIBRARY ${${basename}_LIBRARY_DEBUG} ) + else() + set( ${basename}_LIBRARY "${basename}_LIBRARY-NOTFOUND") + endif() + + set( ${basename}_LIBRARIES "${${basename}_LIBRARY}" ) + + if( ${basename}_LIBRARY ) + set( ${basename}_FOUND TRUE ) + endif() + + mark_as_advanced( ${basename}_LIBRARY_RELEASE + ${basename}_LIBRARY_DEBUG + ) +endmacro() diff --git a/resources/icons/mode_expert.png b/resources/icons/mode_expert.png new file mode 100644 index 000000000..1716bab44 Binary files /dev/null and b/resources/icons/mode_expert.png differ diff --git a/resources/icons/mode_expert_.png b/resources/icons/mode_expert_.png new file mode 100644 index 000000000..4d78bcccf Binary files /dev/null and b/resources/icons/mode_expert_.png differ diff --git a/resources/icons/mode_middle.png b/resources/icons/mode_middle.png new file mode 100644 index 000000000..b08f73ca6 Binary files /dev/null and b/resources/icons/mode_middle.png differ diff --git a/resources/icons/mode_middle_.png b/resources/icons/mode_middle_.png new file mode 100644 index 000000000..d98d8f709 Binary files /dev/null and b/resources/icons/mode_middle_.png differ diff --git a/resources/icons/mode_simple.png b/resources/icons/mode_simple.png new file mode 100644 index 000000000..8bc300228 Binary files /dev/null and b/resources/icons/mode_simple.png differ diff --git a/resources/icons/mode_simple_.png b/resources/icons/mode_simple_.png new file mode 100644 index 000000000..aac2b61b0 Binary files /dev/null and b/resources/icons/mode_simple_.png differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 330134c3b..dbc4ecd21 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -84,7 +84,7 @@ if (SLIC3R_GUI) # Configure libcurl & OpenSSL find_package(CURL REQUIRED) target_include_directories(slic3r PRIVATE ${CURL_INCLUDE_DIRS}) - target_link_libraries(slic3r ${CURL_LIBRARIES}) + target_link_libraries(slic3r CURL::libcurl) if (SLIC3R_STATIC) if (NOT APPLE) # libcurl is always linked dynamically to the system libcurl on OSX. @@ -119,28 +119,28 @@ if (MSVC) foreach (CONF ${CMAKE_CONFIGURATION_TYPES}) file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${CONF}" WIN_CONF_OUTPUT_DIR) file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${CONF}/resources" WIN_RESOURCES_SYMLINK) - add_custom_target("resources_symlink_${CONF}" ALL - DEPENDS slic3r + add_custom_command(TARGET slic3r POST_BUILD COMMAND if exist "${WIN_CONF_OUTPUT_DIR}" "(" if not exist "${WIN_RESOURCES_SYMLINK}" "(" mklink /J "${WIN_RESOURCES_SYMLINK}" "${SLIC3R_RESOURCES_DIR_WIN}" ")" ")" + COMMENT "Symlinking the resources directory into the build tree" VERBATIM ) endforeach () else () file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}/resources" WIN_RESOURCES_SYMLINK) - add_custom_target(resources_symlink ALL - DEPENDS slic3r + add_custom_command(TARGET slic3r POST_BUILD COMMAND if not exist "${WIN_RESOURCES_SYMLINK}" "(" mklink /J "${WIN_RESOURCES_SYMLINK}" "${SLIC3R_RESOURCES_DIR_WIN}" ")" + COMMENT "Symlinking the resources directory into the build tree" VERBATIM ) endif () else () - add_custom_target(resources_symlink ALL - DEPENDS slic3r + add_custom_command(TARGET slic3r POST_BUILD COMMAND ln -sf "${SLIC3R_RESOURCES_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/../resources" + COMMENT "Symlinking the resources directory into the build tree" VERBATIM ) endif() diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index b5974489d..574dc4301 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -167,7 +167,7 @@ target_link_libraries(libslic3r poly2tri qhull semver - ${TBB_LIBRARIES} + tbb ) if(SLIC3R_PROFILE) diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 281591596..051204f1e 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -61,6 +61,12 @@ enum ConfigOptionType { coEnum = 8, }; +enum ConfigOptionMode { + comSimple, + comMiddle, + comExpert +}; + // A generic value of a configuration option. class ConfigOption { public: @@ -982,6 +988,7 @@ public: // By setting min=0, only nonnegative input is allowed. int min = INT_MIN; int max = INT_MAX; + ConfigOptionMode mode = comSimple; // Legacy names for this configuration option. // Used when parsing legacy configuration file. std::vector aliases; diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index bf94db6b1..085f55b9b 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -1249,14 +1249,25 @@ namespace Slic3r { void _3MF_Importer::_apply_transform(ModelInstance& instance, const Transform3d& transform) { - // slic3r ModelInstance cannot be transformed using a matrix - // we extract from the given matrix only the values currently used - // translation Vec3d offset = transform.matrix().block(0, 3, 3, 1); - // scale Eigen::Matrix m3x3 = transform.matrix().block(0, 0, 3, 3); +#if ENABLE_MIRROR + // mirror + // it is impossible to reconstruct the original mirroring factors from a matrix, + // we can only detect if the matrix contains a left handed reference system + // in which case we reorient it back to right handed by mirroring the x axis + Vec3d mirror = Vec3d::Ones(); + if (m3x3.col(0).dot(m3x3.col(1).cross(m3x3.col(2))) < 0.0) + { + mirror(0) = -1.0; + // remove mirror + m3x3.col(0) *= -1.0; + } + + // scale +#endif // ENABLE_MIRROR Vec3d scale(m3x3.col(0).norm(), m3x3.col(1).norm(), m3x3.col(2).norm()); // invalid scale value, return @@ -1273,6 +1284,9 @@ namespace Slic3r { instance.set_offset(offset); instance.set_scaling_factor(scale); instance.set_rotation(rotation); +#if ENABLE_MIRROR + instance.set_mirror(mirror); +#endif // ENABLE_MIRROR } bool _3MF_Importer::_handle_start_config(const char** attributes, unsigned int num_attributes) diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index 00ed0d937..be0261634 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -32,6 +32,9 @@ // 2 : Added z component of offset // Added x and y components of rotation // Added x, y and z components of scale +#if ENABLE_MIRROR +// Added x, y and z components of mirror +#endif // ENABLE_MIRROR const unsigned int VERSION_AMF = 2; const char* SLIC3RPE_AMF_VERSION = "slic3rpe_amf_version"; @@ -126,14 +129,27 @@ struct AMFParserContext NODE_TYPE_RY, // amf/constellation/instance/ry NODE_TYPE_RZ, // amf/constellation/instance/rz NODE_TYPE_SCALE, // amf/constellation/instance/scale - NODE_TYPE_SCALEX, // amf/constellation/instance/scalex - NODE_TYPE_SCALEY, // amf/constellation/instance/scaley - NODE_TYPE_SCALEZ, // amf/constellation/instance/scalez + NODE_TYPE_SCALEX, // amf/constellation/instance/scalex + NODE_TYPE_SCALEY, // amf/constellation/instance/scaley + NODE_TYPE_SCALEZ, // amf/constellation/instance/scalez +#if ENABLE_MIRROR + NODE_TYPE_MIRRORX, // amf/constellation/instance/mirrorx + NODE_TYPE_MIRRORY, // amf/constellation/instance/mirrory + NODE_TYPE_MIRRORZ, // amf/constellation/instance/mirrorz +#endif // ENABLE_MIRROR NODE_TYPE_METADATA, // anywhere under amf/*/metadata }; struct Instance { +#if ENABLE_MIRROR + Instance() + : deltax_set(false), deltay_set(false), deltaz_set(false) + , rx_set(false), ry_set(false), rz_set(false) + , scalex_set(false), scaley_set(false), scalez_set(false) + , mirrorx_set(false), mirrory_set(false), mirrorz_set(false) {} +#else Instance() : deltax_set(false), deltay_set(false), deltaz_set(false), rx_set(false), ry_set(false), rz_set(false), scalex_set(false), scaley_set(false), scalez_set(false) {} +#endif // ENABLE_MIRROR // Shift in the X axis. float deltax; bool deltax_set; @@ -159,6 +175,15 @@ struct AMFParserContext bool scaley_set; float scalez; bool scalez_set; +#if ENABLE_MIRROR + // Mirroring factors + float mirrorx; + bool mirrorx_set; + float mirrory; + bool mirrory_set; + float mirrorz; + bool mirrorz_set; +#endif // ENABLE_MIRROR }; struct Object { @@ -289,6 +314,14 @@ void AMFParserContext::startElement(const char *name, const char **atts) node_type_new = NODE_TYPE_SCALEZ; else if (strcmp(name, "scale") == 0) node_type_new = NODE_TYPE_SCALE; +#if ENABLE_MIRROR + else if (strcmp(name, "mirrorx") == 0) + node_type_new = NODE_TYPE_MIRRORX; + else if (strcmp(name, "mirrory") == 0) + node_type_new = NODE_TYPE_MIRRORY; + else if (strcmp(name, "mirrorz") == 0) + node_type_new = NODE_TYPE_MIRRORZ; +#endif // ENABLE_MIRROR } break; case 4: @@ -345,16 +378,23 @@ void AMFParserContext::characters(const XML_Char *s, int len) { switch (m_path.size()) { case 4: - if (m_path.back() == NODE_TYPE_DELTAX || - m_path.back() == NODE_TYPE_DELTAY || - m_path.back() == NODE_TYPE_DELTAZ || + if (m_path.back() == NODE_TYPE_DELTAX || + m_path.back() == NODE_TYPE_DELTAY || + m_path.back() == NODE_TYPE_DELTAZ || m_path.back() == NODE_TYPE_RX || m_path.back() == NODE_TYPE_RY || m_path.back() == NODE_TYPE_RZ || m_path.back() == NODE_TYPE_SCALEX || m_path.back() == NODE_TYPE_SCALEY || m_path.back() == NODE_TYPE_SCALEZ || +#if ENABLE_MIRROR + m_path.back() == NODE_TYPE_SCALE || + m_path.back() == NODE_TYPE_MIRRORX || + m_path.back() == NODE_TYPE_MIRRORY || + m_path.back() == NODE_TYPE_MIRRORZ) +#else m_path.back() == NODE_TYPE_SCALE) +#endif // ENABLE_MIRROR m_value[0].append(s, len); break; case 6: @@ -446,6 +486,26 @@ void AMFParserContext::endElement(const char * /* name */) m_instance->scalez_set = true; m_value[0].clear(); break; +#if ENABLE_MIRROR + case NODE_TYPE_MIRRORX: + assert(m_instance); + m_instance->mirrorx = float(atof(m_value[0].c_str())); + m_instance->mirrorx_set = true; + m_value[0].clear(); + break; + case NODE_TYPE_MIRRORY: + assert(m_instance); + m_instance->mirrory = float(atof(m_value[0].c_str())); + m_instance->mirrory_set = true; + m_value[0].clear(); + break; + case NODE_TYPE_MIRRORZ: + assert(m_instance); + m_instance->mirrorz = float(atof(m_value[0].c_str())); + m_instance->mirrorz_set = true; + m_value[0].clear(); + break; +#endif // ENABLE_MIRROR // Object vertices: case NODE_TYPE_VERTEX: @@ -585,6 +645,9 @@ void AMFParserContext::endDocument() mi->set_offset(Vec3d(instance.deltax_set ? (double)instance.deltax : 0.0, instance.deltay_set ? (double)instance.deltay : 0.0, instance.deltaz_set ? (double)instance.deltaz : 0.0)); mi->set_rotation(Vec3d(instance.rx_set ? (double)instance.rx : 0.0, instance.ry_set ? (double)instance.ry : 0.0, instance.rz_set ? (double)instance.rz : 0.0)); mi->set_scaling_factor(Vec3d(instance.scalex_set ? (double)instance.scalex : 1.0, instance.scaley_set ? (double)instance.scaley : 1.0, instance.scalez_set ? (double)instance.scalez : 1.0)); +#if ENABLE_MIRROR + mi->set_mirror(Vec3d(instance.mirrorx_set ? (double)instance.mirrorx : 1.0, instance.mirrory_set ? (double)instance.mirrory : 1.0, instance.mirrorz_set ? (double)instance.mirrorz : 1.0)); +#endif // ENABLE_MIRROR } } } @@ -891,6 +954,11 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c " %lf\n" " %lf\n" " %lf\n" +#if ENABLE_MIRROR + " %lf\n" + " %lf\n" + " %lf\n" +#endif // ENABLE_MIRROR " \n", object_id, instance->get_offset(X), @@ -901,7 +969,14 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c instance->get_rotation(Z), instance->get_scaling_factor(X), instance->get_scaling_factor(Y), +#if ENABLE_MIRROR + instance->get_scaling_factor(Z), + instance->get_mirror(X), + instance->get_mirror(Y), + instance->get_mirror(Z)); +#else instance->get_scaling_factor(Z)); +#endif // ENABLE_MIRROR //FIXME missing instance->scaling_factor instances.append(buf); diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index c45f08791..a100f520a 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -1166,7 +1166,11 @@ MedialAxis::retrieve_endpoint(const VD::cell_type* cell) const } } +#if ENABLE_MIRROR +void assemble_transform(Transform3d& transform, const Vec3d& translation, const Vec3d& rotation, const Vec3d& scale, const Vec3d& mirror) +#else void assemble_transform(Transform3d& transform, const Vec3d& translation, const Vec3d& rotation, const Vec3d& scale) +#endif // ENABLE_MIRROR { transform = Transform3d::Identity(); transform.translate(translation); @@ -1174,12 +1178,23 @@ void assemble_transform(Transform3d& transform, const Vec3d& translation, const transform.rotate(Eigen::AngleAxisd(rotation(1), Vec3d::UnitY())); transform.rotate(Eigen::AngleAxisd(rotation(0), Vec3d::UnitX())); transform.scale(scale); +#if ENABLE_MIRROR + transform.scale(mirror); +#endif // ENABLE_MIRROR } +#if ENABLE_MIRROR +Transform3d assemble_transform(const Vec3d& translation, const Vec3d& rotation, const Vec3d& scale, const Vec3d& mirror) +#else Transform3d assemble_transform(const Vec3d& translation, const Vec3d& rotation, const Vec3d& scale) +#endif // ENABLE_MIRROR { Transform3d transform; +#if ENABLE_MIRROR + assemble_transform(transform, translation, rotation, scale, mirror); +#else assemble_transform(transform, translation, rotation, scale); +#endif // ENABLE_MIRROR return transform; } diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index 35f59bdfd..9046add25 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -158,20 +158,40 @@ class MedialAxis { }; // Sets the given transform by assembling the given transformations in the following order: +#if ENABLE_MIRROR +// 1) mirror +// 2) scale +// 3) rotate X +// 4) rotate Y +// 5) rotate Z +// 6) translate +void assemble_transform(Transform3d& transform, const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(), const Vec3d& scale = Vec3d::Ones(), const Vec3d& mirror = Vec3d::Ones()); +#else // 1) scale // 2) rotate X // 3) rotate Y // 4) rotate Z // 5) translate void assemble_transform(Transform3d& transform, const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(), const Vec3d& scale = Vec3d::Ones()); +#endif // ENABLE_MIRROR // Returns the transform obtained by assembling the given transformations in the following order: +#if ENABLE_MIRROR +// 1) mirror +// 2) scale +// 3) rotate X +// 4) rotate Y +// 5) rotate Z +// 6) translate +Transform3d assemble_transform(const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(), const Vec3d& scale = Vec3d::Ones(), const Vec3d& mirror = Vec3d::Ones()); +#else // 1) scale // 2) rotate X // 3) rotate Y // 4) rotate Z // 5) translate Transform3d assemble_transform(const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(), const Vec3d& scale = Vec3d::Ones()); +#endif // ENABLE_MIRROR // Returns the euler angles extracted from the given rotation matrix // Warning -> The matrix should not contain any scale or shear !!! diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index bc9935086..1cd20c6fa 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -412,6 +412,7 @@ void Model::convert_multipart_object(unsigned int max_extruders) ModelObject* object = new ModelObject(this); object->input_file = this->objects.front()->input_file; + object->name = this->objects.front()->name; reset_auto_extruder_id(); @@ -694,9 +695,13 @@ void ModelObject::center_around_origin() if (!this->instances.empty()) { for (ModelInstance *i : this->instances) { +#if ENABLE_MIRROR + i->set_offset(i->get_offset() - shift); +#else // apply rotation and scaling to vector as well before translating instance, // in order to leave final position unaltered i->set_offset(i->get_offset() + i->transform_vector(-shift, true)); +#endif // ENABLE_MIRROR } this->invalidate_bounding_box(); } @@ -1064,6 +1069,38 @@ void ModelInstance::set_rotation(Axis axis, double rotation) m_rotation(axis) = rotation; } +#if ENABLE_MIRROR +void ModelInstance::set_scaling_factor(const Vec3d& scaling_factor) +{ + set_scaling_factor(X, scaling_factor(0)); + set_scaling_factor(Y, scaling_factor(1)); + set_scaling_factor(Z, scaling_factor(2)); +} + +void ModelInstance::set_scaling_factor(Axis axis, double scaling_factor) +{ + m_scaling_factor(axis) = std::abs(scaling_factor); +} + +void ModelInstance::set_mirror(const Vec3d& mirror) +{ + set_mirror(X, mirror(0)); + set_mirror(Y, mirror(1)); + set_mirror(Z, mirror(2)); +} + +void ModelInstance::set_mirror(Axis axis, double mirror) +{ + double abs_mirror = std::abs(mirror); + if (abs_mirror == 0.0) + mirror = 1.0; + else if (abs_mirror != 1.0) + mirror /= abs_mirror; + + m_mirror(axis) = mirror; +} +#endif // ENABLE_MIRROR + void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const { mesh->transform(world_matrix(dont_translate).cast()); @@ -1114,12 +1151,21 @@ void ModelInstance::transform_polygon(Polygon* polygon) const polygon->scale(this->m_scaling_factor(0), this->m_scaling_factor(1)); // scale around polygon origin } +#if ENABLE_MIRROR +Transform3d ModelInstance::world_matrix(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const +#else Transform3d ModelInstance::world_matrix(bool dont_translate, bool dont_rotate, bool dont_scale) const +#endif // ENABLE_MIRROR { Vec3d translation = dont_translate ? Vec3d::Zero() : m_offset; Vec3d rotation = dont_rotate ? Vec3d::Zero() : m_rotation; Vec3d scale = dont_scale ? Vec3d::Ones() : m_scaling_factor; +#if ENABLE_MIRROR + Vec3d mirror = dont_mirror ? Vec3d::Ones() : m_mirror; + return Geometry::assemble_transform(translation, rotation, scale, mirror); +#else return Geometry::assemble_transform(translation, rotation, scale); +#endif // ENABLE_MIRROR } } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 4ca7e140c..81f101636 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -115,6 +115,7 @@ public: ModelVolume* add_volume(const ModelVolume &volume); void delete_volume(size_t idx); void clear_volumes(); + bool is_multiparts() const { return volumes.size() > 1; } ModelInstance* add_instance(); ModelInstance* add_instance(const ModelInstance &instance); @@ -285,6 +286,9 @@ private: Vec3d m_offset; // in unscaled coordinates Vec3d m_rotation; // Rotation around the three axes, in radians around mesh center point Vec3d m_scaling_factor; // Scaling factors along the three axes +#if ENABLE_MIRROR + Vec3d m_mirror; // Mirroring along the three axes +#endif // ENABLE_MIRROR public: // flag showing the position of this instance with respect to the print volume (set by Print::validate() using ModelObject::check_instances_print_volume_state()) @@ -307,8 +311,21 @@ public: Vec3d get_scaling_factor() const { return m_scaling_factor; } double get_scaling_factor(Axis axis) const { return m_scaling_factor(axis); } +#if ENABLE_MIRROR + void set_scaling_factor(const Vec3d& scaling_factor); + void set_scaling_factor(Axis axis, double scaling_factor); +#else void set_scaling_factor(const Vec3d& scaling_factor) { m_scaling_factor = scaling_factor; } void set_scaling_factor(Axis axis, double scaling_factor) { m_scaling_factor(axis) = scaling_factor; } +#endif // ENABLE_MIRROR + +#if ENABLE_MIRROR + const Vec3d& get_mirror() const { return m_mirror; } + double get_mirror(Axis axis) const { return m_mirror(axis); } + + void set_mirror(const Vec3d& mirror); + void set_mirror(Axis axis, double mirror); +#endif // ENABLE_MIRROR // To be called on an external mesh void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const; @@ -321,7 +338,11 @@ public: // To be called on an external polygon. It does not translate the polygon, only rotates and scales. void transform_polygon(Polygon* polygon) const; +#if ENABLE_MIRROR + Transform3d world_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const; +#else Transform3d world_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false) const; +#endif // ENABLE_MIRROR bool is_printable() const { return print_volume_state == PVS_Inside; } @@ -329,9 +350,15 @@ private: // Parent object, owning this instance. ModelObject* object; +#if ENABLE_MIRROR + ModelInstance(ModelObject *object) : m_offset(Vec3d::Zero()), m_rotation(Vec3d::Zero()), m_scaling_factor(Vec3d::Ones()), m_mirror(Vec3d::Ones()), object(object), print_volume_state(PVS_Inside) {} + ModelInstance(ModelObject *object, const ModelInstance &other) : + m_offset(other.m_offset), m_rotation(other.m_rotation), m_scaling_factor(other.m_scaling_factor), m_mirror(other.m_mirror), object(object), print_volume_state(PVS_Inside) {} +#else ModelInstance(ModelObject *object) : m_rotation(Vec3d::Zero()), m_scaling_factor(Vec3d::Ones()), m_offset(Vec3d::Zero()), object(object), print_volume_state(PVS_Inside) {} ModelInstance(ModelObject *object, const ModelInstance &other) : m_rotation(other.m_rotation), m_scaling_factor(other.m_scaling_factor), m_offset(other.m_offset), object(object), print_volume_state(PVS_Inside) {} +#endif // ENABLE_MIRROR }; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 46df2b1c0..2b21a760f 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -39,6 +39,7 @@ void PrintConfigDef::init_common_params() def = this->add("bed_shape", coPoints); def->label = L("Bed shape"); + def->mode = comMiddle; def->default_value = new ConfigOptionPoints{ Vec2d(0, 0), Vec2d(200, 0), Vec2d(200, 200), Vec2d(0, 200) }; def = this->add("layer_height", coFloat); @@ -56,6 +57,7 @@ void PrintConfigDef::init_common_params() def->tooltip = L("Set this to the maximum height that can be reached by your extruder while printing."); def->sidetext = L("mm"); def->cli = "max-print-height=f"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloat(200.0); } @@ -73,6 +75,7 @@ void PrintConfigDef::init_fff_params() "This is mostly useful with Bowden extruders which suffer from oozing. " "This feature slows down both the print and the G-code generation."); def->cli = "avoid-crossing-perimeters!"; + def->mode = comMiddle; def->default_value = new ConfigOptionBool(false); def = this->add("bed_temperature", coInts); @@ -94,6 +97,7 @@ void PrintConfigDef::init_fff_params() def->multiline = true; def->full_width = true; def->height = 50; + def->mode = comExpert; def->default_value = new ConfigOptionString(""); def = this->add("between_objects_gcode", coString); @@ -103,6 +107,7 @@ void PrintConfigDef::init_fff_params() def->multiline = true; def->full_width = true; def->height = 120; + def->mode = comExpert; def->default_value = new ConfigOptionString(""); def = this->add("bottom_solid_layers", coInt); @@ -121,6 +126,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s²"); def->cli = "bridge-acceleration=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("bridge_angle", coFloat); @@ -132,6 +138,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("°"); def->cli = "bridge-angle=f"; def->min = 0; + def->mode = comMiddle; def->default_value = new ConfigOptionFloat(0.); def = this->add("bridge_fan_speed", coInts); @@ -141,6 +148,7 @@ void PrintConfigDef::init_fff_params() def->cli = "bridge-fan-speed=i@"; def->min = 0; def->max = 100; + def->mode = comExpert; def->default_value = new ConfigOptionInts { 100 }; def = this->add("bridge_flow_ratio", coFloat); @@ -153,6 +161,7 @@ void PrintConfigDef::init_fff_params() def->cli = "bridge-flow-ratio=f"; def->min = 0; def->max = 2; + def->mode = comMiddle; def->default_value = new ConfigOptionFloat(1); def = this->add("bridge_speed", coFloat); @@ -171,6 +180,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->cli = "brim-width=f"; def->min = 0; + def->mode = comMiddle; def->default_value = new ConfigOptionFloat(0); def = this->add("clip_multipart_objects", coBool); @@ -179,10 +189,12 @@ void PrintConfigDef::init_fff_params() "to clip the overlapping object parts one by the other " "(2nd part will be clipped by the 1st, 3rd part will be clipped by the 1st and 2nd etc)."); def->cli = "clip-multipart-objects!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("compatible_printers", coStrings); def->label = L("Compatible printers"); + def->mode = comMiddle; def->default_value = new ConfigOptionStrings(); def = this->add("compatible_printers_condition", coString); @@ -190,6 +202,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("A boolean expression using the configuration values of an active printer profile. " "If this expression evaluates to true, this profile is considered compatible " "with the active printer profile."); + def->mode = comExpert; def->default_value = new ConfigOptionString(); // The following value is to be stored into the project file (AMF, 3MF, Config ...) @@ -204,6 +217,7 @@ void PrintConfigDef::init_fff_params() "This feature is useful to avoid the risk of ruined prints. " "Slic3r should warn and prevent you from extruder collisions, but beware."); def->cli = "complete-objects!"; + def->mode = comMiddle; def->default_value = new ConfigOptionBool(false); def = this->add("cooling", coBools); @@ -219,6 +233,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->cli = "cooling_tube_retraction=f"; def->min = 0; + def->mode = comMiddle; def->default_value = new ConfigOptionFloat(91.5f); def = this->add("cooling_tube_length", coFloat); @@ -227,6 +242,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->cli = "cooling_tube_length=f"; def->min = 0; + def->mode = comMiddle; def->default_value = new ConfigOptionFloat(5.f); def = this->add("default_acceleration", coFloat); @@ -237,6 +253,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s²"); def->cli = "default-acceleration=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("default_filament_profile", coStrings); @@ -259,6 +276,7 @@ void PrintConfigDef::init_fff_params() def->cli = "disable-fan-first-layers=i@"; def->min = 0; def->max = 1000; + def->mode = comExpert; def->default_value = new ConfigOptionInts { 3 }; def = this->add("dont_support_bridges", coBool); @@ -267,6 +285,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Experimental option for preventing support material from being generated " "under bridged areas."); def->cli = "dont-support-bridges!"; + def->mode = comMiddle; def->default_value = new ConfigOptionBool(true); def = this->add("duplicate_distance", coFloat); @@ -286,6 +305,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->cli = "elefant-foot-compensation=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("end_gcode", coString); @@ -296,6 +316,7 @@ void PrintConfigDef::init_fff_params() def->multiline = true; def->full_width = true; def->height = 120; + def->mode = comExpert; def->default_value = new ConfigOptionString("M104 S0 ; turn off temperature\nG28 X0 ; home X axis\nM84 ; disable motors\n"); def = this->add("end_filament_gcode", coStrings); @@ -307,6 +328,7 @@ void PrintConfigDef::init_fff_params() def->multiline = true; def->full_width = true; def->height = 120; + def->mode = comExpert; def->default_value = new ConfigOptionStrings { "; Filament-specific end gcode \n;END gcode for filament\n" }; def = this->add("ensure_vertical_shell_thickness", coBool); @@ -315,6 +337,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Add solid infill near sloping surfaces to guarantee the vertical shell thickness " "(top+bottom solid layers)."); def->cli = "ensure-vertical-shell-thickness!"; + def->mode = comMiddle; def->default_value = new ConfigOptionBool(false); def = this->add("external_fill_pattern", coEnum); @@ -346,6 +369,7 @@ void PrintConfigDef::init_fff_params() "If expressed as percentage (for example 200%), it will be computed over layer height."); def->sidetext = L("mm or % (leave 0 for default)"); def->cli = "external-perimeter-extrusion-width=s"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloatOrPercent(0, false); def = this->add("external_perimeter_speed", coFloatOrPercent); @@ -366,6 +390,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Print contour perimeters from the outermost one to the innermost one " "instead of the default inverse order."); def->cli = "external-perimeters-first!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("extra_perimeters", coBool); @@ -375,6 +400,7 @@ void PrintConfigDef::init_fff_params() "Slic3r keeps adding perimeters, until more than 70% of the loop immediately above " "is supported."); def->cli = "extra-perimeters!"; + def->mode = comMiddle; def->default_value = new ConfigOptionBool(true); def = this->add("extruder", coInt); @@ -401,6 +427,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->cli = "extruder-clearance-height=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(20); def = this->add("extruder_clearance_radius", coFloat); @@ -412,6 +439,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->cli = "extruder-clearance-radius=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(20); def = this->add("extruder_colour", coStrings); @@ -430,6 +458,7 @@ void PrintConfigDef::init_fff_params() "from the XY coordinate)."); def->sidetext = L("mm"); def->cli = "extruder-offset=s@"; + def->mode = comMiddle; def->default_value = new ConfigOptionPoints { Vec2d(0,0) }; def = this->add("extrusion_axis", coString); @@ -446,6 +475,7 @@ void PrintConfigDef::init_fff_params() "Usual values are between 0.9 and 1.1. If you think you need to change this more, " "check filament diameter and your firmware E steps."); def->cli = "extrusion-multiplier=f@"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloats { 1. }; def = this->add("extrusion_width", coFloatOrPercent); @@ -457,6 +487,7 @@ void PrintConfigDef::init_fff_params() "If expressed as percentage (for example: 230%), it will be computed over layer height."); def->sidetext = L("mm or % (leave 0 for auto)"); def->cli = "extrusion-width=s"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloatOrPercent(0, false); def = this->add("fan_always_on", coBools); @@ -475,6 +506,7 @@ void PrintConfigDef::init_fff_params() def->width = 60; def->min = 0; def->max = 1000; + def->mode = comExpert; def->default_value = new ConfigOptionInts { 60 }; def = this->add("filament_colour", coStrings); @@ -491,6 +523,7 @@ void PrintConfigDef::init_fff_params() def->multiline = true; def->full_width = true; def->height = 130; + def->mode = comMiddle; def->default_value = new ConfigOptionStrings { "" }; def = this->add("filament_max_volumetric_speed", coFloats); @@ -501,6 +534,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm³/s"); def->cli = "filament-max-volumetric-speed=f@"; def->min = 0; + def->mode = comMiddle; def->default_value = new ConfigOptionFloats { 0. }; def = this->add("filament_loading_speed", coFloats); @@ -509,6 +543,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->cli = "filament-loading-speed=f@"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 28. }; def = this->add("filament_loading_speed_start", coFloats); @@ -517,6 +552,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->cli = "filament-loading-speed-start=f@"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 3. }; def = this->add("filament_unloading_speed", coFloats); @@ -526,6 +562,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->cli = "filament-unloading-speed=f@"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 90. }; def = this->add("filament_unloading_speed_start", coFloats); @@ -534,6 +571,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->cli = "filament-unloading-speed-start=f@"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 100. }; def = this->add("filament_toolchange_delay", coFloats); @@ -544,6 +582,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("s"); def->cli = "filament-toolchange-delay=f@"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 0. }; def = this->add("filament_cooling_moves", coInts); @@ -553,6 +592,7 @@ void PrintConfigDef::init_fff_params() def->cli = "filament-cooling-moves=i@"; def->max = 0; def->max = 20; + def->mode = comExpert; def->default_value = new ConfigOptionInts { 4 }; def = this->add("filament_cooling_initial_speed", coFloats); @@ -561,6 +601,7 @@ void PrintConfigDef::init_fff_params() def->cli = "filament-cooling-initial-speed=f@"; def->sidetext = L("mm/s"); def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 2.2f }; def = this->add("filament_minimal_purge_on_wipe_tower", coFloats); @@ -572,6 +613,7 @@ void PrintConfigDef::init_fff_params() def->cli = "filament-minimal-purge-on-wipe-tower=f@"; def->sidetext = L("mm³"); def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 15.f }; def = this->add("filament_cooling_final_speed", coFloats); @@ -580,6 +622,7 @@ void PrintConfigDef::init_fff_params() def->cli = "filament-cooling-final-speed=f@"; def->sidetext = L("mm/s"); def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 3.4f }; def = this->add("filament_load_time", coFloats); @@ -588,12 +631,14 @@ void PrintConfigDef::init_fff_params() def->cli = "filament-load-time=i@"; def->sidetext = L("s"); def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 0.0f }; def = this->add("filament_ramming_parameters", coStrings); def->label = L("Ramming parameters"); def->tooltip = L("This string is edited by RammingDialog and contains ramming specific parameters "); def->cli = "filament-ramming-parameters=s@"; + def->mode = comExpert; def->default_value = new ConfigOptionStrings { "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0|" " 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" }; @@ -603,6 +648,7 @@ void PrintConfigDef::init_fff_params() def->cli = "filament-unload-time=i@"; def->sidetext = L("s"); def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 0.0f }; def = this->add("filament_diameter", coFloats); @@ -668,6 +714,7 @@ void PrintConfigDef::init_fff_params() def->cli = "fill-angle=f"; def->min = 0; def->max = 360; + def->mode = comMiddle; def->default_value = new ConfigOptionFloat(45); def = this->add("fill_density", coPercent); @@ -751,6 +798,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s²"); def->cli = "first-layer-acceleration=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("first_layer_bed_temperature", coInts); @@ -772,6 +820,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm or % (leave 0 for default)"); def->cli = "first-layer-extrusion-width=s"; def->ratio_over = "first_layer_height"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloatOrPercent(200, true); def = this->add("first_layer_height", coFloatOrPercent); @@ -794,6 +843,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s or %"); def->cli = "first-layer-speed=s"; def->min = 0; + def->mode = comMiddle; def->default_value = new ConfigOptionFloatOrPercent(30, false); def = this->add("first_layer_temperature", coInts); @@ -821,6 +871,7 @@ void PrintConfigDef::init_fff_params() "If you print from SD card, the additional weight of the file could make your firmware " "slow down."); def->cli = "gcode-comments!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(0); def = this->add("gcode_flavor", coEnum); @@ -850,6 +901,7 @@ void PrintConfigDef::init_fff_params() def->enum_labels.push_back("Machinekit"); def->enum_labels.push_back("Smoothie"); def->enum_labels.push_back(L("No extrusion")); + def->mode = comExpert; def->default_value = new ConfigOptionEnum(gcfRepRap); def = this->add("infill_acceleration", coFloat); @@ -859,6 +911,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s²"); def->cli = "infill-acceleration=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("infill_every_layers", coInt); @@ -870,6 +923,7 @@ void PrintConfigDef::init_fff_params() def->cli = "infill-every-layers=i"; def->full_label = L("Combine infill every n layers"); def->min = 1; + def->mode = comMiddle; def->default_value = new ConfigOptionInt(1); def = this->add("infill_extruder", coInt); @@ -878,6 +932,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("The extruder to use when printing infill."); def->cli = "infill-extruder=i"; def->min = 1; + def->mode = comMiddle; def->default_value = new ConfigOptionInt(1); def = this->add("infill_extrusion_width", coFloatOrPercent); @@ -889,12 +944,14 @@ void PrintConfigDef::init_fff_params() "If expressed as percentage (for example 90%) it will be computed over layer height."); def->sidetext = L("mm or % (leave 0 for default)"); def->cli = "infill-extrusion-width=s"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloatOrPercent(0, false); def = this->add("infill_first", coBool); def->label = L("Infill before perimeters"); def->tooltip = L("This option will switch the print order of perimeters and infill, making the latter first."); def->cli = "infill-first!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("infill_only_where_needed", coBool); @@ -904,6 +961,7 @@ void PrintConfigDef::init_fff_params() "(it will act as internal support material). If enabled, slows down the G-code generation " "due to the multiple checks involved."); def->cli = "infill-only-where-needed!"; + def->mode = comMiddle; def->default_value = new ConfigOptionBool(false); def = this->add("infill_overlap", coFloatOrPercent); @@ -915,6 +973,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm or %"); def->cli = "infill-overlap=s"; def->ratio_over = "perimeter_extrusion_width"; + def->mode = comExpert; def->default_value = new ConfigOptionFloatOrPercent(25, true); def = this->add("infill_speed", coFloat); @@ -946,6 +1005,7 @@ void PrintConfigDef::init_fff_params() "support material."); def->cli = "interface-shells!"; def->category = L("Layers and Perimeters"); + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("layer_gcode", coString); @@ -957,6 +1017,7 @@ void PrintConfigDef::init_fff_params() def->multiline = true; def->full_width = true; def->height = 50; + def->mode = comExpert; def->default_value = new ConfigOptionString(""); def = this->add("remaining_times", coBool); @@ -965,11 +1026,13 @@ void PrintConfigDef::init_fff_params() " intervals into the G-code to let the firmware show accurate remaining time." " As of now only the Prusa i3 MK3 firmware recognizes M73." " Also the i3 MK3 firmware supports M73 Qxx Sxx for the silent mode."); + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("silent_mode", coBool); def->label = L("Supports silent mode"); def->tooltip = L("Set silent mode for the G-code flavor"); + def->mode = comExpert; def->default_value = new ConfigOptionBool(true); const int machine_limits_opt_width = 70; @@ -997,6 +1060,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->min = 0; def->width = machine_limits_opt_width; + def->mode = comMiddle; def->default_value = new ConfigOptionFloats(axis.max_feedrate); // Add the machine acceleration limits for XYZE axes (M201) def = this->add("machine_max_acceleration_" + axis.name, coFloats); @@ -1006,6 +1070,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s²"); def->min = 0; def->width = machine_limits_opt_width; + def->mode = comMiddle; def->default_value = new ConfigOptionFloats(axis.max_acceleration); // Add the machine jerk limits for XYZE axes (M205) def = this->add("machine_max_jerk_" + axis.name, coFloats); @@ -1015,6 +1080,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->min = 0; def->width = machine_limits_opt_width; + def->mode = comMiddle; def->default_value = new ConfigOptionFloats(axis.max_jerk); } } @@ -1027,6 +1093,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->min = 0; def->width = machine_limits_opt_width; + def->mode = comMiddle; def->default_value = new ConfigOptionFloats{ 0., 0. }; // M205 T... [mm/sec] @@ -1037,6 +1104,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->min = 0; def->width = machine_limits_opt_width; + def->mode = comMiddle; def->default_value = new ConfigOptionFloats{ 0., 0. }; // M204 S... [mm/sec^2] @@ -1047,6 +1115,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s²"); def->min = 0; def->width = machine_limits_opt_width; + def->mode = comMiddle; def->default_value = new ConfigOptionFloats{ 1500., 1250. }; // M204 T... [mm/sec^2] @@ -1057,6 +1126,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s²"); def->min = 0; def->width = machine_limits_opt_width; + def->mode = comMiddle; def->default_value = new ConfigOptionFloats{ 1500., 1250. }; def = this->add("max_fan_speed", coInts); @@ -1066,6 +1136,7 @@ void PrintConfigDef::init_fff_params() def->cli = "max-fan-speed=i@"; def->min = 0; def->max = 100; + def->mode = comExpert; def->default_value = new ConfigOptionInts { 100 }; def = this->add("max_layer_height", coFloats); @@ -1077,6 +1148,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->cli = "max-layer-height=f@"; def->min = 0; + def->mode = comMiddle; def->default_value = new ConfigOptionFloats { 0. }; def = this->add("max_print_speed", coFloat); @@ -1087,6 +1159,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->cli = "max-print-speed=f"; def->min = 1; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(80); def = this->add("max_volumetric_speed", coFloat); @@ -1096,6 +1169,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm³/s"); def->cli = "max-volumetric-speed=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("max_volumetric_extrusion_rate_slope_positive", coFloat); @@ -1107,6 +1181,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm³/s²"); def->cli = "max-volumetric-extrusion-rate-slope-positive=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("max_volumetric_extrusion_rate_slope_negative", coFloat); @@ -1118,6 +1193,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm³/s²"); def->cli = "max-volumetric-extrusion-rate-slope-negative=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("min_fan_speed", coInts); @@ -1127,6 +1203,7 @@ void PrintConfigDef::init_fff_params() def->cli = "min-fan-speed=i@"; def->min = 0; def->max = 100; + def->mode = comExpert; def->default_value = new ConfigOptionInts { 35 }; def = this->add("min_layer_height", coFloats); @@ -1136,6 +1213,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->cli = "min-layer-height=f@"; def->min = 0; + def->mode = comMiddle; def->default_value = new ConfigOptionFloats { 0.07 }; def = this->add("min_print_speed", coFloats); @@ -1144,6 +1222,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->cli = "min-print-speed=f@"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 10. }; def = this->add("min_skirt_length", coFloat); @@ -1154,6 +1233,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->cli = "min-skirt-length=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("notes", coString); @@ -1164,6 +1244,7 @@ void PrintConfigDef::init_fff_params() def->multiline = true; def->full_width = true; def->height = 130; + def->mode = comMiddle; def->default_value = new ConfigOptionString(""); def = this->add("nozzle_diameter", coFloats); @@ -1183,6 +1264,7 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("duet"); def->enum_labels.push_back("OctoPrint"); def->enum_labels.push_back("Duet"); + def->mode = comMiddle; def->default_value = new ConfigOptionEnum(htOctoPrint); def = this->add("printhost_apikey", coString); @@ -1190,6 +1272,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain " "the API Key or the password required for authentication."); def->cli = "printhost-apikey=s"; + def->mode = comMiddle; def->default_value = new ConfigOptionString(""); def = this->add("printhost_cafile", coString); @@ -1204,6 +1287,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain " "the hostname, IP address or URL of the printer host instance."); def->cli = "print-host=s"; + def->mode = comMiddle; def->default_value = new ConfigOptionString(""); def = this->add("only_retract_when_crossing_perimeters", coBool); @@ -1211,6 +1295,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Disables retraction when the travel path does not exceed the upper layer's perimeters " "(and thus any ooze will be probably invisible)."); def->cli = "only-retract-when-crossing-perimeters!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(true); def = this->add("ooze_prevention", coBool); @@ -1219,6 +1304,7 @@ void PrintConfigDef::init_fff_params() "It will enable a tall skirt automatically and move extruders outside such " "skirt when changing temperatures."); def->cli = "ooze-prevention!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("output_filename_format", coString); @@ -1229,6 +1315,7 @@ void PrintConfigDef::init_fff_params() "[input_filename_base]."); def->cli = "output-filename-format=s"; def->full_width = true; + def->mode = comExpert; def->default_value = new ConfigOptionString("[input_filename_base].gcode"); def = this->add("overhangs", coBool); @@ -1237,6 +1324,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Experimental option to adjust flow for overhangs (bridge flow will be used), " "to apply bridge speed to them and enable fan."); def->cli = "overhangs!"; + def->mode = comMiddle; def->default_value = new ConfigOptionBool(true); def = this->add("parking_pos_retraction", coFloat); @@ -1246,6 +1334,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->cli = "parking_pos_retraction=f"; def->min = 0; + def->mode = comMiddle; def->default_value = new ConfigOptionFloat(92.f); def = this->add("extra_loading_move", coFloat); @@ -1255,6 +1344,7 @@ void PrintConfigDef::init_fff_params() " if negative, the loading move is shorter than unloading. "); def->sidetext = L("mm"); def->cli = "extra_loading_move=f"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloat(-2.f); def = this->add("perimeter_acceleration", coFloat); @@ -1264,6 +1354,7 @@ void PrintConfigDef::init_fff_params() "Set zero to disable acceleration control for perimeters."); def->sidetext = L("mm/s²"); def->cli = "perimeter-acceleration=f"; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("perimeter_extruder", coInt); @@ -1273,6 +1364,7 @@ void PrintConfigDef::init_fff_params() def->cli = "perimeter-extruder=i"; def->aliases = { "perimeters_extruder" }; def->min = 1; + def->mode = comMiddle; def->default_value = new ConfigOptionInt(1); def = this->add("perimeter_extrusion_width", coFloatOrPercent); @@ -1285,6 +1377,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm or % (leave 0 for default)"); def->cli = "perimeter-extrusion-width=s"; def->aliases = { "perimeters_extrusion_width" }; + def->mode = comMiddle; def->default_value = new ConfigOptionFloatOrPercent(0, false); def = this->add("perimeter_speed", coFloat); @@ -1321,6 +1414,7 @@ void PrintConfigDef::init_fff_params() def->multiline = true; def->full_width = true; def->height = 60; + def->mode = comExpert; def->default_value = new ConfigOptionStrings(); def = this->add("printer_model", coString); @@ -1335,6 +1429,7 @@ void PrintConfigDef::init_fff_params() def->multiline = true; def->full_width = true; def->height = 130; + def->mode = comMiddle; def->default_value = new ConfigOptionString(""); def = this->add("printer_vendor", coString); @@ -1361,6 +1456,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("layers"); def->cli = "raft-layers=i"; def->min = 0; + def->mode = comMiddle; def->default_value = new ConfigOptionInt(0); def = this->add("resolution", coFloat); @@ -1372,6 +1468,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->cli = "resolution=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("retract_before_travel", coFloats); @@ -1379,6 +1476,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Retraction is not triggered when travel moves are shorter than this length."); def->sidetext = L("mm"); def->cli = "retract-before-travel=f@"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloats { 2. }; def = this->add("retract_before_wipe", coPercents); @@ -1387,12 +1485,14 @@ void PrintConfigDef::init_fff_params() "before doing the wipe movement."); def->sidetext = L("%"); def->cli = "retract-before-wipe=s@"; + def->mode = comMiddle; def->default_value = new ConfigOptionPercents { 0. }; def = this->add("retract_layer_change", coBools); def->label = L("Retract on layer change"); def->tooltip = L("This flag enforces a retraction whenever a Z move is done."); def->cli = "retract-layer-change!"; + def->mode = comMiddle; def->default_value = new ConfigOptionBools { false }; def = this->add("retract_length", coFloats); @@ -1412,6 +1512,7 @@ void PrintConfigDef::init_fff_params() "the extruder)."); def->sidetext = L("mm (zero to disable)"); def->cli = "retract-length-toolchange=f@"; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 10. }; def = this->add("retract_lift", coFloats); @@ -1430,6 +1531,7 @@ void PrintConfigDef::init_fff_params() "absolute Z. You can tune this setting for skipping lift on the first layers."); def->sidetext = L("mm"); def->cli = "retract-lift-above=f@"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloats { 0. }; def = this->add("retract_lift_below", coFloats); @@ -1440,6 +1542,7 @@ void PrintConfigDef::init_fff_params() "to the first layers."); def->sidetext = L("mm"); def->cli = "retract-lift-below=f@"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloats { 0. }; def = this->add("retract_restart_extra", coFloats); @@ -1448,6 +1551,7 @@ void PrintConfigDef::init_fff_params() "this additional amount of filament. This setting is rarely needed."); def->sidetext = L("mm"); def->cli = "retract-restart-extra=f@"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloats { 0. }; def = this->add("retract_restart_extra_toolchange", coFloats); @@ -1456,6 +1560,7 @@ void PrintConfigDef::init_fff_params() "this additional amount of filament."); def->sidetext = L("mm"); def->cli = "retract-restart-extra-toolchange=f@"; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 0. }; def = this->add("retract_speed", coFloats); @@ -1464,6 +1569,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("The speed for retractions (it only applies to the extruder motor)."); def->sidetext = L("mm/s"); def->cli = "retract-speed=f@"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloats { 40. }; def = this->add("deretract_speed", coFloats); @@ -1473,6 +1579,7 @@ void PrintConfigDef::init_fff_params() "(it only applies to the extruder motor). If left to zero, the retraction speed is used."); def->sidetext = L("mm/s"); def->cli = "retract-speed=f@"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloats { 0. }; def = this->add("seam_position", coEnum); @@ -1489,6 +1596,7 @@ void PrintConfigDef::init_fff_params() def->enum_labels.push_back(L("Nearest")); def->enum_labels.push_back(L("Aligned")); def->enum_labels.push_back(L("Rear")); + def->mode = comMiddle; def->default_value = new ConfigOptionEnum(spAligned); #if 0 @@ -1551,6 +1659,7 @@ void PrintConfigDef::init_fff_params() "as a shield against drafts."); def->sidetext = L("layers"); def->cli = "skirt-height=i"; + def->mode = comMiddle; def->default_value = new ConfigOptionInt(1); def = this->add("skirts", coInt); @@ -1561,6 +1670,7 @@ void PrintConfigDef::init_fff_params() "to disable skirt completely."); def->cli = "skirts=i"; def->min = 0; + def->mode = comMiddle; def->default_value = new ConfigOptionInt(1); def = this->add("slowdown_below_layer_time", coInts); @@ -1572,6 +1682,7 @@ void PrintConfigDef::init_fff_params() def->width = 60; def->min = 0; def->max = 1000; + def->mode = comExpert; def->default_value = new ConfigOptionInts { 5 }; def = this->add("small_perimeter_speed", coFloatOrPercent); @@ -1593,6 +1704,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm²"); def->cli = "solid-infill-below-area=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(70); def = this->add("solid_infill_extruder", coInt); @@ -1601,6 +1713,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("The extruder to use when printing solid infill."); def->cli = "solid-infill-extruder=i"; def->min = 1; + def->mode = comMiddle; def->default_value = new ConfigOptionInt(1); def = this->add("solid_infill_every_layers", coInt); @@ -1613,6 +1726,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("layers"); def->cli = "solid-infill-every-layers=i"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionInt(0); def = this->add("solid_infill_extrusion_width", coFloatOrPercent); @@ -1623,6 +1737,7 @@ void PrintConfigDef::init_fff_params() "If expressed as percentage (for example 90%) it will be computed over layer height."); def->sidetext = L("mm or % (leave 0 for default)"); def->cli = "solid-infill-extrusion-width=s"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloatOrPercent(0, false); def = this->add("solid_infill_speed", coFloatOrPercent); @@ -1664,6 +1779,7 @@ void PrintConfigDef::init_fff_params() def->cli = "standby-temperature-delta=i"; def->min = -max_temp; def->max = max_temp; + def->mode = comExpert; def->default_value = new ConfigOptionInt(-5); def = this->add("start_gcode", coString); @@ -1679,6 +1795,7 @@ void PrintConfigDef::init_fff_params() def->multiline = true; def->full_width = true; def->height = 120; + def->mode = comExpert; def->default_value = new ConfigOptionString("G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n"); def = this->add("start_filament_gcode", coStrings); @@ -1695,18 +1812,21 @@ void PrintConfigDef::init_fff_params() def->multiline = true; def->full_width = true; def->height = 120; + def->mode = comExpert; def->default_value = new ConfigOptionStrings { "; Filament gcode\n" }; def = this->add("single_extruder_multi_material", coBool); def->label = L("Single Extruder Multi Material"); def->tooltip = L("The printer multiplexes filaments into a single hot end."); def->cli = "single-extruder-multi-material!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("single_extruder_multi_material_priming", coBool); def->label = L("Prime all printing extruders"); def->tooltip = L("If enabled, all printing extruders will be primed at the front edge of the print bed at the start of the print."); def->cli = "single-extruder-multi-material-priming!"; + def->mode = comMiddle; def->default_value = new ConfigOptionBool(true); def = this->add("support_material", coBool); @@ -1722,6 +1842,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("If checked, supports will be generated automatically based on the overhang threshold value."\ " If unchecked, supports will be generated inside the \"Support Enforcer\" volumes only."); def->cli = "support-material-auto!"; + def->mode = comMiddle; def->default_value = new ConfigOptionBool(true); def = this->add("support_material_xy_spacing", coFloatOrPercent); @@ -1733,6 +1854,7 @@ void PrintConfigDef::init_fff_params() def->cli = "support-material-xy-spacing=s"; def->ratio_over = "external_perimeter_extrusion_width"; def->min = 0; + def->mode = comMiddle; // Default is half the external perimeter width. def->default_value = new ConfigOptionFloatOrPercent(50, true); @@ -1744,6 +1866,7 @@ void PrintConfigDef::init_fff_params() def->cli = "support-material-angle=f"; def->min = 0; def->max = 359; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("support_material_buildplate_only", coBool); @@ -1751,6 +1874,7 @@ void PrintConfigDef::init_fff_params() def->category = L("Support material"); def->tooltip = L("Only create support if it lies on a build plate. Don't create support on a print."); def->cli = "support-material-buildplate-only!"; + def->mode = comMiddle; def->default_value = new ConfigOptionBool(false); def = this->add("support_material_contact_distance", coFloat); @@ -1767,6 +1891,7 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("0.2"); def->enum_labels.push_back((boost::format("0 (%1%)") % L("soluble")).str()); def->enum_labels.push_back((boost::format("0.2 (%1%)") % L("detachable")).str()); + def->mode = comMiddle; def->default_value = new ConfigOptionFloat(0.2); def = this->add("support_material_enforce_layers", coInt); @@ -1780,6 +1905,7 @@ void PrintConfigDef::init_fff_params() def->cli = "support-material-enforce-layers=f"; def->full_label = L("Enforce support for the first n layers"); def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionInt(0); def = this->add("support_material_extruder", coInt); @@ -1789,6 +1915,7 @@ void PrintConfigDef::init_fff_params() "(1+, 0 to use the current extruder to minimize tool changes)."); def->cli = "support-material-extruder=i"; def->min = 0; + def->mode = comMiddle; def->default_value = new ConfigOptionInt(1); def = this->add("support_material_extrusion_width", coFloatOrPercent); @@ -1799,6 +1926,7 @@ void PrintConfigDef::init_fff_params() "If expressed as percentage (for example 90%) it will be computed over layer height."); def->sidetext = L("mm or % (leave 0 for default)"); def->cli = "support-material-extrusion-width=s"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloatOrPercent(0, false); def = this->add("support_material_interface_contact_loops", coBool); @@ -1806,6 +1934,7 @@ void PrintConfigDef::init_fff_params() def->category = L("Support material"); def->tooltip = L("Cover the top contact layer of the supports with loops. Disabled by default."); def->cli = "support-material-interface-contact-loops!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("support_material_interface_extruder", coInt); @@ -1815,6 +1944,7 @@ void PrintConfigDef::init_fff_params() "(1+, 0 to use the current extruder to minimize tool changes). This affects raft too."); def->cli = "support-material-interface-extruder=i"; def->min = 0; + def->mode = comMiddle; def->default_value = new ConfigOptionInt(1); def = this->add("support_material_interface_layers", coInt); @@ -1824,6 +1954,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("layers"); def->cli = "support-material-interface-layers=i"; def->min = 0; + def->mode = comMiddle; def->default_value = new ConfigOptionInt(3); def = this->add("support_material_interface_spacing", coFloat); @@ -1833,6 +1964,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->cli = "support-material-interface-spacing=f"; def->min = 0; + def->mode = comMiddle; def->default_value = new ConfigOptionFloat(0); def = this->add("support_material_interface_speed", coFloatOrPercent); @@ -1858,6 +1990,7 @@ void PrintConfigDef::init_fff_params() def->enum_labels.push_back(L("Rectilinear")); def->enum_labels.push_back(L("Rectilinear grid")); def->enum_labels.push_back(L("Honeycomb")); + def->mode = comMiddle; def->default_value = new ConfigOptionEnum(smpRectilinear); def = this->add("support_material_spacing", coFloat); @@ -1867,6 +2000,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->cli = "support-material-spacing=f"; def->min = 0; + def->mode = comMiddle; def->default_value = new ConfigOptionFloat(2.5); def = this->add("support_material_speed", coFloat); @@ -1884,6 +2018,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Synchronize support layers with the object print layers. This is useful " "with multi-material printers, where the extruder switch is expensive."); def->cli = "support-material-synchronize-layers!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("support_material_threshold", coInt); @@ -1898,6 +2033,7 @@ void PrintConfigDef::init_fff_params() def->cli = "support-material-threshold=i"; def->min = 0; def->max = 90; + def->mode = comMiddle; def->default_value = new ConfigOptionInt(0); def = this->add("support_material_with_sheath", coBool); @@ -1906,6 +2042,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Add a sheath (a single perimeter line) around the base support. This makes " "the support more reliable, but also more difficult to remove."); def->cli = "support-material-with-sheath!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(true); def = this->add("temperature", coInts); @@ -1924,6 +2061,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Detect single-width walls (parts where two extrusions don't fit and we need " "to collapse them into a single trace)."); def->cli = "thin-walls!"; + def->mode = comMiddle; def->default_value = new ConfigOptionBool(true); def = this->add("threads", coInt); @@ -1947,6 +2085,7 @@ void PrintConfigDef::init_fff_params() def->multiline = true; def->full_width = true; def->height = 50; + def->mode = comExpert; def->default_value = new ConfigOptionString(""); def = this->add("top_infill_extrusion_width", coFloatOrPercent); @@ -1958,6 +2097,7 @@ void PrintConfigDef::init_fff_params() "If expressed as percentage (for example 90%) it will be computed over layer height."); def->sidetext = L("mm or % (leave 0 for default)"); def->cli = "top-infill-extrusion-width=s"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloatOrPercent(0, false); def = this->add("top_solid_infill_speed", coFloatOrPercent); @@ -1990,6 +2130,7 @@ void PrintConfigDef::init_fff_params() def->cli = "travel-speed=f"; def->aliases = { "travel_feed_rate" }; def->min = 1; + def->mode = comMiddle; def->default_value = new ConfigOptionFloat(130); def = this->add("use_firmware_retraction", coBool); @@ -1997,6 +2138,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("This experimental setting uses G10 and G11 commands to have the firmware " "handle the retraction. This is only supported in recent Marlin."); def->cli = "use-firmware-retraction!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("use_relative_e_distances", coBool); @@ -2004,6 +2146,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("If your firmware requires relative E values, check this, " "otherwise leave it unchecked. Most firmwares use absolute values."); def->cli = "use-relative-e-distances!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("use_volumetric_e", coBool); @@ -2015,6 +2158,7 @@ void PrintConfigDef::init_fff_params() "diameter associated to the filament selected in Slic3r. This is only supported " "in recent Marlin."); def->cli = "use-volumetric-e!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("variable_layer_height", coBool); @@ -2022,6 +2166,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Some printers or printer setups may have difficulties printing " "with a variable layer height. Enabled by default."); def->cli = "variable-layer-height!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(true); def = this->add("wipe", coBools); @@ -2029,6 +2174,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("This flag will move the nozzle while retracting to minimize the possible blob " "on leaky extruders."); def->cli = "wipe!"; + def->mode = comMiddle; def->default_value = new ConfigOptionBools { false }; def = this->add("wipe_tower", coBool); @@ -2036,6 +2182,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Multi material printers may need to prime or purge extruders on tool changes. " "Extrude the excess material into the wipe tower."); def->cli = "wipe-tower!"; + def->mode = comMiddle; def->default_value = new ConfigOptionBool(false); def = this->add("wiping_volumes_extruders", coFloats); @@ -2062,6 +2209,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("X coordinate of the left front corner of a wipe tower"); def->sidetext = L("mm"); def->cli = "wipe-tower-x=f"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloat(180.); def = this->add("wipe_tower_y", coFloat); @@ -2069,6 +2217,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Y coordinate of the left front corner of a wipe tower"); def->sidetext = L("mm"); def->cli = "wipe-tower-y=f"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloat(140.); def = this->add("wipe_tower_width", coFloat); @@ -2076,6 +2225,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Width of a wipe tower"); def->sidetext = L("mm"); def->cli = "wipe-tower-width=f"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloat(60.); def = this->add("wipe_tower_rotation_angle", coFloat); @@ -2083,6 +2233,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Wipe tower rotation angle with respect to x-axis "); def->sidetext = L("degrees"); def->cli = "wipe-tower-rotation-angle=f"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloat(0.); def = this->add("wipe_into_infill", coBool); @@ -2108,6 +2259,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Maximal distance between supports on sparse infill sections. "); def->sidetext = L("mm"); def->cli = "wipe-tower-bridging=f"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloat(10.); def = this->add("xy_size_compensation", coFloat); @@ -2118,6 +2270,7 @@ void PrintConfigDef::init_fff_params() "for fine-tuning hole sizes."); def->sidetext = L("mm"); def->cli = "xy-size-compensation=f"; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("z_offset", coFloat); @@ -2128,6 +2281,7 @@ void PrintConfigDef::init_fff_params() "from the print bed, set this to -0.3 (or fix your endstop)."); def->sidetext = L("mm"); def->cli = "z-offset=f"; + def->mode = comMiddle; def->default_value = new ConfigOptionFloat(0); def = this->add("bed_size_x", coFloat); @@ -2150,6 +2304,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("px"); def->cli = "pixel-width=i"; def->min = 1; + def->mode = comExpert; def->default_value = new ConfigOptionInt(1440); def = this->add("pixel_height", coInt); @@ -2262,6 +2417,7 @@ void PrintConfigDef::init_sla_params() def->multiline = true; def->full_width = true; def->height = 130; + def->mode = comMiddle; def->default_value = new ConfigOptionString(""); def = this->add("default_sla_material_profile", coString); diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 4c653e543..d80d6df0d 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -10,6 +10,8 @@ #define ENABLE_USE_UNIQUE_GLCONTEXT (1 && ENABLE_1_42_0) // New selections #define ENABLE_EXTENDED_SELECTION (1 && ENABLE_1_42_0) +// Add mirror components along the three axes in ModelInstance and GLVolume +#define ENABLE_MIRROR (1 && ENABLE_1_42_0) #endif // _technologies_h_ diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 402d7b8ee..91f60947c 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -198,6 +198,9 @@ GLVolume::GLVolume(float r, float g, float b, float a) : m_offset(Vec3d::Zero()) , m_rotation(Vec3d::Zero()) , m_scaling_factor(Vec3d::Ones()) +#if ENABLE_MIRROR + , m_mirror(Vec3d::Ones()) +#endif // ENABLE_MIRROR , m_world_matrix(Transform3f::Identity()) , m_world_matrix_dirty(true) , m_transformed_bounding_box_dirty(true) @@ -324,6 +327,40 @@ void GLVolume::set_scaling_factor(const Vec3d& scaling_factor) } } +#if ENABLE_MIRROR +const Vec3d& GLVolume::get_mirror() const +{ + return m_mirror; +} + +double GLVolume::get_mirror(Axis axis) const +{ + return m_mirror(axis); +} + +void GLVolume::set_mirror(const Vec3d& mirror) +{ + if (m_mirror != mirror) + { + m_mirror = mirror; + m_world_matrix_dirty = true; + m_transformed_bounding_box_dirty = true; + m_transformed_convex_hull_bounding_box_dirty = true; + } +} + +void GLVolume::set_mirror(Axis axis, double mirror) +{ + if (m_mirror(axis) != mirror) + { + m_mirror(axis) = mirror; + m_world_matrix_dirty = true; + m_transformed_bounding_box_dirty = true; + m_transformed_convex_hull_bounding_box_dirty = true; + } +} +#endif // ENABLE_MIRROR + void GLVolume::set_convex_hull(const TriangleMesh& convex_hull) { m_convex_hull = &convex_hull; @@ -353,7 +390,11 @@ const Transform3f& GLVolume::world_matrix() const { if (m_world_matrix_dirty) { +#if ENABLE_MIRROR + m_world_matrix = Geometry::assemble_transform(m_offset, m_rotation, m_scaling_factor, m_mirror).cast(); +#else m_world_matrix = Geometry::assemble_transform(m_offset, m_rotation, m_scaling_factor).cast(); +#endif // ENABLE_MIRROR m_world_matrix_dirty = false; } return m_world_matrix; @@ -729,6 +770,9 @@ std::vector GLVolumeCollection::load_object( v.set_offset(instance->get_offset()); v.set_rotation(instance->get_rotation()); v.set_scaling_factor(instance->get_scaling_factor()); +#if ENABLE_MIRROR + v.set_mirror(instance->get_mirror()); +#endif // ENABLE_MIRROR } } @@ -2076,6 +2120,15 @@ int _3DScene::get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx) return s_canvas_mgr.get_in_object_volume_id(canvas, scene_vol_idx); } +#if ENABLE_MIRROR +#if ENABLE_EXTENDED_SELECTION +void _3DScene::mirror_selection(wxGLCanvas* canvas, Axis axis) +{ + s_canvas_mgr.mirror_selection(canvas, axis); +} +#endif // ENABLE_EXTENDED_SELECTION +#endif // ENABLE_MIRROR + void _3DScene::reload_scene(wxGLCanvas* canvas, bool force) { s_canvas_mgr.reload_scene(canvas, force); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index e3f7ea0d3..7e74ff92f 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -260,6 +260,10 @@ private: Vec3d m_rotation; // Scale factor along the three axes of the volume to be rendered. Vec3d m_scaling_factor; +#if ENABLE_MIRROR + // Mirroring along the three axes of the volume to be rendered. + Vec3d m_mirror; +#endif // ENABLE_MIRROR // World matrix of the volume to be rendered. mutable Transform3f m_world_matrix; // Whether or not is needed to recalculate the world matrix. @@ -337,6 +341,13 @@ public: #endif // ENABLE_EXTENDED_SELECTION void set_scaling_factor(const Vec3d& scaling_factor); +#if ENABLE_MIRROR + const Vec3d& get_mirror() const; + double get_mirror(Axis axis) const; + void set_mirror(const Vec3d& mirror); + void set_mirror(Axis axis, double mirror); +#endif // ENABLE_MIRROR + const Vec3d& get_offset() const; void set_offset(const Vec3d& offset); @@ -581,6 +592,12 @@ public: static int get_first_volume_id(wxGLCanvas* canvas, int obj_idx); static int get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx); +#if ENABLE_MIRROR +#if ENABLE_EXTENDED_SELECTION + static void mirror_selection(wxGLCanvas* canvas, Axis axis); +#endif // ENABLE_EXTENDED_SELECTION +#endif // ENABLE_MIRROR + static void reload_scene(wxGLCanvas* canvas, bool force); static void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector& str_tool_colors); diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index c38658e2b..58c6afebe 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -54,10 +54,13 @@ public: virtual bool AcceptsFocusFromKeyboard() const { return false; } + void set_as_hidden() { + Hide(); + hidden = true; + } + virtual bool Show(bool show = true) override { - if (!show) - hidden = true; - return wxButton::Show(!hidden); + return wxButton::Show(hidden ? false : show); } }; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e8c79b176..7d47ff8f6 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1409,14 +1409,13 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation) if (!m_valid) return; - Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); - for (unsigned int i : m_list) { if (is_single_full_instance()) (*m_volumes)[i]->set_rotation(rotation); else { + Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); // extracts rotations from the composed transformation Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_rotation_matrix()); @@ -1436,15 +1435,13 @@ void GLCanvas3D::Selection::scale(const Vec3d& scale) if (!m_valid) return; - Transform3d m = Transform3d::Identity(); - m.scale(scale); - for (unsigned int i : m_list) { if (is_single_full_instance()) (*m_volumes)[i]->set_scaling_factor(scale); else { + Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale); Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_scale_matrix()).matrix().block(0, 0, 3, 3); // extracts scaling factors from the composed transformation Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); @@ -1460,6 +1457,25 @@ void GLCanvas3D::Selection::scale(const Vec3d& scale) m_bounding_box_dirty = true; } +#if ENABLE_MIRROR +void GLCanvas3D::Selection::mirror(Axis axis) +{ + if (!m_valid) + return; + + for (unsigned int i : m_list) + { + if (is_single_full_instance()) + (*m_volumes)[i]->set_mirror(axis, -(*m_volumes)[i]->get_mirror(axis)); + } + + if (m_mode == Instance) + _synchronize_unselected_instances(); + + m_bounding_box_dirty = true; +} +#endif // ENABLE_MIRROR + void GLCanvas3D::Selection::render(bool show_indirect_selection) const { if (is_empty()) @@ -1783,6 +1799,9 @@ void GLCanvas3D::Selection::_synchronize_unselected_instances() int instance_idx = volume->instance_idx(); const Vec3d& rotation = volume->get_rotation(); const Vec3d& scaling_factor = volume->get_scaling_factor(); +#if ENABLE_MIRROR + const Vec3d& mirror = volume->get_mirror(); +#endif // ENABLE_MIRROR // Process unselected instances. for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) @@ -1799,6 +1818,9 @@ void GLCanvas3D::Selection::_synchronize_unselected_instances() v->set_rotation(rotation); v->set_scaling_factor(scaling_factor); +#if ENABLE_MIRROR + v->set_mirror(mirror); +#endif // ENABLE_MIRROR done.insert(j); } @@ -2674,9 +2696,11 @@ wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent); wxDEFINE_EVENT(EVT_GLCANVAS_MODEL_UPDATE, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent); +#if !ENABLE_EXTENDED_SELECTION wxDEFINE_EVENT(EVT_GLCANVAS_ROTATE_OBJECT, Event); wxDEFINE_EVENT(EVT_GLCANVAS_SCALE_UNIFORMLY, SimpleEvent); -wxDEFINE_EVENT(EVT_GLCANVAS_INCREASE_OBJECTS, Event); +#endif // !ENABLE_EXTENDED_SELECTION +wxDEFINE_EVENT(EVT_GLCANVAS_INCREASE_INSTANCES, Event); wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_MOVED, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event); @@ -3404,6 +3428,17 @@ int GLCanvas3D::get_in_object_volume_id(int scene_vol_idx) const return ((0 <= scene_vol_idx) && (scene_vol_idx < (int)m_volumes.volumes.size())) ? m_volumes.volumes[scene_vol_idx]->volume_idx() : -1; } +#if ENABLE_MIRROR +#if ENABLE_EXTENDED_SELECTION +void GLCanvas3D::mirror_selection(Axis axis) +{ + m_selection.mirror(axis); + _on_mirror(); + wxGetApp().obj_manipul()->update_settings_value(m_selection); +} +#endif // ENABLE_EXTENDED_SELECTION +#endif // ENABLE_MIRROR + void GLCanvas3D::reload_scene(bool force) { if ((m_canvas == nullptr) || (m_config == nullptr) || (m_model == nullptr)) @@ -3421,7 +3456,12 @@ void GLCanvas3D::reload_scene(bool force) #if ENABLE_EXTENDED_SELECTION if (m_regenerate_volumes) + { reset_volumes(); + + // to update the toolbar + post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); + } #endif // ENABLE_EXTENDED_SELECTION set_bed_shape(dynamic_cast(m_config->option("bed_shape"))->values); @@ -3441,9 +3481,6 @@ void GLCanvas3D::reload_scene(bool force) { load_object(*m_model, obj_idx); } - - // to update the toolbar - post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); } update_gizmos_data(); @@ -3685,24 +3722,26 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) switch (keyCode) { // key + - case 43: { post_event(Event(EVT_GLCANVAS_INCREASE_OBJECTS, +1)); break; } + case 43: { post_event(Event(EVT_GLCANVAS_INCREASE_INSTANCES, +1)); break; } // key - - case 45: { post_event(Event(EVT_GLCANVAS_INCREASE_OBJECTS, -1)); break; } + case 45: { post_event(Event(EVT_GLCANVAS_INCREASE_INSTANCES, -1)); break; } // key A/a case 65: case 97: { post_event(SimpleEvent(EVT_GLCANVAS_ARRANGE)); break; } // key B/b case 66: case 98: { zoom_to_bed(); break; } +#if !ENABLE_EXTENDED_SELECTION // key L/l case 76: case 108: { post_event(Event(EVT_GLCANVAS_ROTATE_OBJECT, -1)); break; } - // key R/r + // key R/r case 82: case 114: { post_event(Event(EVT_GLCANVAS_ROTATE_OBJECT, +1)); break; } - // key S/s + // key S/s case 83: case 115: { post_event(SimpleEvent(EVT_GLCANVAS_SCALE_UNIFORMLY)); break; } +#endif // !ENABLE_EXTENDED_SELECTION // key Z/z case 90: case 122: { zoom_to_volumes(); break; } @@ -3820,7 +3859,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) case Gizmos::Scale: { #if ENABLE_EXTENDED_SELECTION - m_regenerate_volumes = false; m_selection.scale(m_gizmos.get_scale()); _on_scale(); #else @@ -3837,7 +3875,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) case Gizmos::Rotate: { #if ENABLE_EXTENDED_SELECTION - m_regenerate_volumes = false; m_selection.rotate(m_gizmos.get_rotation()); _on_rotate(); #else @@ -3903,9 +3940,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } #endif // ENABLE_EXTENDED_SELECTION #if ENABLE_EXTENDED_SELECTION - else if (!m_selection.is_empty() && m_gizmos.grabber_contains_mouse()) + else if (evt.LeftDown() && !m_selection.is_empty() && m_gizmos.grabber_contains_mouse()) #else - else if ((selected_object_idx != -1) && m_gizmos.grabber_contains_mouse()) + else if (evt.LeftDown() && (selected_object_idx != -1) && m_gizmos.grabber_contains_mouse()) #endif // ENABLE_EXTENDED_SELECTION { update_gizmos_data(); @@ -3920,7 +3957,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_gizmos.get_current_type() == Gizmos::Flatten) { // Rotate the object so the normal points downward: #if ENABLE_EXTENDED_SELECTION - m_regenerate_volumes = false; m_selection.rotate(m_gizmos.get_flattening_rotation()); _on_flatten(); wxGetApp().obj_manipul()->update_settings_value(m_selection); @@ -3951,14 +3987,15 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) #if ENABLE_EXTENDED_SELECTION if (evt.LeftDown() && (m_hover_volume_id != -1)) { - if (evt.ControlDown()) - m_selection.remove(m_hover_volume_id); - else + if (!evt.ShiftDown() || !m_selection.contains_volume(m_hover_volume_id)) m_selection.add(m_hover_volume_id, !evt.ShiftDown()); + else + m_selection.remove(m_hover_volume_id); m_gizmos.update_on_off_state(m_selection); update_gizmos_data(); wxGetApp().obj_manipul()->update_settings_value(m_selection); + post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); m_dirty = true; } #else @@ -3983,16 +4020,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } // propagate event through callback -#if ENABLE_EXTENDED_SELECTION - if (m_picking_enabled && (m_hover_volume_id != -1)) - { - int object_idx = m_selection.get_object_idx(); - _on_select(m_hover_volume_id, object_idx); - } -#else +#if !ENABLE_EXTENDED_SELECTION if (m_picking_enabled && (volume_idx != -1)) _on_select(volume_idx, selected_object_idx); -#endif // ENABLE_EXTENDED_SELECTION +#endif // !ENABLE_EXTENDED_SELECTION #if ENABLE_EXTENDED_SELECTION if (m_hover_volume_id != -1) @@ -4319,10 +4350,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) #if ENABLE_EXTENDED_SELECTION m_selection.clear(); wxGetApp().obj_manipul()->update_settings_value(m_selection); + post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); #else deselect_volumes(); -#endif // ENABLE_EXTENDED_SELECTION _on_select(-1, -1); +#endif // ENABLE_EXTENDED_SELECTION update_gizmos_data(); } #if ENABLE_GIZMOS_RESET @@ -4363,7 +4395,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) case Gizmos::Scale: { #if ENABLE_EXTENDED_SELECTION - m_regenerate_volumes = false; _on_scale(); #endif // ENABLE_EXTENDED_SELECTION break; @@ -4371,7 +4402,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) case Gizmos::Rotate: { #if ENABLE_EXTENDED_SELECTION - m_regenerate_volumes = false; _on_rotate(); #else post_event(Vec3dEvent(EVT_GIZMO_ROTATE, m_gizmos.get_rotation())); @@ -6524,6 +6554,41 @@ void GLCanvas3D::_on_flatten() _on_rotate(); } +#if ENABLE_MIRROR +void GLCanvas3D::_on_mirror() +{ + if (m_model == nullptr) + return; + + std::set> done; // prevent mirroring instances twice + + for (const GLVolume* v : m_volumes.volumes) + { + int object_idx = v->object_idx(); + if (object_idx >= 1000) + continue; + + int instance_idx = v->instance_idx(); + + // prevent mirroring instances twice + std::pair done_id(object_idx, instance_idx); + if (done.find(done_id) != done.end()) + continue; + + done.insert(done_id); + + // Mirror instances. + ModelObject* model_object = m_model->objects[object_idx]; + if (model_object != nullptr) + { + model_object->instances[instance_idx]->set_mirror(v->get_mirror()); + model_object->invalidate_bounding_box(); + } + } + + // schedule_background_process +} +#endif // ENABLE_MIRROR #else void GLCanvas3D::_on_move(const std::vector& volume_idxs) { @@ -6572,11 +6637,9 @@ void GLCanvas3D::_on_move(const std::vector& volume_idxs) } #endif // ENABLE_EXTENDED_SELECTION +#if !ENABLE_EXTENDED_SELECTION void GLCanvas3D::_on_select(int volume_idx, int object_idx) { -#if ENABLE_EXTENDED_SELECTION - post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); -#else int vol_id = -1; int obj_id = -1; @@ -6605,8 +6668,8 @@ void GLCanvas3D::_on_select(int volume_idx, int object_idx) post_event(ObjectSelectEvent(obj_id, vol_id)); wxGetApp().obj_list()->select_current_volume(obj_id, vol_id); -#endif // !ENABLE_EXTENDED_SELECTION } +#endif // !ENABLE_EXTENDED_SELECTION std::vector GLCanvas3D::_parse_colors(const std::vector& colors) { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index be9548a49..c26dae098 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -112,9 +112,11 @@ wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent); wxDECLARE_EVENT(EVT_GLCANVAS_MODEL_UPDATE, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent); +#if !ENABLE_EXTENDED_SELECTION wxDECLARE_EVENT(EVT_GLCANVAS_ROTATE_OBJECT, Event); // data: -1 => rotate left, +1 => rotate right wxDECLARE_EVENT(EVT_GLCANVAS_SCALE_UNIFORMLY, SimpleEvent); -wxDECLARE_EVENT(EVT_GLCANVAS_INCREASE_OBJECTS, Event); // data: +1 => increase, -1 => decrease +#endif // !ENABLE_EXTENDED_SELECTION +wxDECLARE_EVENT(EVT_GLCANVAS_INCREASE_INSTANCES, Event); // data: +1 => increase, -1 => decrease wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_MOVED, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent); wxDECLARE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event); @@ -483,6 +485,8 @@ public: bool is_from_single_instance() const { return get_instance_idx() != -1; } bool is_from_single_object() const { return get_object_idx() != -1; } + bool contains_volume(unsigned int volume_idx) const { return std::find(m_list.begin(), m_list.end(), volume_idx) != m_list.end(); } + // Returns the the object id if the selection is from a single object, otherwise is -1 int get_object_idx() const; // Returns the instance id if the selection is from a single object and from a single instance, otherwise is -1 @@ -499,6 +503,9 @@ public: void translate(const Vec3d& displacement); void rotate(const Vec3d& rotation); void scale(const Vec3d& scale); +#if ENABLE_MIRROR + void mirror(Axis axis); +#endif // ENABLE_MIRROR void render(bool show_indirect_selection) const; @@ -831,6 +838,12 @@ public: int get_first_volume_id(int obj_idx) const; int get_in_object_volume_id(int scene_vol_idx) const; +#if ENABLE_MIRROR +#if ENABLE_EXTENDED_SELECTION + void mirror_selection(Axis axis); +#endif // ENABLE_EXTENDED_SELECTION +#endif // ENABLE_MIRROR + void reload_scene(bool force); void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors); @@ -953,10 +966,15 @@ private: void _on_rotate(); void _on_scale(); void _on_flatten(); +#if ENABLE_MIRROR + void _on_mirror(); +#endif // ENABLE_MIRROR #else void _on_move(const std::vector& volume_idxs); #endif // ENABLE_EXTENDED_SELECTION +#if !ENABLE_EXTENDED_SELECTION void _on_select(int volume_idx, int object_idx); +#endif // !ENABLE_EXTENDED_SELECTION // generates the legend texture in dependence of the current shown view type void _generate_legend_texture(const GCodePreviewData& preview_data, const std::vector& tool_colors); diff --git a/src/slic3r/GUI/GLCanvas3DManager.cpp b/src/slic3r/GUI/GLCanvas3DManager.cpp index 182298a69..1d963e3b7 100644 --- a/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -596,6 +596,17 @@ int GLCanvas3DManager::get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol return (it != m_canvases.end()) ? it->second->get_in_object_volume_id(scene_vol_idx) : -1; } +#if ENABLE_MIRROR +#if ENABLE_EXTENDED_SELECTION +void GLCanvas3DManager::mirror_selection(wxGLCanvas* canvas, Axis axis) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->mirror_selection(axis); +} +#endif // ENABLE_EXTENDED_SELECTION +#endif // ENABLE_MIRROR + void GLCanvas3DManager::reload_scene(wxGLCanvas* canvas, bool force) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/src/slic3r/GUI/GLCanvas3DManager.hpp b/src/slic3r/GUI/GLCanvas3DManager.hpp index 2261a57b2..89cb09e7f 100644 --- a/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -163,6 +163,12 @@ public: int get_first_volume_id(wxGLCanvas* canvas, int obj_idx) const; int get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx) const; +#if ENABLE_MIRROR +#if ENABLE_EXTENDED_SELECTION + void mirror_selection(wxGLCanvas* canvas, Axis axis); +#endif // ENABLE_EXTENDED_SELECTION +#endif // ENABLE_MIRROR + void reload_scene(wxGLCanvas* canvas, bool force); void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector& str_tool_colors); diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 144301d2d..2f75d43fd 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -868,7 +868,7 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const BoundingBoxf3 box; Transform3d transform = Transform3d::Identity(); Vec3d angles = Vec3d::Zero(); - Transform3d rotation = Transform3d::Identity(); + Transform3d offsets_transform = Transform3d::Identity(); if (selection.is_from_single_instance()) { @@ -880,13 +880,19 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const } // gets transform from first selected volume - transform = selection.get_volume(*idxs.begin())->world_matrix().cast(); + const GLVolume* v = selection.get_volume(*idxs.begin()); + transform = v->world_matrix().cast(); - // extract angles from transform - angles = Slic3r::Geometry::extract_euler_angles(transform); + // gets angles from first selected volume + angles = v->get_rotation(); +#if ENABLE_MIRROR + // consider rotation+mirror only components of the transform for offsets + offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_mirror()); +#else // set rotation-only component of transform - rotation = Geometry::assemble_transform(Vec3d::Zero(), angles); + offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles); +#endif // ENABLE_MIRROR } else box = selection.get_bounding_box(); @@ -898,9 +904,9 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const const Vec3d& center = m_box.center(); #if ENABLE_EXTENDED_SELECTION - Vec3d offset_x = rotation * Vec3d((double)Offset, 0.0, 0.0); - Vec3d offset_y = rotation * Vec3d(0.0, (double)Offset, 0.0); - Vec3d offset_z = rotation * Vec3d(0.0, 0.0, (double)Offset); + Vec3d offset_x = offsets_transform * Vec3d((double)Offset, 0.0, 0.0); + Vec3d offset_y = offsets_transform * Vec3d(0.0, (double)Offset, 0.0); + Vec3d offset_z = offsets_transform * Vec3d(0.0, 0.0, (double)Offset); #endif // ENABLE_EXTENDED_SELECTION // x axis @@ -1468,6 +1474,7 @@ void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const ::glEnable(GL_BLEND); ::glEnable(GL_DEPTH_TEST); + ::glDisable(GL_CULL_FACE); for (int i=0; i<(int)m_planes.size(); ++i) { if (i == m_hover_id) @@ -1506,6 +1513,7 @@ void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const #endif // ENABLE_EXTENDED_SELECTION } + ::glEnable(GL_CULL_FACE); ::glDisable(GL_BLEND); } @@ -1516,6 +1524,7 @@ void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const #endif // ENABLE_EXTENDED_SELECTION { ::glEnable(GL_DEPTH_TEST); + ::glDisable(GL_CULL_FACE); for (unsigned int i = 0; i < m_planes.size(); ++i) { @@ -1546,6 +1555,8 @@ void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const } #endif // ENABLE_EXTENDED_SELECTION } + + ::glEnable(GL_CULL_FACE); } void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object) @@ -1769,9 +1780,9 @@ Vec3d GLGizmoFlatten::get_flattening_rotation() const // calculates the rotations in model space, taking in account the scaling factors Eigen::Matrix m = m_model_object->instances.front()->world_matrix(true, true).matrix().block(0, 0, 3, 3).inverse().transpose(); Eigen::Quaterniond q; - Vec3d angles = q.setFromTwoVectors(m * m_normal, -Vec3d::UnitZ()).toRotationMatrix().eulerAngles(2, 1, 0); + Vec3d angles = Geometry::extract_euler_angles(q.setFromTwoVectors(m * m_normal, -Vec3d::UnitZ()).toRotationMatrix()); m_normal = Vec3d::Zero(); - return Vec3d(angles(2), angles(1), angles(0)); + return angles; } } // namespace GUI diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index e58142f26..c1683bba3 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -327,24 +327,6 @@ void GUI_App::CallAfter(std::function cb) callback_register.unlock(); } -wxMenuItem* GUI_App::append_submenu(wxMenu* menu, - wxMenu* sub_menu, - int id, - const wxString& string, - const wxString& description, - const std::string& icon) -{ - if (id == wxID_ANY) - id = wxNewId(); - auto item = new wxMenuItem(menu, id, string, description); - if (!icon.empty()) - item->SetBitmap(wxBitmap(Slic3r::var(icon), wxBITMAP_TYPE_PNG)); - item->SetSubMenu(sub_menu); - menu->Append(item); - - return item; -} - void GUI_App::window_pos_save(wxTopLevelWindow* window, const std::string &name) { if (name.empty()) { return; } @@ -500,7 +482,8 @@ ConfigMenuIDs GUI_App::get_view_mode() return ConfigMenuModeSimple; const auto mode = app_config->get("view_mode"); - return mode == "expert" ? ConfigMenuModeExpert : ConfigMenuModeSimple; + return mode == "expert" ? ConfigMenuModeExpert : + mode == "simple" ? ConfigMenuModeSimple : ConfigMenuModeMiddle; } // Update view mode according to selected menu @@ -518,6 +501,11 @@ void GUI_App::update_mode() sidebar().Layout(); mainframe->m_plater->Layout(); + + ConfigOptionMode opt_mode = mode == ConfigMenuModeSimple ? comSimple : + mode == ConfigMenuModeExpert ? comExpert : comMiddle; + for (auto tab : tabs_list) + tab->update_visibility(opt_mode); } void GUI_App::add_config_menu(wxMenuBar *menu) @@ -537,6 +525,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu) local_menu->AppendSeparator(); auto mode_menu = new wxMenu(); mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeSimple, _(L("&Simple")), _(L("Simple View Mode"))); + mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeMiddle, _(L("&Middle")), _(L("Middle View Mode"))); mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeExpert, _(L("&Expert")), _(L("Expert View Mode"))); mode_menu->Check(config_id_base + get_view_mode(), true); local_menu->AppendSubMenu(mode_menu, _(L("&Mode")), _(L("Slic3r View Mode"))); @@ -607,8 +596,9 @@ void GUI_App::add_config_menu(wxMenuBar *menu) } }); mode_menu->Bind(wxEVT_MENU, [this, config_id_base](wxEvent& event) { - std::string mode = event.GetId() - config_id_base == ConfigMenuModeExpert ? - "expert" : "simple"; + int id_mode = event.GetId() - config_id_base; + std::string mode = id_mode == ConfigMenuModeExpert ? "expert" : + id_mode == ConfigMenuModeSimple ? "simple" : "middle"; app_config->set("view_mode", mode); app_config->save(); update_mode(); diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index e1e48e9b9..52d582690 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -51,6 +51,7 @@ enum ConfigMenuIDs { ConfigMenuUpdate, ConfigMenuPreferences, ConfigMenuModeSimple, + ConfigMenuModeMiddle, ConfigMenuModeExpert, ConfigMenuLanguage, ConfigMenuFlashFirmware, @@ -107,12 +108,6 @@ public: // void notify(/*message*/); void update_ui_from_settings(); void CallAfter(std::function cb); - wxMenuItem* append_submenu(wxMenu* menu, - wxMenu* sub_menu, - int id, - const wxString& string, - const wxString& description, - const std::string& icon); void window_pos_save(wxTopLevelWindow* window, const std::string &name); void window_pos_restore(wxTopLevelWindow* window, const std::string &name); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 53066d708..6c9077bad 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -868,7 +868,7 @@ void ObjectList::del_settings_from_config() void ObjectList::del_instances_from_object(const int obj_idx) { - auto instances = (*m_objects)[obj_idx]->instances; + auto& instances = (*m_objects)[obj_idx]->instances; if (instances.size() <= 1) return; @@ -1054,7 +1054,11 @@ void ObjectList::part_selection_changed() m_selected_object_id = obj_idx; +#if ENABLE_EXTENDED_SELECTION + wxGetApp().obj_manipul()->update_settings_value(_3DScene::get_canvas(wxGetApp().canvas3D())->get_selection()); +#else wxGetApp().obj_manipul()->update_values(); +#endif // ENABLE_EXTENDED_SELECTION } void ObjectList::update_manipulation_sizer(const bool is_simple_mode) diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index c56e69670..56d01b7f5 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -4,6 +4,7 @@ #include #include #include +#include class wxBoxSizer; class PrusaObjectDataViewModel; diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index fc51b419b..ca3587118 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -221,7 +221,7 @@ void ObjectManipulation::update_settings_list() if (cat.second.size() == 1 && cat.second[0] == "extruder") continue; - auto optgroup = std::make_shared(parent, cat.first, config, false, ogDEFAULT, extra_column); + auto optgroup = std::make_shared(parent, cat.first, config, false, extra_column); optgroup->label_width = 150; optgroup->sidetext_width = 70; diff --git a/src/slic3r/GUI/GUI_Utils.cpp b/src/slic3r/GUI/GUI_Utils.cpp index 8fff2d208..5a7ece586 100644 --- a/src/slic3r/GUI/GUI_Utils.cpp +++ b/src/slic3r/GUI/GUI_Utils.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include "libslic3r/Config.hpp" @@ -16,6 +15,30 @@ namespace Slic3r { namespace GUI { +CheckboxFileDialog::ExtraPanel::ExtraPanel(wxWindow *parent) + : wxPanel(parent, wxID_ANY) +{ + // WARN: wxMSW does some extra shenanigans to calc the extra control size. + // It first calls the create function with a dummy empty wxDialog parent and saves its size. + // Afterwards, the create function is called again with the real parent. + // Additionally there's no way to pass any extra data to the create function (no closure), + // which is why we have to this stuff here. Grrr! + auto *dlg = dynamic_cast(parent); + const wxString checkbox_label(dlg != nullptr ? dlg->checkbox_label : wxString("String long enough to contain dlg->checkbox_label")); + + auto* sizer = new wxBoxSizer(wxHORIZONTAL); + cbox = new wxCheckBox(this, wxID_ANY, checkbox_label); + cbox->SetValue(true); + sizer->AddSpacer(5); + sizer->Add(this->cbox, 0, wxEXPAND | wxALL, 5); + SetSizer(sizer); + sizer->SetSizeHints(this); +} + +wxWindow* CheckboxFileDialog::ExtraPanel::ctor(wxWindow *parent) { + return new ExtraPanel(parent); +} + CheckboxFileDialog::CheckboxFileDialog(wxWindow *parent, const wxString &checkbox_label, bool checkbox_value, @@ -29,35 +52,22 @@ CheckboxFileDialog::CheckboxFileDialog(wxWindow *parent, const wxString &name ) : wxFileDialog(parent, message, default_dir, default_file, wildcard, style, pos, size, name) - , cbox(nullptr) + , checkbox_label(checkbox_label) { if (checkbox_label.IsEmpty()) { return; } - extra_control_creator = [this, checkbox_label](wxWindow *parent) -> wxWindow* { - wxPanel* panel = new wxPanel(parent, -1); - wxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); - this->cbox = new wxCheckBox(panel, wxID_HIGHEST + 1, checkbox_label); - this->cbox->SetValue(true); - sizer->AddSpacer(5); - sizer->Add(this->cbox, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); - panel->SetSizer(sizer); - sizer->SetSizeHints(panel); - - return panel; - }; - - SetExtraControlCreator(*extra_control_creator.target()); + SetExtraControlCreator(ExtraPanel::ctor); } bool CheckboxFileDialog::get_checkbox_value() const { - return this->cbox != nullptr ? cbox->IsChecked() : false; + auto *extra_panel = dynamic_cast(GetExtraControl()); + return extra_panel != nullptr ? extra_panel->cbox->GetValue() : false; } - WindowMetrics WindowMetrics::from_window(wxTopLevelWindow *window) { WindowMetrics res; diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index 1c776df81..fe96e5a1b 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -8,6 +8,7 @@ #include #include +#include class wxCheckBox; class wxTopLevelWindow; @@ -37,8 +38,15 @@ public: bool get_checkbox_value() const; private: - std::function extra_control_creator; - wxCheckBox *cbox; + struct ExtraPanel : public wxPanel + { + wxCheckBox *cbox; + + ExtraPanel(wxWindow *parent); + static wxWindow* ctor(wxWindow *parent); + }; + + wxString checkbox_label; }; diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index fdff39cb4..5960e8886 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -296,13 +296,13 @@ void MainFrame::init_menubar() if (m_plater) { m_plater_menu = new wxMenu(); append_menu_item(m_plater_menu, wxID_ANY, _(L("Export G-code...")), _(L("Export current plate as G-code")), - [this](wxCommandEvent&){ /*m_plater->export_gcode(); */}, "cog_go.png"); + [this](wxCommandEvent&){ m_plater->export_gcode(); }, "cog_go.png"); append_menu_item(m_plater_menu, wxID_ANY, _(L("Export plate as STL...")), _(L("Export current plate as STL")), - [this](wxCommandEvent&){ /*m_plater->export_stl(); */}, "brick_go.png"); + [this](wxCommandEvent&){ m_plater->export_stl(); }, "brick_go.png"); append_menu_item(m_plater_menu, wxID_ANY, _(L("Export plate as AMF...")), _(L("Export current plate as AMF")), - [this](wxCommandEvent&){ /*m_plater->export_amf();*/ }, "brick_go.png"); + [this](wxCommandEvent&){ m_plater->export_amf(); }, "brick_go.png"); append_menu_item(m_plater_menu, wxID_ANY, _(L("Export plate as 3MF...")), _(L("Export current plate as 3MF")), - [this](wxCommandEvent&){ /*m_plater->export_3mf(); */}, "brick_go.png"); + [this](wxCommandEvent&){ m_plater->export_3mf(); }, "brick_go.png"); } // Window menu diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index bfe54c3ce..371747bc1 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -97,8 +97,8 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co void OptionsGroup::add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& field) { if (!m_show_modified_btns) { - field->m_Undo_btn->Hide(); - field->m_Undo_to_sys_btn->Hide(); + field->m_Undo_btn->set_as_hidden(); + field->m_Undo_to_sys_btn->set_as_hidden(); return; } @@ -123,6 +123,10 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* for (auto opt : option_set) m_options.emplace(opt.opt_id, opt); + // add mode value for current line to m_options_mode + if (!option_set.empty()) + m_options_mode.push_back(option_set[0].opt.mode); + // if we have a single option with no label, no sidetext just add it directly to sizer if (option_set.size() == 1 && label_width == 0 && option_set.front().opt.full_width && option_set.front().opt.sidetext.size() == 0 && option_set.front().side_widget == nullptr && @@ -156,16 +160,8 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* #endif /* __WXGTK__ */ // if we have an extra column, build it - if (extra_column) { - if (extra_column) { - grid_sizer->Add(extra_column(parent(), line), 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 3); - } - else { - // if the callback provides no sizer for the extra cell, put a spacer - grid_sizer->AddSpacer(1); - } - } - + if (extra_column) + grid_sizer->Add(extra_column(parent(), line), 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 3); // Build a label if we have it wxStaticText* label=nullptr; @@ -182,16 +178,14 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* label->SetFont(label_font); label->Wrap(label_width); // avoid a Linux/GTK bug if (!line.near_label_widget) - grid_sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | - (m_flag == ogSIDE_OPTIONS_VERTICAL ? wxTOP : wxALIGN_CENTER_VERTICAL), 5); + grid_sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, 5); else { // If we're here, we have some widget near the label // so we need a horizontal sizer to arrange these things auto sizer = new wxBoxSizer(wxHORIZONTAL); grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM | wxTOP | wxLEFT), staticbox ? 0 : 1); sizer->Add(line.near_label_widget(parent()), 0, wxRIGHT, 7); - sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | - (m_flag == ogSIDE_OPTIONS_VERTICAL ? wxTOP : wxALIGN_CENTER_VERTICAL), 5); + sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, 5); } if (line.label_tooltip.compare("") != 0) label->SetToolTip(line.label_tooltip); @@ -208,7 +202,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* // If we're here, we have more than one option or a single option with sidetext // so we need a horizontal sizer to arrange these things - auto sizer = new wxBoxSizer(m_flag == ogSIDE_OPTIONS_VERTICAL ? wxVERTICAL : wxHORIZONTAL); + auto sizer = new wxBoxSizer(wxHORIZONTAL); grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM | wxTOP | wxLEFT), staticbox ? 0 : 1); // If we have a single option with no sidetext just add it directly to the grid sizer if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 && @@ -227,14 +221,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* for (auto opt : option_set) { ConfigOptionDef option = opt.opt; - wxSizer* sizer_tmp; - if (m_flag == ogSIDE_OPTIONS_VERTICAL){ - auto sz = new wxFlexGridSizer(1, 3, 2, 2); - sz->RemoveGrowableCol(2); - sizer_tmp = sz; - } - else - sizer_tmp = sizer; + wxSizer* sizer_tmp = sizer; // add label if any if (option.label != "") { wxString str_label = _(option.label); @@ -260,7 +247,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* auto sidetext = new wxStaticText( parent(), wxID_ANY, _(option.sidetext), wxDefaultPosition, wxSize(sidetext_width, -1)/*wxDefaultSize*/, wxALIGN_LEFT); sidetext->SetFont(sidetext_font); - sizer_tmp->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, m_flag == ogSIDE_OPTIONS_VERTICAL ? 0 : 4); + sizer_tmp->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4); field->set_side_text_ptr(sidetext); } @@ -269,13 +256,10 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* sizer_tmp->Add(opt.side_widget(parent())/*!.target()*/, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 1); //! requires verification } - if (opt.opt_id != option_set.back().opt_id && m_flag != ogSIDE_OPTIONS_VERTICAL) //! istead of (opt != option_set.back()) + if (opt.opt_id != option_set.back().opt_id) //! istead of (opt != option_set.back()) { sizer_tmp->AddSpacer(6); } - - if (m_flag == ogSIDE_OPTIONS_VERTICAL) - sizer->Add(sizer_tmp, 0, wxALIGN_RIGHT|wxALL, 0); } // add extra sizers if any for (auto extra_widget : line.get_extra_widgets()) { @@ -403,6 +387,39 @@ void ConfigOptionsGroup::reload_config(){ } +bool ConfigOptionsGroup::update_visibility(ConfigOptionMode mode) { + if (m_options_mode.empty()) + return true; + if (m_grid_sizer->GetEffectiveRowsCount() != m_options_mode.size() && + m_options_mode.size() == 1) + return m_options_mode[0] <= mode; + + sizer->ShowItems(true); +#ifdef __WXGTK__ + m_panel->Show(true); + m_grid_sizer->Show(true); +#endif /* __WXGTK__ */ + + int coef = 0; + int hidden_row_cnt = 0; + const int cols = m_grid_sizer->GetCols(); + for (auto opt_mode : m_options_mode) { + const bool show = opt_mode <= mode; + if (!show) { + hidden_row_cnt++; + for (int i = 0; i < cols; ++i) + m_grid_sizer->Show(coef + i, show); + } + coef+= cols; + } + + if (hidden_row_cnt == m_options_mode.size()) { + sizer->ShowItems(false); + return false; + } + return true; +} + boost::any ConfigOptionsGroup::config_value(const std::string& opt_key, int opt_index, bool deserialize){ if (deserialize) { diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index 8ee2ee167..656ae1d72 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -27,11 +27,6 @@ namespace Slic3r { namespace GUI { -enum ogDrawFlag{ - ogDEFAULT, - ogSIDE_OPTIONS_VERTICAL -}; - /// Widget type describes a function object that returns a wxWindow (our widget) and accepts a wxWidget (parent window). using widget_t = std::function;//!std::function; @@ -151,7 +146,6 @@ public: inline void enable() { for (auto& field : m_fields) field.second->enable(); } inline void disable() { for (auto& field : m_fields) field.second->disable(); } - void set_flag(ogDrawFlag flag) { m_flag = flag; } void set_grid_vgap(int gap) { m_grid_sizer->SetVGap(gap); } void set_show_modified_btns_val(bool show) { @@ -159,9 +153,10 @@ public: } OptionsGroup( wxWindow* _parent, const wxString& title, bool is_tab_opt = false, - ogDrawFlag flag = ogDEFAULT, column_t extra_clmn = nullptr) : - m_parent(_parent), title(title), m_show_modified_btns(is_tab_opt), - staticbox(title!=""), m_flag(flag), extra_column(extra_clmn){ + column_t extra_clmn = nullptr) : + m_parent(_parent), title(title), + m_show_modified_btns(is_tab_opt), + staticbox(title!=""), extra_column(extra_clmn){ if (staticbox) { stb = new wxStaticBox(_parent, wxID_ANY, title); stb->SetFont(wxGetApp().bold_font()); @@ -172,7 +167,7 @@ public: if (extra_column != nullptr) num_columns++; m_grid_sizer = new wxFlexGridSizer(0, num_columns, 1,0); static_cast(m_grid_sizer)->SetFlexibleDirection(wxBOTH/*wxHORIZONTAL*/); - static_cast(m_grid_sizer)->AddGrowableCol(label_width != 0); + static_cast(m_grid_sizer)->AddGrowableCol(label_width == 0 ? 0 : !extra_column ? 1 : 2 ); #ifdef __WXGTK__ m_panel = new wxPanel( _parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); sizer->Fit(m_panel); @@ -187,6 +182,7 @@ public: protected: std::map m_options; wxWindow* m_parent {nullptr}; + std::vector m_options_mode; /// Field list, contains unique_ptrs of the derived type. /// using types that need to know what it is beyond the public interface @@ -197,8 +193,6 @@ protected: // "true" if option is created in preset tabs bool m_show_modified_btns{ false }; - ogDrawFlag m_flag{ ogDEFAULT }; - // This panel is needed for correct showing of the ToolTips for Button, StaticText and CheckBox // Tooltips on GTK doesn't work inside wxStaticBoxSizer unless you insert a panel // inside it before you insert the other controls. @@ -223,8 +217,8 @@ protected: class ConfigOptionsGroup: public OptionsGroup { public: ConfigOptionsGroup( wxWindow* parent, const wxString& title, DynamicPrintConfig* _config = nullptr, - bool is_tab_opt = false, ogDrawFlag flag = ogDEFAULT, column_t extra_clmn = nullptr) : - OptionsGroup(parent, title, is_tab_opt, flag, extra_clmn), m_config(_config) {} + bool is_tab_opt = false, column_t extra_clmn = nullptr) : + OptionsGroup(parent, title, is_tab_opt, extra_clmn), m_config(_config) {} /// reference to libslic3r config, non-owning pointer (?). DynamicPrintConfig* m_config {nullptr}; @@ -252,6 +246,8 @@ public: void back_to_config_value(const DynamicPrintConfig& config, const std::string& opt_key); void on_kill_focus() override{ reload_config();} void reload_config(); + // return value shows visibility : false => all options are hidden + bool update_visibility(ConfigOptionMode mode); boost::any config_value(const std::string& opt_key, int opt_index, bool deserialize); // return option value from config boost::any get_config_value(const DynamicPrintConfig& config, const std::string& opt_key, int opt_index = -1); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b62194b3d..e5668732a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -701,7 +701,7 @@ private: static const std::regex pattern_drop; }; -const std::regex PlaterDropTarget::pattern_drop("[.](stl|obj|amf|3mf|prusa)$", std::regex::icase); +const std::regex PlaterDropTarget::pattern_drop(".*[.](stl|obj|amf|3mf|prusa)", std::regex::icase); bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames) { @@ -783,13 +783,19 @@ struct Plater::priv #endif // ENABLE_EXTENDED_SELECTION void selection_changed(); void object_list_changed(); +#if !ENABLE_EXTENDED_SELECTION void select_view(); +#endif // !ENABLE_EXTENDED_SELECTION void remove(size_t obj_idx); void reset(); +#if !ENABLE_EXTENDED_SELECTION void rotate(); - void mirror(const Axis &axis); +#endif // !ENABLE_EXTENDED_SELECTION + void mirror(Axis axis); +#if !ENABLE_EXTENDED_SELECTION void scale(); +#endif // !ENABLE_EXTENDED_SELECTION void arrange(); void split_object(); void schedule_background_process(); @@ -844,12 +850,17 @@ private: bool can_split_object() const; bool can_cut_object() const; bool layers_height_allowed() const; + bool can_delete_all() const; + bool can_arrange() const; +#if ENABLE_MIRROR + bool can_mirror() const; +#endif // ENABLE_MIRROR #endif // ENABLE_EXTENDED_SELECTION }; -const std::regex Plater::priv::pattern_bundle("[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)$", std::regex::icase); -const std::regex Plater::priv::pattern_3mf("[.]3mf$", std::regex::icase); -const std::regex Plater::priv::pattern_zip_amf("[.]zip[.]amf$", std::regex::icase); +const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)", std::regex::icase); +const std::regex Plater::priv::pattern_3mf(".*3mf", std::regex::icase); +const std::regex Plater::priv::pattern_zip_amf(".*[.]zip[.]amf", std::regex::icase); Plater::priv::priv(Plater *q, MainFrame *main_frame) : q(q), @@ -925,9 +936,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) : canvas3D->Bind(EVT_GLCANVAS_MODEL_UPDATE, &priv::on_model_update, this); canvas3D->Bind(EVT_GLCANVAS_REMOVE_OBJECT, [q](SimpleEvent&) { q->remove_selected(); }); canvas3D->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { arrange(); }); +#if !ENABLE_EXTENDED_SELECTION canvas3D->Bind(EVT_GLCANVAS_ROTATE_OBJECT, [this](Event &evt) { /*TODO: call rotate */ }); canvas3D->Bind(EVT_GLCANVAS_SCALE_UNIFORMLY, [this](SimpleEvent&) { scale(); }); - canvas3D->Bind(EVT_GLCANVAS_INCREASE_OBJECTS, [q](Event &evt) { evt.data == 1 ? q->increase() : q->decrease(); }); +#endif // !ENABLE_EXTENDED_SELECTION + canvas3D->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [q](Event &evt) { evt.data == 1 ? q->increase_instances() : q->decrease_instances(); }); canvas3D->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); }); canvas3D->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this); canvas3D->Bind(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, &priv::on_enable_action_buttons, this); @@ -937,8 +950,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) : canvas3D->Bind(EVT_GLTOOLBAR_DELETE, [q](SimpleEvent&) { q->remove_selected(); } ); canvas3D->Bind(EVT_GLTOOLBAR_DELETE_ALL, [this](SimpleEvent&) { reset(); }); canvas3D->Bind(EVT_GLTOOLBAR_ARRANGE, [this](SimpleEvent&) { arrange(); }); - canvas3D->Bind(EVT_GLTOOLBAR_MORE, [q](SimpleEvent&) { q->increase(); }); - canvas3D->Bind(EVT_GLTOOLBAR_FEWER, [q](SimpleEvent&) { q->decrease(); }); + canvas3D->Bind(EVT_GLTOOLBAR_MORE, [q](SimpleEvent&) { q->increase_instances(); }); + canvas3D->Bind(EVT_GLTOOLBAR_FEWER, [q](SimpleEvent&) { q->decrease_instances(); }); canvas3D->Bind(EVT_GLTOOLBAR_SPLIT, &priv::on_action_split, this); canvas3D->Bind(EVT_GLTOOLBAR_CUT, &priv::on_action_cut, this); #if !ENABLE_EXTENDED_SELECTION @@ -1092,6 +1105,9 @@ std::vector Plater::priv::load_files(const std::vector &input_ // $self->async_apply_config; } else { model = Slic3r::Model::read_from_file(path.string(), nullptr, false); + for (auto obj : model.objects) + if (obj->name.empty()) + obj->name = fs::path(obj->input_file).filename().string(); } } catch (const std::runtime_error &e) { @@ -1139,7 +1155,7 @@ std::vector Plater::priv::load_files(const std::vector &input_ new_model->convert_multipart_object(nozzle_dmrs->values.size()); } - auto loaded_idxs = load_model_objects(model.objects); + auto loaded_idxs = load_model_objects(new_model->objects); obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end()); } @@ -1159,7 +1175,7 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode bool scaled_down = false; std::vector obj_idxs; #if ENABLE_EXTENDED_SELECTION - unsigned int obj_count = 0; + unsigned int obj_count = model.objects.size(); #endif // ENABLE_EXTENDED_SELECTION for (ModelObject *model_object : model_objects) { @@ -1238,7 +1254,8 @@ std::unique_ptr Plater::priv::get_export_file(GUI::FileType case FT_STL: case FT_AMF: case FT_3MF: - wildcard = file_wildcards[FT_STL]; + case FT_GCODE: + wildcard = file_wildcards[file_type]; break; default: @@ -1342,7 +1359,7 @@ void Plater::priv::selection_changed() _3DScene::enable_toolbar_item(canvas3D, "split", have_sel); _3DScene::enable_toolbar_item(canvas3D, "cut", have_sel); _3DScene::enable_toolbar_item(canvas3D, "settings", have_sel); - _3DScene::enable_toolbar_item(canvas3D, "layersediting", layers_height_allowed); + _3DScene::enable_toolbar_item(canvas3D, "layersediting", have_sel && config->opt_bool("variable_layer_height") && _3DScene::is_layers_editing_allowed(canvas3D)); #endif // ENABLE_EXTENDED_SELECTION #if ENABLE_EXTENDED_SELECTION @@ -1421,25 +1438,31 @@ void Plater::priv::object_list_changed() { // Enable/disable buttons depending on whether there are any objects on the platter. #if ENABLE_EXTENDED_SELECTION - const bool have_objects = !model.objects.empty(); + _3DScene::enable_toolbar_item(canvas3D, "deleteall", can_delete_all()); + _3DScene::enable_toolbar_item(canvas3D, "arrange", can_arrange()); #else const bool have_objects = !objects.empty(); -#endif // ENABLE_EXTENDED_SELECTION - _3DScene::enable_toolbar_item(canvas3D, "deleteall", have_objects); _3DScene::enable_toolbar_item(canvas3D, "arrange", have_objects); +#endif // ENABLE_EXTENDED_SELECTION const bool export_in_progress = !(export_gcode_output_file.empty() && send_gcode_file.empty()); // XXX: is this right? const bool model_fits = _3DScene::check_volumes_outside_state(canvas3D, config) == ModelInstance::PVS_Inside; +#if ENABLE_EXTENDED_SELECTION + sidebar->enable_buttons(!model.objects.empty() && !export_in_progress && model_fits); +#else sidebar->enable_buttons(have_objects && !export_in_progress && model_fits); +#endif // ENABLE_EXTENDED_SELECTION } +#if !ENABLE_EXTENDED_SELECTION void Plater::priv::select_view() { // TODO } +#endif // !ENABLE_EXTENDED_SELECTION void Plater::priv::remove(size_t obj_idx) { @@ -1448,6 +1471,9 @@ void Plater::priv::remove(size_t obj_idx) // Prevent toolpaths preview from rendering while we modify the Print object preview->set_enabled(false); + if (_3DScene::is_layers_editing_enabled(canvas3D)) + _3DScene::enable_layers_editing(canvas3D, false); + #if !ENABLE_EXTENDED_SELECTION objects.erase(objects.begin() + obj_idx); #endif // !ENABLE_EXTENDED_SELECTION @@ -1471,6 +1497,9 @@ void Plater::priv::reset() // Prevent toolpaths preview from rendering while we modify the Print object preview->set_enabled(false); + if (_3DScene::is_layers_editing_enabled(canvas3D)) + _3DScene::enable_layers_editing(canvas3D, false); + #if !ENABLE_EXTENDED_SELECTION objects.clear(); #endif // !ENABLE_EXTENDED_SELECTION @@ -1487,13 +1516,20 @@ void Plater::priv::reset() update(); } +#if !ENABLE_EXTENDED_SELECTION void Plater::priv::rotate() { // TODO } +#endif // !ENABLE_EXTENDED_SELECTION -void Plater::priv::mirror(const Axis &axis) +void Plater::priv::mirror(Axis axis) { +#if ENABLE_MIRROR +#if ENABLE_EXTENDED_SELECTION + _3DScene::mirror_selection(canvas3D, axis); +#endif // ENABLE_EXTENDED_SELECTION +#else #if ENABLE_EXTENDED_SELECTION int obj_idx = get_selected_object_idx(); if (obj_idx == -1) @@ -1526,12 +1562,15 @@ void Plater::priv::mirror(const Axis &axis) #endif // ENABLE_EXTENDED_SELECTION selection_changed(); update(); +#endif // ENABLE_MIRROR } +#if !ENABLE_EXTENDED_SELECTION void Plater::priv::scale() { // TODO } +#endif // !ENABLE_EXTENDED_SELECTION void Plater::priv::arrange() { @@ -1547,7 +1586,45 @@ void Plater::priv::arrange() void Plater::priv::split_object() { - // TODO +#if ENABLE_EXTENDED_SELECTION + int obj_idx = get_selected_object_idx(); + if (obj_idx == -1) + return; + + // we clone model object because split_object() adds the split volumes + // into the same model object, thus causing duplicates when we call load_model_objects() + Model new_model = model; + ModelObject* current_model_object = new_model.objects[obj_idx]; + + if (current_model_object->volumes.size() > 1) + { + Slic3r::GUI::warning_catcher(q, _(L("The selected object can't be split because it contains more than one volume/material."))); + return; + } + +// $self->stop_background_process; + + ModelObjectPtrs new_objects; + current_model_object->split(&new_objects); + if (new_objects.size() == 1) + { + Slic3r::GUI::warning_catcher(q, _(L("The selected object couldn't be split because it contains only one part."))); +// $self->schedule_background_process; + } + else + { + for (ModelObject* m : new_objects) + { + m->center_around_origin(); + } + + remove(obj_idx); + + // load all model objects at once, otherwise the plate would be rearranged after each one + // causing original positions not to be kept + load_model_objects(new_objects); + } +#endif // ENABLE_EXTENDED_SELECTION } void Plater::priv::schedule_background_process() @@ -1821,7 +1898,7 @@ void Plater::priv::on_action_add(SimpleEvent&) void Plater::priv::on_action_split(SimpleEvent&) { - // TODO + split_object(); } void Plater::priv::on_action_cut(SimpleEvent&) @@ -1838,7 +1915,10 @@ void Plater::priv::on_action_settings(SimpleEvent&) void Plater::priv::on_action_layersediting(SimpleEvent&) { - // TODO + bool enable = !_3DScene::is_layers_editing_enabled(canvas3D); + _3DScene::enable_layers_editing(canvas3D, enable); + if (enable && !_3DScene::is_layers_editing_enabled(canvas3D)) + _3DScene::enable_toolbar_item(canvas3D, "layersediting", false); } #if !ENABLE_EXTENDED_SELECTION @@ -1958,17 +2038,41 @@ bool Plater::priv::init_object_menu() wxMenuItem* item_delete = append_menu_item(&object_menu, wxID_ANY, _(L("Delete\tDel")), _(L("Remove the selected object")), [this](wxCommandEvent&){ q->remove_selected(); }, "brick_delete.png"); wxMenuItem* item_increase = append_menu_item(&object_menu, wxID_ANY, _(L("Increase copies\t+")), _(L("Place one more copy of the selected object")), - [this](wxCommandEvent&){ q->increase(); }, "add.png"); + [this](wxCommandEvent&){ q->increase_instances(); }, "add.png"); wxMenuItem* item_decrease = append_menu_item(&object_menu, wxID_ANY, _(L("Decrease copies\t-")), _(L("Remove one copy of the selected object")), - [this](wxCommandEvent&){ q->decrease(); }, "delete.png"); + [this](wxCommandEvent&){ q->decrease_instances(); }, "delete.png"); + + object_menu.AppendSeparator(); + +#if ENABLE_MIRROR + wxMenu* mirror_menu = new wxMenu(); + if (mirror_menu == nullptr) + return false; + + append_menu_item(mirror_menu, wxID_ANY, _(L("Along X axis")), _(L("Mirror the selected object along the X axis")), + [this](wxCommandEvent&){ mirror(X); }, "bullet_red.png", &object_menu); + append_menu_item(mirror_menu, wxID_ANY, _(L("Along Y axis")), _(L("Mirror the selected object along the Y axis")), + [this](wxCommandEvent&){ mirror(Y); }, "bullet_green.png", &object_menu); + append_menu_item(mirror_menu, wxID_ANY, _(L("Along Z axis")), _(L("Mirror the selected object along the Z axis")), + [this](wxCommandEvent&){ mirror(Z); }, "bullet_blue.png", &object_menu); + + wxMenuItem* item_mirror = append_submenu(&object_menu, mirror_menu, wxID_ANY, _(L("Mirror")), _(L("Mirror the selected object"))); +#endif // ENABLE_MIRROR + + wxMenuItem* item_split = append_menu_item(&object_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object into individual parts")), + [this](wxCommandEvent&){ split_object(); }, "shape_ungroup.png"); #if ENABLE_EXTENDED_SELECTION // ui updates needs to be binded to the parent panel if (q != nullptr) { +#if ENABLE_MIRROR + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_mirror()); }, item_mirror->GetId()); +#endif // ENABLE_MIRROR q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_delete_object()); }, item_delete->GetId()); q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_increase_instances()); }, item_increase->GetId()); q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_decrease_instances()); }, item_decrease->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_object()); }, item_split->GetId()); } #endif // ENABLE_EXTENDED_SELECTION @@ -1997,7 +2101,7 @@ bool Plater::priv::can_decrease_instances() const bool Plater::priv::can_split_object() const { int obj_idx = get_selected_object_idx(); - return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()); + return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && !model.objects[obj_idx]->is_multiparts(); } bool Plater::priv::can_cut_object() const @@ -2008,8 +2112,26 @@ bool Plater::priv::can_cut_object() const bool Plater::priv::layers_height_allowed() const { - return config->opt_bool("variable_layer_height") && _3DScene::is_layers_editing_allowed(canvas3D); + int obj_idx = get_selected_object_idx(); + return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && config->opt_bool("variable_layer_height") && _3DScene::is_layers_editing_allowed(canvas3D); } + +bool Plater::priv::can_delete_all() const +{ + return !model.objects.empty(); +} + +bool Plater::priv::can_arrange() const +{ + return !model.objects.empty(); +} + +#if ENABLE_MIRROR +bool Plater::priv::can_mirror() const +{ + return get_selection().is_from_single_instance(); +} +#endif // ENABLE_MIRROR #endif // ENABLE_EXTENDED_SELECTION // Plater / Public @@ -2063,7 +2185,7 @@ void Plater::remove_selected() #endif // ENABLE_EXTENDED_SELECTION } -void Plater::increase(size_t num) +void Plater::increase_instances(size_t num) { #if ENABLE_EXTENDED_SELECTION int obj_idx = p->get_selected_object_idx(); @@ -2105,12 +2227,16 @@ void Plater::increase(size_t num) p->update(); } +#if ENABLE_EXTENDED_SELECTION + p->get_selection().add_instance(obj_idx, (int)model_object->instances.size() - 1); +#endif // ENABLE_EXTENDED_SELECTION + p->selection_changed(); this->p->schedule_background_process(); } -void Plater::decrease(size_t num) +void Plater::decrease_instances(size_t num) { #if ENABLE_EXTENDED_SELECTION int obj_idx = p->get_selected_object_idx(); @@ -2148,6 +2274,12 @@ void Plater::decrease(size_t num) } p->update(); + +#if ENABLE_EXTENDED_SELECTION + if (!model_object->instances.empty()) + p->get_selection().add_instance(obj_idx, (int)model_object->instances.size() - 1); +#endif // ENABLE_EXTENDED_SELECTION + p->selection_changed(); // $self->schedule_background_process; @@ -2169,11 +2301,10 @@ void Plater::set_number_of_copies(size_t num) #endif // ENABLE_EXTENDED_SELECTION int diff = (int)num - (int)model_object->instances.size(); - if (diff > 0) { - increase(diff); - } else if (diff < 0) { - decrease(-diff); - } + if (diff > 0) + increase_instances(diff); + else if (diff < 0) + decrease_instances(-diff); } fs::path Plater::export_gcode(const fs::path &output_path) diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 3924f990f..544d4672a 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -113,8 +113,8 @@ public: void remove(size_t obj_idx); void remove_selected(); - void increase(size_t num = 1); - void decrease(size_t num = 1); + void increase_instances(size_t num = 1); + void decrease_instances(size_t num = 1); void set_number_of_copies(size_t num); // Note: empty path means "use the default" diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 3b9fe0bd3..55aeaa234 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -476,14 +476,14 @@ void Tab::get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool void Tab::update_changed_tree_ui() { auto cur_item = m_treectrl->GetFirstVisibleItem(); - if (!m_treectrl->IsVisible(cur_item)) + if (!cur_item || !m_treectrl->IsVisible(cur_item)) return; auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection()); while (cur_item){ auto title = m_treectrl->GetItemText(cur_item); for (auto page : m_pages) { - if (page->title() != title) + if (page->title() != title) continue; bool sys_page = true; bool modified_page = false; @@ -632,6 +632,25 @@ void Tab::reload_config(){ Thaw(); } +void Tab::update_visibility(ConfigOptionMode mode) +{ + Freeze(); + + for (auto page : m_pages) + page->update_visibility(mode); + update_page_tree_visibility(); + + m_hsizer->Layout(); + Refresh(); + + Thaw(); + + // to update tree items color + wxTheApp->CallAfter([this]() { + update_changed_tree_ui(); + }); +} + Field* Tab::get_field(const t_config_option_key& opt_key, int opt_index/* = -1*/) const { Field* field = nullptr; @@ -1021,7 +1040,7 @@ void TabPrint::build() page = add_options_page(_(L("Dependencies")), "wrench.png"); optgroup = page->new_optgroup(_(L("Profile dependencies"))); - line = { _(L("Compatible printers")), "" }; + line = optgroup->create_single_option_line("compatible_printers");//{ _(L("Compatible printers")), "" }; line.widget = [this](wxWindow* parent){ return compatible_printers_widget(parent, &m_compatible_printers_checkbox, &m_compatible_printers_btn); }; @@ -1347,7 +1366,7 @@ void TabFilament::build() optgroup->append_single_option_line("filament_cooling_final_speed"); optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower"); - line = { _(L("Ramming")), "" }; + line = optgroup->create_single_option_line("filament_ramming_parameters");// { _(L("Ramming")), "" }; line.widget = [this](wxWindow* parent){ auto ramming_dialog_btn = new wxButton(parent, wxID_ANY, _(L("Ramming settings"))+dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); auto sizer = new wxBoxSizer(wxHORIZONTAL); @@ -1387,7 +1406,7 @@ void TabFilament::build() page = add_options_page(_(L("Dependencies")), "wrench.png"); optgroup = page->new_optgroup(_(L("Profile dependencies"))); - line = { _(L("Compatible printers")), "" }; + line = optgroup->create_single_option_line("compatible_printers");//{ _(L("Compatible printers")), "" }; line.widget = [this](wxWindow* parent){ return compatible_printers_widget(parent, &m_compatible_printers_checkbox, &m_compatible_printers_btn); }; @@ -1482,7 +1501,7 @@ void TabPrinter::build_fff() auto page = add_options_page(_(L("General")), "printer_empty.png"); auto optgroup = page->new_optgroup(_(L("Size and coordinates"))); - Line line{ _(L("Bed shape")), "" }; + Line line = optgroup->create_single_option_line("bed_shape");//{ _(L("Bed shape")), "" }; line.widget = [this](wxWindow* parent){ auto btn = new wxButton(parent, wxID_ANY, _(L(" Set "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); btn->SetFont(wxGetApp().small_font()); @@ -1514,6 +1533,7 @@ void TabPrinter::build_fff() def.label = L("Extruders"); def.tooltip = L("Number of extruders of the printer."); def.min = 1; + def.mode = comExpert; Option option(def, "extruders_count"); optgroup->append_single_option_line(option); optgroup->append_single_option_line("single_extruder_multi_material"); @@ -1767,7 +1787,7 @@ void TabPrinter::build_sla() auto page = add_options_page(_(L("General")), "printer_empty.png"); auto optgroup = page->new_optgroup(_(L("Size and coordinates"))); - Line line{ _(L("Bed shape")), "" }; + Line line = optgroup->create_single_option_line("bed_shape");//{ _(L("Bed shape")), "" }; line.widget = [this](wxWindow* parent){ auto btn = new wxButton(parent, wxID_ANY, _(L(" Set ")) + dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); // btn->SetFont(Slic3r::GUI::small_font); @@ -2255,6 +2275,33 @@ void Tab::rebuild_page_tree(bool tree_sel_change_event /*= false*/) Thaw(); } +void Tab::update_page_tree_visibility() +{ + const auto sel_item = m_treectrl->GetSelection(); + const auto selected = sel_item ? m_treectrl->GetItemText(sel_item) : ""; + const auto rootItem = m_treectrl->GetRootItem(); + + auto have_selection = 0; + m_treectrl->DeleteChildren(rootItem); + for (auto p : m_pages) + { + if (!p->get_show()) + continue; + auto itemId = m_treectrl->AppendItem(rootItem, p->title(), p->iconID()); + m_treectrl->SetItemTextColour(itemId, p->get_item_colour()); + if (p->title() == selected) { + m_treectrl->SelectItem(itemId); + have_selection = 1; + } + } + + if (!have_selection) { + // this is triggered on first load, so we don't disable the sel change event + m_treectrl->SelectItem(m_treectrl->GetFirstVisibleItem());//! (treectrl->GetFirstChild(rootItem)); + } + +} + // Called by the UI combo box when the user switches profiles. // Select a preset by a name.If !defined(name), then the default preset is selected. // If the current profile is modified, user is asked to save the changes. @@ -2379,10 +2426,13 @@ void Tab::OnTreeSelChange(wxTreeEvent& event) wxWindowUpdateLocker noUpdates(this); #endif + if (m_pages.empty()) + return; + Page* page = nullptr; const auto sel_item = m_treectrl->GetSelection(); const auto selection = sel_item ? m_treectrl->GetItemText(sel_item) : ""; - for (auto p : m_pages) + for (auto p : m_pages) if (p->title() == selection) { page = p.get(); @@ -2681,6 +2731,15 @@ void Page::reload_config() group->reload_config(); } +void Page::update_visibility(ConfigOptionMode mode) +{ + bool ret_val = false; + for (auto group : m_optgroups) + ret_val = group->update_visibility(mode) || ret_val; + + m_show = ret_val; +} + Field* Page::get_field(const t_config_option_key& opt_key, int opt_index /*= -1*/) const { Field* field = nullptr; @@ -2704,8 +2763,22 @@ bool Page::set_value(const t_config_option_key& opt_key, const boost::any& value // package Slic3r::GUI::Tab::Page; ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_label_width /*= -1*/) { + auto extra_column = [](wxWindow* parent, const Line& line) + { + std::string bmp_name; + if (line.get_options().size() == 0) + bmp_name = "error.png"; + else { + auto mode = line.get_options()[0].opt.mode; //we assume that we have one option per line + bmp_name = mode == comExpert ? "mode_expert_.png" : + mode == comMiddle ? "mode_middle_.png" : "mode_simple_.png"; + } + auto bmp = new wxStaticBitmap(parent, wxID_ANY, wxBitmap(from_u8(var(bmp_name)), wxBITMAP_TYPE_PNG)); + return bmp; + }; + //! config_ have to be "right" - ConfigOptionsGroupShp optgroup = std::make_shared(this, title, m_config, true); + ConfigOptionsGroupShp optgroup = std::make_shared(this, title, m_config, true, extra_column); if (noncommon_label_width >= 0) optgroup->label_width = noncommon_label_width; @@ -2844,7 +2917,7 @@ void TabSLAMaterial::build() page = add_options_page(_(L("Dependencies")), "wrench.png"); optgroup = page->new_optgroup(_(L("Profile dependencies"))); - auto line = Line { _(L("Compatible printers")), "" }; + Line line = optgroup->create_single_option_line("compatible_printers");//Line { _(L("Compatible printers")), "" }; line.widget = [this](wxWindow* parent){ return compatible_printers_widget(parent, &m_compatible_printers_checkbox, &m_compatible_printers_btn); }; diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index fecb642f4..196114256 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -47,6 +47,7 @@ class Page : public wxScrolledWindow wxString m_title; size_t m_iconID; wxBoxSizer* m_vsizer; + bool m_show = true; public: Page(wxWindow* parent, const wxString title, const int iconID) : m_parent(parent), @@ -73,6 +74,7 @@ public: size_t iconID() const { return m_iconID; } void set_config(DynamicPrintConfig* config_in) { m_config = config_in; } void reload_config(); + void update_visibility(ConfigOptionMode mode); Field* get_field(const t_config_option_key& opt_key, int opt_index = -1) const; bool set_value(const t_config_option_key& opt_key, const boost::any& value); ConfigOptionsGroupShp new_optgroup(const wxString& title, int noncommon_label_width = -1); @@ -88,6 +90,7 @@ public: const wxColour get_item_colour() { return *m_item_color; } + bool get_show() const { return m_show; } protected: // Color of TreeCtrlItem. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one. @@ -214,6 +217,7 @@ public: void create_preset_tab(); void load_current_preset(); void rebuild_page_tree(bool tree_sel_change_event = false); + void update_page_tree_visibility(); void select_preset(std::string preset_name = ""); bool may_discard_current_dirty_preset(PresetCollection* presets = nullptr, const std::string& new_printer_name = ""); wxSizer* compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox, wxButton** btn); @@ -249,6 +253,7 @@ public: void update_tab_ui(); void load_config(const DynamicPrintConfig& config); virtual void reload_config(); + void update_visibility(ConfigOptionMode mode); Field* get_field(const t_config_option_key& opt_key, int opt_index = -1) const; bool set_value(const t_config_option_key& opt_key, const boost::any& value); wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText); diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 4f7f88602..27628c3db 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -11,7 +11,7 @@ #include "GUI_ObjectList.hpp" wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description, - std::function cb, const std::string& icon) + std::function cb, const std::string& icon, wxEvtHandler* event_handler) { if (id == wxID_ANY) id = wxNewId(); @@ -20,7 +20,26 @@ wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const if (!icon.empty()) item->SetBitmap(wxBitmap(Slic3r::var(icon), wxBITMAP_TYPE_PNG)); - menu->Bind(wxEVT_MENU, cb, id); + if (event_handler != nullptr) + event_handler->Bind(wxEVT_MENU, cb, id); + else + menu->Bind(wxEVT_MENU, cb, id); + + return item; +} + +wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxString& string, const wxString& description, const std::string& icon) +{ + if (id == wxID_ANY) + id = wxNewId(); + + wxMenuItem* item = new wxMenuItem(menu, id, string, description); + if (!icon.empty()) + item->SetBitmap(wxBitmap(Slic3r::var(icon), wxBITMAP_TYPE_PNG)); + + item->SetSubMenu(sub_menu); + menu->Append(item); + return item; } @@ -770,7 +789,7 @@ void PrusaObjectDataViewModel::GetItemInfo(const wxDataViewItem& item, ItemType& type = itUndef; PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - if (!node || node->GetIdx() < 0 && node->GetType() != itObject) + if (!node || node->GetIdx() < 0 && !(node->GetType() & (itObject|itSettings|itInstanceRoot))) return; idx = node->GetIdx(); diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index f39c38ab5..80a564fd0 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -12,9 +12,12 @@ #include #include +#include wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description, - std::function cb, const std::string& icon = ""); + std::function cb, const std::string& icon = "", wxEvtHandler* event_handler = nullptr); + +wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxString& string, const wxString& description, const std::string& icon = ""); class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup { @@ -259,7 +262,10 @@ public: m_name = "Settings to modified"; } else if (type == itInstanceRoot) { - m_name = "Instances"; + m_name = "Instances"; +#ifdef __WXGTK__ + m_container = true; +#endif //__WXGTK__ } else if (type == itInstance) { m_idx = parent->GetChildCount();