mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	Merge branch 'master' into tm_pad_improve
This commit is contained in:
		
						commit
						a25072f0a3
					
				
					 24 changed files with 618 additions and 202 deletions
				
			
		|  | @ -2,6 +2,8 @@ project(Slic3r) | ||||||
| cmake_minimum_required(VERSION 3.2) | cmake_minimum_required(VERSION 3.2) | ||||||
| 
 | 
 | ||||||
| include("version.inc") | include("version.inc") | ||||||
|  | include(GNUInstallDirs) | ||||||
|  | 
 | ||||||
| set(SLIC3R_RESOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/resources") | set(SLIC3R_RESOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/resources") | ||||||
| file(TO_NATIVE_PATH "${SLIC3R_RESOURCES_DIR}" SLIC3R_RESOURCES_DIR_WIN) | file(TO_NATIVE_PATH "${SLIC3R_RESOURCES_DIR}" SLIC3R_RESOURCES_DIR_WIN) | ||||||
| 
 | 
 | ||||||
|  | @ -22,6 +24,7 @@ endif() | ||||||
| 
 | 
 | ||||||
| option(SLIC3R_STATIC 			"Compile Slic3r with static libraries (Boost, TBB, glew)" ${SLIC3R_STATIC_INITIAL}) | option(SLIC3R_STATIC 			"Compile Slic3r with static libraries (Boost, TBB, glew)" ${SLIC3R_STATIC_INITIAL}) | ||||||
| option(SLIC3R_GUI    			"Compile Slic3r with GUI components (OpenGL, wxWidgets)" 1) | option(SLIC3R_GUI    			"Compile Slic3r with GUI components (OpenGL, wxWidgets)" 1) | ||||||
|  | option(SLIC3R_FHS               "Assume Slic3r is to be installed in a FHS directory structure" 0) | ||||||
| option(SLIC3R_PROFILE 			"Compile Slic3r with an invasive Shiny profiler" 0) | 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_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1) | ||||||
| option(SLIC3R_MSVC_PDB          "Generate PDB files on MSVC in Release mode" 1) | option(SLIC3R_MSVC_PDB          "Generate PDB files on MSVC in Release mode" 1) | ||||||
|  | @ -61,6 +64,8 @@ foreach (DIR ${PREFIX_PATH_CHECK}) | ||||||
|     endif () |     endif () | ||||||
| endforeach () | endforeach () | ||||||
| 
 | 
 | ||||||
|  | message(STATUS "SLIC3R_FHS: ${SLIC3R_FHS}") | ||||||
|  | 
 | ||||||
| # Add our own cmake module path. | # Add our own cmake module path. | ||||||
| list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules/) | list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules/) | ||||||
| 
 | 
 | ||||||
|  | @ -146,12 +151,14 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STRE | ||||||
| endif() | endif() | ||||||
| 
 | 
 | ||||||
| # Where all the bundled libraries reside? | # Where all the bundled libraries reside? | ||||||
| set(LIBDIR ${CMAKE_CURRENT_SOURCE_DIR}/src/) | set(LIBDIR ${CMAKE_CURRENT_SOURCE_DIR}/src) | ||||||
|  | set(LIBDIR_BIN ${CMAKE_CURRENT_BINARY_DIR}/src) | ||||||
| # For the bundled boost libraries (boost::nowide) | # For the bundled boost libraries (boost::nowide) | ||||||
| include_directories(${LIBDIR}) | include_directories(${LIBDIR}) | ||||||
|  | # For generated header files | ||||||
|  | include_directories(${LIBDIR_BIN}/platform) | ||||||
| # For libslic3r.h | # For libslic3r.h | ||||||
| include_directories(${LIBDIR}/clipper ${LIBDIR}/polypartition) | include_directories(${LIBDIR}/clipper ${LIBDIR}/polypartition) | ||||||
| #set(CMAKE_INCLUDE_CURRENT_DIR ON) |  | ||||||
| 
 | 
 | ||||||
| if(WIN32) | if(WIN32) | ||||||
|     # BOOST_ALL_NO_LIB: Avoid the automatic linking of Boost libraries on Windows. Rather rely on explicit linking. |     # BOOST_ALL_NO_LIB: Avoid the automatic linking of Boost libraries on Windows. Rather rely on explicit linking. | ||||||
|  | @ -215,7 +222,6 @@ endif() | ||||||
| # The Intel TBB library will use the std::exception_ptr feature of C++11. | # The Intel TBB library will use the std::exception_ptr feature of C++11. | ||||||
| add_definitions(-DTBB_USE_CAPTURED_EXCEPTION=0) | add_definitions(-DTBB_USE_CAPTURED_EXCEPTION=0) | ||||||
| 
 | 
 | ||||||
| #set(CURL_DEBUG 1) |  | ||||||
| find_package(CURL REQUIRED) | find_package(CURL REQUIRED) | ||||||
| include_directories(${CURL_INCLUDE_DIRS}) | include_directories(${CURL_INCLUDE_DIRS}) | ||||||
| 
 | 
 | ||||||
|  | @ -280,7 +286,6 @@ include_directories(${GLEW_INCLUDE_DIRS}) | ||||||
| # l10n | # l10n | ||||||
| set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization") | set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization") | ||||||
| add_custom_target(pot | add_custom_target(pot | ||||||
|     # FIXME: file list stale |  | ||||||
|     COMMAND xgettext --keyword=L --from-code=UTF-8 --debug |     COMMAND xgettext --keyword=L --from-code=UTF-8 --debug | ||||||
|         -f "${L10N_DIR}/list.txt" |         -f "${L10N_DIR}/list.txt" | ||||||
|         -o "${L10N_DIR}/Slic3rPE.pot" |         -o "${L10N_DIR}/Slic3rPE.pot" | ||||||
|  | @ -307,5 +312,12 @@ if(SLIC3R_BUILD_TESTS) | ||||||
|     add_subdirectory(tests) |     add_subdirectory(tests) | ||||||
| endif() | endif() | ||||||
| 
 | 
 | ||||||
| file(GLOB MyVar var/*.png) | 
 | ||||||
| install(FILES ${MyVar} DESTINATION share/slic3r-prusa3d) | # Resources install target, configure fhs.hpp on UNIX | ||||||
|  | if (WIN32) | ||||||
|  |     install(DIRECTORY "${SLIC3R_RESOURCES_DIR}/" DESTINATION "${CMAKE_INSTALL_PREFIX}/resources") | ||||||
|  | else () | ||||||
|  |     set(SLIC3R_FHS_RESOURCES "${CMAKE_INSTALL_FULL_DATAROOTDIR}/slic3r-prusa3d") | ||||||
|  |     install(DIRECTORY "${SLIC3R_RESOURCES_DIR}/" DESTINATION "${SLIC3R_FHS_RESOURCES}") | ||||||
|  | endif () | ||||||
|  | configure_file(${LIBDIR}/platform/unix/fhs.hpp.in ${LIBDIR_BIN}/platform/unix/fhs.hpp) | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								deps/CMakeLists.txt
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								deps/CMakeLists.txt
									
										
									
									
										vendored
									
									
								
							|  | @ -6,6 +6,7 @@ | ||||||
| # All the dependencies are installed in a `destdir` directory in the root of the build directory, | # All the dependencies are installed in a `destdir` directory in the root of the build directory, | ||||||
| # in a traditional Unix-style prefix structure. The destdir can be used directly by CMake | # in a traditional Unix-style prefix structure. The destdir can be used directly by CMake | ||||||
| # when building Slic3r - to do this, set the CMAKE_PREFIX_PATH to ${destdir}/usr/local. | # when building Slic3r - to do this, set the CMAKE_PREFIX_PATH to ${destdir}/usr/local. | ||||||
|  | # Warning: On UNIX/Linux, you also need to set -DSLIC3R_STATIC=1 when building Slic3r. | ||||||
| # | # | ||||||
| # For better clarity of console output, it's recommended to _not_ use a parallelized build | # For better clarity of console output, it's recommended to _not_ use a parallelized build | ||||||
| # for the top-level command, ie. use `make -j 1` or `ninja -j 1` to force single-threaded top-level | # for the top-level command, ie. use `make -j 1` or `ninja -j 1` to force single-threaded top-level | ||||||
|  |  | ||||||
							
								
								
									
										31
									
								
								deps/deps-windows.cmake
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										31
									
								
								deps/deps-windows.cmake
									
										
									
									
										vendored
									
									
								
							|  | @ -121,7 +121,8 @@ ExternalProject_Add(dep_zlib | ||||||
|     URL_HASH SHA256=4ff941449631ace0d4d203e3483be9dbc9da454084111f97ea0a2114e19bf066 |     URL_HASH SHA256=4ff941449631ace0d4d203e3483be9dbc9da454084111f97ea0a2114e19bf066 | ||||||
|     CMAKE_GENERATOR "${DEP_MSVC_GEN}" |     CMAKE_GENERATOR "${DEP_MSVC_GEN}" | ||||||
|     CMAKE_ARGS |     CMAKE_ARGS | ||||||
|         "-DINSTALL_BIN_DIR=${CMAKE_CURRENT_BINARY_DIR}\\fallout"   # I found no better way of preventing zlib creating & installing DLLs :-/ |         -DSKIP_INSTALL_FILES=ON                                    # Prevent installation of man pages et al. | ||||||
|  |         "-DINSTALL_BIN_DIR=${CMAKE_CURRENT_BINARY_DIR}\\fallout"   # I found no better way of preventing zlib from creating & installing DLLs :-/ | ||||||
|         -DCMAKE_POSITION_INDEPENDENT_CODE=ON |         -DCMAKE_POSITION_INDEPENDENT_CODE=ON | ||||||
|         "-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local" |         "-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local" | ||||||
|     BUILD_COMMAND msbuild /P:Configuration=Release INSTALL.vcxproj |     BUILD_COMMAND msbuild /P:Configuration=Release INSTALL.vcxproj | ||||||
|  | @ -136,6 +137,19 @@ if (${DEP_DEBUG}) | ||||||
|         WORKING_DIRECTORY "${BINARY_DIR}" |         WORKING_DIRECTORY "${BINARY_DIR}" | ||||||
|     ) |     ) | ||||||
| endif () | endif () | ||||||
|  | # The following steps are unfortunately needed to remove the _static suffix on libraries | ||||||
|  | ExternalProject_Add_Step(dep_zlib fix_static | ||||||
|  |     DEPENDEES install | ||||||
|  |     COMMAND "${CMAKE_COMMAND}" -E rename zlibstatic.lib zlib.lib | ||||||
|  |     WORKING_DIRECTORY "${DESTDIR}\\usr\\local\\lib\\" | ||||||
|  | ) | ||||||
|  | if (${DEP_DEBUG}) | ||||||
|  |     ExternalProject_Add_Step(dep_zlib fix_static_debug | ||||||
|  |         DEPENDEES install | ||||||
|  |         COMMAND "${CMAKE_COMMAND}" -E rename zlibstaticd.lib zlibd.lib | ||||||
|  |         WORKING_DIRECTORY "${DESTDIR}\\usr\\local\\lib\\" | ||||||
|  |     ) | ||||||
|  | endif () | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ExternalProject_Add(dep_libpng | ExternalProject_Add(dep_libpng | ||||||
|  | @ -147,6 +161,7 @@ ExternalProject_Add(dep_libpng | ||||||
|     CMAKE_ARGS |     CMAKE_ARGS | ||||||
|         -DPNG_SHARED=OFF |         -DPNG_SHARED=OFF | ||||||
|         -DPNG_TESTS=OFF |         -DPNG_TESTS=OFF | ||||||
|  |         -DSKIP_INSTALL_FILES=ON                                   # Prevent installation of man pages et al. | ||||||
|         -DCMAKE_POSITION_INDEPENDENT_CODE=ON |         -DCMAKE_POSITION_INDEPENDENT_CODE=ON | ||||||
|         "-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local" |         "-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local" | ||||||
|     BUILD_COMMAND msbuild /P:Configuration=Release INSTALL.vcxproj |     BUILD_COMMAND msbuild /P:Configuration=Release INSTALL.vcxproj | ||||||
|  | @ -161,6 +176,20 @@ if (${DEP_DEBUG}) | ||||||
|         WORKING_DIRECTORY "${BINARY_DIR}" |         WORKING_DIRECTORY "${BINARY_DIR}" | ||||||
|     ) |     ) | ||||||
| endif () | endif () | ||||||
|  | # The following steps are unfortunately needed to remove the _static suffix on libraries | ||||||
|  | # (And also overwrite the dynamic .lib) | ||||||
|  | ExternalProject_Add_Step(dep_libpng fix_static | ||||||
|  |     DEPENDEES install | ||||||
|  |     COMMAND "${CMAKE_COMMAND}" -E rename libpng16_static.lib libpng16.lib | ||||||
|  |     WORKING_DIRECTORY "${DESTDIR}\\usr\\local\\lib\\" | ||||||
|  | ) | ||||||
|  | if (${DEP_DEBUG}) | ||||||
|  |     ExternalProject_Add_Step(dep_libpng fix_static_debug | ||||||
|  |         DEPENDEES install | ||||||
|  |         COMMAND "${CMAKE_COMMAND}" -E rename libpng16_staticd.lib libpng16d.lib | ||||||
|  |         WORKING_DIRECTORY "${DESTDIR}\\usr\\local\\lib\\" | ||||||
|  |     ) | ||||||
|  | endif () | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| if (${DEPS_BITS} EQUAL 32) | if (${DEPS_BITS} EQUAL 32) | ||||||
|  |  | ||||||
							
								
								
									
										66
									
								
								doc/How to build - Linux et al.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								doc/How to build - Linux et al.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,66 @@ | ||||||
|  | 
 | ||||||
|  | # Building Slic3r PE on UNIX/Linux | ||||||
|  | 
 | ||||||
|  | Slic3r PE uses the CMake build system and requires several dependencies. | ||||||
|  | The dependencies can be listed in `deps/deps-linux.cmake`, although they don't necessarily need to be as recent | ||||||
|  | as the versions listed - generally versions available on conservative Linux distros such as Debian stable or CentOS should suffice. | ||||||
|  | 
 | ||||||
|  | Perl is not required any more. | ||||||
|  | 
 | ||||||
|  | In a typical situaction, one would open a command line, go to the Slic3r sources, create a directory called `build` or similar, | ||||||
|  | `cd` into it and call: | ||||||
|  | 
 | ||||||
|  |     cmake .. | ||||||
|  |     make -jN | ||||||
|  | 
 | ||||||
|  | where `N` is the number of CPU cores available. | ||||||
|  | 
 | ||||||
|  | Additional CMake flags may be applicable as explained below. | ||||||
|  | 
 | ||||||
|  | ### Dependenciy resolution | ||||||
|  | 
 | ||||||
|  | By default Slic3r looks for dependencies the default way CMake looks for them, ie. in default system locations. | ||||||
|  | On Linux this will typically make Slic3r depend on dynamically loaded libraries from the system, however, Slic3r can be told | ||||||
|  | to specifically look for static libraries with the `SLIC3R_STATIC` flag passed to cmake: | ||||||
|  | 
 | ||||||
|  |     cmake .. -DSLIC3R_STATIC=1 | ||||||
|  | 
 | ||||||
|  | Additionally, Slic3r can be built in a static manner mostly independent of the system libraries with a dependencies bundle | ||||||
|  | created using CMake script in the `deps` directory (these are not interconnected with the rest of the CMake scripts). | ||||||
|  | 
 | ||||||
|  | Note: We say _mostly independent_ because it's still expected the system will provide some transitive dependencies, such as GTK for wxWidgets. | ||||||
|  | 
 | ||||||
|  | To do this, go to the `deps` directory, create a `build` subdirectory (or the like) and use: | ||||||
|  | 
 | ||||||
|  |     cmake .. -DDESTDIR=<target destdir> | ||||||
|  | 
 | ||||||
|  | where the target destdir is a directory of your choosing where the dependencies will be installed. | ||||||
|  | You can also omit the `DESTDIR` option to use the default, in that case the `destdir` will be created inside the `build` directory where `cmake` is run. | ||||||
|  | 
 | ||||||
|  | To pass the destdir path to the top-level Slic3r CMake script, use the `CMAKE_PREFIX_PATH` option along with turning on `SLIC3R_STATIC`: | ||||||
|  | 
 | ||||||
|  |     cmake .. -DSLIC3R_STATIC=1 -DCMAKE_PREFIX_PATH=<path to destdir>/usr/local | ||||||
|  | 
 | ||||||
|  | Note that `/usr/local` needs to be appended to the destdir path and also the prefix path should be absolute. | ||||||
|  | 
 | ||||||
|  | **Warning**: Once the dependency bundle is installed in a destdir, the destdir cannot be moved elsewhere. | ||||||
|  | This is because wxWidgets hardcode the installation path. | ||||||
|  | 
 | ||||||
|  | ### Build variant | ||||||
|  | 
 | ||||||
|  | By default Scli3r builds the release variant. | ||||||
|  | To create a debug build, use the following CMake flag: | ||||||
|  | 
 | ||||||
|  |     -DCMAKE_BUILD_TYPE=Debug | ||||||
|  | 
 | ||||||
|  | ### Installation | ||||||
|  | 
 | ||||||
|  | In runtime, Slic3r needs a way to access its resource files. By default, it looks for a `resources` directory relative to its binary. | ||||||
|  | 
 | ||||||
|  | If you instead wnat Slic3r installed in a structure according to the Filesystem Hierarchy Standard, use the `SLIC3R_FHS` flag | ||||||
|  | 
 | ||||||
|  |     cmake .. -DSLIC3R_FHS=1 | ||||||
|  | 
 | ||||||
|  | This will make Slic3r look for a fixed-location `share/slic3r-prusa3d` directory instead (note that the location becomes hardcoded). | ||||||
|  | 
 | ||||||
|  | You can then use the `make install` target to install Slic3r. | ||||||
							
								
								
									
										62
									
								
								doc/How to build - Mac OS.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								doc/How to build - Mac OS.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,62 @@ | ||||||
|  | 
 | ||||||
|  | # Building Slic3r PE on Mac OS | ||||||
|  | 
 | ||||||
|  | To build Slic3r PE on Mac OS, you will need to install XCode and an appropriate SDK. | ||||||
|  | You will also need [CMake](https://cmake.org/) installed (available on Brew) and possibly git. | ||||||
|  | 
 | ||||||
|  | Currently Slic3r PE is built against the Mac OS X SDK version 10.9. | ||||||
|  | Building against older SDKs is unsupported. Building against newer SDKs might work, | ||||||
|  | but there may be subtle issues, such as dark mode not working very well on Mojave or other GUI problems. | ||||||
|  | 
 | ||||||
|  | You can obtain the SDK 10.9 for example [in this repository](https://github.com/phracker/MacOSX-SDKs). | ||||||
|  | If you don't already have the 10.9 version as part of your Mac OS installation, please download it | ||||||
|  | and place it into a reachable location. | ||||||
|  | 
 | ||||||
|  | The default location for Mac OS SDKs is: | ||||||
|  | 
 | ||||||
|  |     /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ | ||||||
|  | 
 | ||||||
|  | Wherever the 10.9 SDK is, please note down its location, it will be required to build Slic3r. | ||||||
|  | 
 | ||||||
|  | On my system, for example, the path to the SDK is | ||||||
|  | 
 | ||||||
|  |     /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk | ||||||
|  | 
 | ||||||
|  | ### Dependencies | ||||||
|  | 
 | ||||||
|  | Slic3r comes with a set of CMake scripts to build its dependencies, it lives in the `deps` directory. | ||||||
|  | Open a terminal window and navigate to Slic3r sources directory and then to `deps`. | ||||||
|  | Use the following commands to build the dependencies: | ||||||
|  | 
 | ||||||
|  |     mkdir build | ||||||
|  |     cd build | ||||||
|  |     cmake .. -DDEPS_OSX_SYSROOT=<path to the 10.9 SDK> | ||||||
|  | 
 | ||||||
|  | This will create a dependencies bundle inside the `build/destdir` directory. | ||||||
|  | You can also customize the bundle output path using the `-DDESTDIR=<some path>` option passed to `cmake`. | ||||||
|  | 
 | ||||||
|  | ### Building Slic3r | ||||||
|  | 
 | ||||||
|  | If dependencies built without an error, you can proceed to build Slic3r itself. | ||||||
|  | Go back to top level Slic3r sources directory and use these commands: | ||||||
|  | 
 | ||||||
|  |     mkdir build | ||||||
|  |     cd build | ||||||
|  |     cmake .. -DCMAKE_PREFIX_PATH="$PWD/../deps/build/destdir/usr/local" -DCMAKE_OSX_SYSROOT=<path to the 10.9 SDK> | ||||||
|  | 
 | ||||||
|  | The `CMAKE_PREFIX_PATH` is the path to the dependencies bundle but with `/usr/local` appended - if you set a custom path | ||||||
|  | using the `DESTDIR` option, you will need to change this accordingly. **Warning:** the `CMAKE_PREFIX_PATH` needs to be an absolute path. | ||||||
|  | 
 | ||||||
|  | The CMake command above prepares Slic3r for building from the command line. | ||||||
|  | To start the build, use | ||||||
|  | 
 | ||||||
|  |     make -jN | ||||||
|  | 
 | ||||||
|  | where `N` is the number of CPU cores, so, for example `make -j4` for a 4-core machine. | ||||||
|  | 
 | ||||||
|  | Alternatively, if you would like to use XCode GUI, modify the `cmake` command to include the `-GXcode` option: | ||||||
|  | 
 | ||||||
|  |     cmake .. -GXcode -DCMAKE_PREFIX_PATH="$PWD/../deps/build/destdir/usr/local" -DCMAKE_OSX_SYSROOT=<path to the 10.9 SDK> | ||||||
|  | 
 | ||||||
|  | and then open the `Slic3r.xcodeproj` file. | ||||||
|  | This should open up XCode where you can perform build using the GUI or perform other tasks. | ||||||
|  | @ -1,99 +1,98 @@ | ||||||
| 
 | 
 | ||||||
| ### NOTE: This document is currently outdated wrt. the new post-Perl Slic3rPE. A new build process and the description thereof is a work in progress and is comming soon. Please stay tuned. |  | ||||||
| 
 |  | ||||||
| -- |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Building Slic3r PE on Microsoft Windows | # Building Slic3r PE on Microsoft Windows | ||||||
| 
 | 
 | ||||||
| The currently supported way of building Slic3r PE on Windows is with CMake and MS Visual Studio 2013 | The currently supported way of building Slic3r PE on Windows is with CMake and MS Visual Studio 2013. | ||||||
| using our Perl binary distribution (compiled from official Perl sources). |  | ||||||
| You can use the free [Visual Studio 2013 Community Edition](https://www.visualstudio.com/vs/older-downloads/). | You can use the free [Visual Studio 2013 Community Edition](https://www.visualstudio.com/vs/older-downloads/). | ||||||
| CMake installer can be downloaded from [the official website](https://cmake.org/download/). | CMake installer can be downloaded from [the official website](https://cmake.org/download/). | ||||||
| 
 | 
 | ||||||
| Other setups (such as mingw + Strawberry Perl) _may_ work, but we cannot guarantee this will work | Building with newer versions of MSVS (2015, 2017) may work too as reported by some of our users. | ||||||
| and cannot provide guidance. |  | ||||||
| 
 | 
 | ||||||
|  | _Note:_ Thanks to **@supermerill** for testing and inspiration on this guide. | ||||||
| 
 | 
 | ||||||
| ### Geting the dependencies | ### Dependencies | ||||||
| 
 | 
 | ||||||
| First, download and upnack our Perl + wxWidgets binary distribution: | On Windows Slic3r is built against statically built libraries. | ||||||
|  | We provide a prebuilt package of all the needed dependencies. | ||||||
|  | The package comes in a several variants: | ||||||
| 
 | 
 | ||||||
|   - 32 bit, release mode: [wperl32-5.24.0-2018-03-02.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=wperl32-5.24.0-2018-03-02.7z) |   - [64 bit, Release mode only](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=destdir-64.7z) (41 MB, 578 MB unpacked) | ||||||
|   - 64 bit, release mode: [wperl64-5.24.0-2018-03-02.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=wperl64-5.24.0-2018-03-02.7z) |   - [64 bit, Release and Debug mode](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=destdir-64-dev.7z) (88 MB, 1.3 GB unpacked) | ||||||
|   - 64 bit, release mode + debug symbols: [wperl64d-5.24.0-2018-03-02.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=wperl64d-5.24.0-2018-03-02.7z) |   - [32 bit, Release mode only](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=destdir-32.7z) (38 MB, 520 MB unpacked) | ||||||
|  |   - [32 bit, Release and Debug mode](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=destdir-32-dev.7z) (74 MB, 1.1 GB unpacked) | ||||||
| 
 | 
 | ||||||
| It is recommended to unpack this package into `C:\`. | When unsure, use the _Release mode only_ variant, the _Release and Debug_ variant is only needed for debugging & developement. | ||||||
| 
 | 
 | ||||||
| Apart from wxWidgets and Perl, you will also need additional dependencies: | If you're unsure where to unpack the package, unpack it into `C:\local\` (but it can really be anywhere). | ||||||
| 
 | 
 | ||||||
|   - Boost | Alternatively you can also compile the dependencies yourself, see below. | ||||||
|   - Intel TBB |  | ||||||
|   - libcurl |  | ||||||
| 
 | 
 | ||||||
| We have prepared a binary package of the listed libraries: | ### Building Slic3r PE with Visual Studio | ||||||
| 
 | 
 | ||||||
|   - 32 bit: [slic3r-destdir-32.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=2%2Fslic3r-destdir-32.7z) | First obtain the Slic3 PE sources via either git or by extracting the source archive. | ||||||
|   - 64 bit: [slic3r-destdir-64.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=2%2Fslic3r-destdir-64.7z) |  | ||||||
| 
 | 
 | ||||||
| It is recommended you unpack this package into `C:\local\` as the environment | Then you will need to note down the so-called 'prefix path' to the dependencies, this is the location of the dependencies packages + `\usr\local` appended. | ||||||
| setup script expects it there. | For example on 64 bits this would be `C:\local\destdir-64\usr\local`. The prefix path will need to be passed to CMake. | ||||||
| 
 | 
 | ||||||
| Alternatively you can also compile the additional dependencies yourself. | When ready, open the relevant Visual Studio command line and `cd` into the directory with Slic3r sources. | ||||||
| There is a [powershell script](./deps-build/windows/slic3r-makedeps.ps1) which automates this process. | Use these commands to prepare Visual Studio solution file: | ||||||
| 
 | 
 | ||||||
| ### Building Slic3r PE |  | ||||||
| 
 |  | ||||||
| Once the dependencies are set up in their respective locations, |  | ||||||
| go to the `wperl*` directory extracted earlier and launch the `cmdline.lnk` file |  | ||||||
| which opens a command line prompt with appropriate environment variables set up. |  | ||||||
| 
 |  | ||||||
| In this command line, `cd` into the directory with Slic3r sources |  | ||||||
| and use these commands to build the Slic3r from the command line: |  | ||||||
| 
 |  | ||||||
|     perl Build.PL |  | ||||||
|     perl Build.PL --gui |  | ||||||
|     mkdir build |     mkdir build | ||||||
|     cd build |     cd build | ||||||
|     cmake .. -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release |     cmake .. -G "Visual Studio 12 Win64" -DCMAKE_PREFIX_PATH="<insert prefix path here>" | ||||||
|     nmake |  | ||||||
|     cd .. |  | ||||||
|     perl slic3r.pl |  | ||||||
| 
 | 
 | ||||||
| The above commands use `nmake` Makefiles. | Note that if you're building a 32-bit variant, you will need to change the `"Visual Studio 12 Win64"` to just `"Visual Studio 12"`. | ||||||
| You may also build Slic3r PE with other build tools: |  | ||||||
| 
 | 
 | ||||||
|  | Conversely, if you're using Visual Studio version other than 2013, the version number will need to be changed accordingly. | ||||||
| 
 | 
 | ||||||
| ### Building with Visual Studio | If `cmake` has finished without errors, go to the build directory and open the `Slic3r.sln` solution file in Visual Studio. | ||||||
|  | Before building, make sure you're building the right project (use one of those starting with `slic3r_app_...`) and that you're building | ||||||
|  | with the right configuration, ie. _Release_ vs. _Debug_. When unsure, choose _Release_. | ||||||
|  | Note that you won't be able to build a _Debug_ variant against a _Release_-only dependencies package. | ||||||
| 
 | 
 | ||||||
| To build and debug Slic3r PE with Visual Studio (64 bits), replace the `cmake` command with: | #### Installing using the `INSTALL` project | ||||||
| 
 | 
 | ||||||
|     cmake .. -G "Visual Studio 12 Win64" -DCMAKE_CONFIGURATION_TYPES=RelWithDebInfo | Slic3r PE can be run from the Visual Studio or from Visual Studio's build directory (`src\Release` or `src\Debug`), | ||||||
|  | but for longer-term usage you migth want to install somewhere using the `INSTALL` project. | ||||||
|  | By default, this installs into `C:\Program Files\Slic3r`. | ||||||
|  | To customize the install path, use the `-DCMAKE_INSTALL_PREFIX=<path of your choice>` when invoking `cmake`. | ||||||
| 
 | 
 | ||||||
| For the 32-bit variant, use: | ### Building from the command line | ||||||
| 
 | 
 | ||||||
|     cmake .. -G "Visual Studio 12" -DCMAKE_CONFIGURATION_TYPES=RelWithDebInfo | There are several options for building from the command line: | ||||||
| 
 | 
 | ||||||
| After `cmake` has finished, go to the build directory and open the `Slic3r.sln` solution file. | - [msbuild](https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-reference?view=vs-2017&viewFallbackFrom=vs-2013) | ||||||
| This should open Visual Studio and load the Slic3r solution containing all the projects. | - [Ninja](https://ninja-build.org/) | ||||||
| Make sure you use Visual Studio 2013 to open the solution. | - [nmake](https://docs.microsoft.com/en-us/cpp/build/nmake-reference?view=vs-2017) | ||||||
| 
 | 
 | ||||||
| You can then use the usual Visual Studio controls to build Slic3r (Hit `F5` to build and run with debugger). | To build with msbuild, use the same CMake command as in previous paragraph and then build using | ||||||
| If you want to run or debug Slic3r from within Visual Studio, make sure the `XS` project is activated. |  | ||||||
| It should be set as the Startup project by CMake by default, but you might want to check anyway. |  | ||||||
| There are multiple projects in the Slic3r solution, but only the `XS` project is configured with the right |  | ||||||
| commands to run and debug Slic3r. |  | ||||||
| 
 | 
 | ||||||
| The above cmake commands generate Visual Studio project files with the `RelWithDebInfo` configuration only. |     msbuild /P:Configuration=Release ALL_BUILD.vcxproj | ||||||
| If you also want to use the `Release` configuration, you can generate Visual Studio projects with: |  | ||||||
| 
 | 
 | ||||||
|     -DCMAKE_CONFIGURATION_TYPES=Release;RelWithDebInfo | To build with Ninja or nmake, replace the `-G` option in the CMake call with `-G Ninja` or `-G "NMake Makefiles"` , respectively. | ||||||
|  | Then use either `ninja` or `nmake` to start the build. | ||||||
| 
 | 
 | ||||||
| (The `Debug` configuration is not supported as of now.) | To install, use `msbuild /P:Configuration=Release INSTALL.vcxproj` , `ninja install` , or `nmake install` . | ||||||
| 
 | 
 | ||||||
| ### Building with ninja | ### Building the dependencies package yourself | ||||||
| 
 | 
 | ||||||
| To use [Ninja](https://ninja-build.org/), replace the `cmake` and `nmake` commands with: | The dependencies package is built using CMake scripts inside the `deps` subdirectory of Slic3r PE sources. | ||||||
|  | (This is intentionally not interconnected with the CMake scripts in the rest of the sources.) | ||||||
| 
 | 
 | ||||||
|     cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release | Open the preferred Visual Studio command line (64 or 32 bit variant) and `cd` into the directory with Slic3r sources. | ||||||
|     ninja | Then `cd` into the `deps` directory and use these commands to build: | ||||||
|  | 
 | ||||||
|  |     mkdir build | ||||||
|  |     cd build | ||||||
|  |     cmake .. -G "Visual Studio 12 Win64" -DDESTDIR="C:\local\destdir-custom" | ||||||
|  |     msbuild ALL_BUILD.vcxproj | ||||||
|  | 
 | ||||||
|  | You can also use the Visual Studio GUI or other generators as mentioned above. | ||||||
|  | 
 | ||||||
|  | The `DESTDIR` option is the location where the bundle will be installed. | ||||||
|  | This may be customized. If you leave it empty, the `DESTDIR` will be places inside the same `build` directory. | ||||||
|  | 
 | ||||||
|  | Note that the build variant that you may choose using Visual Studio (ie. _Release_ or _Debug_ etc.) when building the dependency package is **not relevant**. | ||||||
|  | The dependency build will by default build _both_ the _Release_ and _Debug_ variants regardless of what you choose in Visual Studio. | ||||||
|  | You can disable building of the debug variant by passing the `-DDEP_DEBUG=OFF` option to CMake, this will only produce a _Release_ build. | ||||||
|  | 
 | ||||||
|  | Refer to the CMake scripts inside the `deps` directory to see which dependencies are built in what versions and how this is done. | ||||||
|  |  | ||||||
|  | @ -33,6 +33,7 @@ if(PNG_FOUND AND NOT RASTERIZER_FORCE_BUILTIN_LIBPNG) | ||||||
| else() | else() | ||||||
|     set(ZLIB_LIBRARY "") |     set(ZLIB_LIBRARY "") | ||||||
|     message(WARNING "Using builtin libpng. This can cause crashes on some platforms.") |     message(WARNING "Using builtin libpng. This can cause crashes on some platforms.") | ||||||
|  |     set(SKIP_INSTALL_ALL 1)   # Prevent png+zlib from creating install targets | ||||||
|     add_subdirectory(png/zlib) |     add_subdirectory(png/zlib) | ||||||
|     set(ZLIB_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/png/zlib ${CMAKE_CURRENT_BINARY_DIR}/png/zlib) |     set(ZLIB_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/png/zlib ${CMAKE_CURRENT_BINARY_DIR}/png/zlib) | ||||||
|     include_directories(${ZLIB_INCLUDE_DIR}) |     include_directories(${ZLIB_INCLUDE_DIR}) | ||||||
|  | @ -188,3 +189,15 @@ else () | ||||||
|         VERBATIM |         VERBATIM | ||||||
|     ) |     ) | ||||||
| endif() | endif() | ||||||
|  | 
 | ||||||
|  | # Slic3r binary install target | ||||||
|  | if (WIN32) | ||||||
|  |     install(TARGETS slic3r RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}") | ||||||
|  |     if (MSVC) | ||||||
|  |         install(TARGETS slic3r_app_gui RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}") | ||||||
|  |         install(TARGETS slic3r_app_console RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}") | ||||||
|  |         install(TARGETS slic3r_app_noconsole RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}") | ||||||
|  |     endif () | ||||||
|  | else () | ||||||
|  |     install(TARGETS slic3r RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") | ||||||
|  | endif () | ||||||
|  |  | ||||||
|  | @ -218,7 +218,7 @@ public: | ||||||
| 
 | 
 | ||||||
|     const T& get_at(size_t i) const { return const_cast<ConfigOptionVector<T>*>(this)->get_at(i); } |     const T& get_at(size_t i) const { return const_cast<ConfigOptionVector<T>*>(this)->get_at(i); } | ||||||
| 
 | 
 | ||||||
|     // Resize this vector by duplicating the last value.
 |     // Resize this vector by duplicating the /*last*/first value.
 | ||||||
|     // If the current vector is empty, the default value is used instead.
 |     // If the current vector is empty, the default value is used instead.
 | ||||||
|     void resize(size_t n, const ConfigOption *opt_default = nullptr) override |     void resize(size_t n, const ConfigOption *opt_default = nullptr) override | ||||||
|     { |     { | ||||||
|  | @ -238,7 +238,7 @@ public: | ||||||
|                 this->values.resize(n, static_cast<const ConfigOptionVector<T>*>(opt_default)->values.front()); |                 this->values.resize(n, static_cast<const ConfigOptionVector<T>*>(opt_default)->values.front()); | ||||||
|             } else { |             } else { | ||||||
|                 // Resize by duplicating the last value.
 |                 // Resize by duplicating the last value.
 | ||||||
|                 this->values.resize(n, this->values.back()); |                 this->values.resize(n, this->values./*back*/front()); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -204,7 +204,9 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T | ||||||
|     if (! start_filament_gcode.empty()) { |     if (! start_filament_gcode.empty()) { | ||||||
|         // Process the start_filament_gcode for the active filament only.
 |         // Process the start_filament_gcode for the active filament only.
 | ||||||
|         gcodegen.placeholder_parser().set("current_extruder", new_extruder_id); |         gcodegen.placeholder_parser().set("current_extruder", new_extruder_id); | ||||||
|         gcode += gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id); |         DynamicConfig config; | ||||||
|  |         config.set_key_value("filament_extruder_id", new ConfigOptionInt(new_extruder_id)); | ||||||
|  |         gcode += gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id, &config); | ||||||
|         check_add_eol(gcode); |         check_add_eol(gcode); | ||||||
|     } |     } | ||||||
|     // A phony move to the end position at the wipe tower.
 |     // A phony move to the end position at the wipe tower.
 | ||||||
|  | @ -787,11 +789,17 @@ void GCode::_do_export(Print &print, FILE *file) | ||||||
|             // Wipe tower will control the extruder switching, it will call the start_filament_gcode.
 |             // Wipe tower will control the extruder switching, it will call the start_filament_gcode.
 | ||||||
|         } else { |         } else { | ||||||
|             // Only initialize the initial extruder.
 |             // Only initialize the initial extruder.
 | ||||||
|             _writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id)); |             DynamicConfig config; | ||||||
|  |             config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(initial_extruder_id))); | ||||||
|  | 			_writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config)); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         for (const std::string &start_gcode : print.config().start_filament_gcode.values) |         DynamicConfig config; | ||||||
|             _writeln(file, this->placeholder_parser_process("start_filament_gcode", start_gcode, (unsigned int)(&start_gcode - &print.config().start_filament_gcode.values.front()))); |         for (const std::string &start_gcode : print.config().start_filament_gcode.values) { | ||||||
|  |             int extruder_id = (unsigned int)(&start_gcode - &print.config().start_filament_gcode.values.front()); | ||||||
|  |             config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id)); | ||||||
|  |             _writeln(file, this->placeholder_parser_process("start_filament_gcode", start_gcode, extruder_id, &config)); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true); |     this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true); | ||||||
|     print.throw_if_canceled(); |     print.throw_if_canceled(); | ||||||
|  | @ -809,7 +817,7 @@ void GCode::_do_export(Print &print, FILE *file) | ||||||
|                 for (const ExPolygon &expoly : layer->slices.expolygons) |                 for (const ExPolygon &expoly : layer->slices.expolygons) | ||||||
|                     for (const Point © : object->copies()) { |                     for (const Point © : object->copies()) { | ||||||
|                         islands.emplace_back(expoly.contour); |                         islands.emplace_back(expoly.contour); | ||||||
|                         islands.back().translate(- copy); |                         islands.back().translate(copy); | ||||||
|                     } |                     } | ||||||
|         //FIXME Mege the islands in parallel.
 |         //FIXME Mege the islands in parallel.
 | ||||||
|         m_avoid_crossing_perimeters.init_external_mp(union_ex(islands)); |         m_avoid_crossing_perimeters.init_external_mp(union_ex(islands)); | ||||||
|  | @ -990,10 +998,15 @@ void GCode::_do_export(Print &print, FILE *file) | ||||||
|         config.set_key_value("layer_z",   new ConfigOptionFloat(m_writer.get_position()(2) - m_config.z_offset.value)); |         config.set_key_value("layer_z",   new ConfigOptionFloat(m_writer.get_position()(2) - m_config.z_offset.value)); | ||||||
|         if (print.config().single_extruder_multi_material) { |         if (print.config().single_extruder_multi_material) { | ||||||
|             // Process the end_filament_gcode for the active filament only.
 |             // Process the end_filament_gcode for the active filament only.
 | ||||||
|             _writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config().end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id(), &config)); |             int extruder_id = m_writer.extruder()->id(); | ||||||
|  |             config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id)); | ||||||
|  |             _writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config().end_filament_gcode.get_at(extruder_id), extruder_id, &config)); | ||||||
|         } else { |         } else { | ||||||
|             for (const std::string &end_gcode : print.config().end_filament_gcode.values) |             for (const std::string &end_gcode : print.config().end_filament_gcode.values) { | ||||||
|                 _writeln(file, this->placeholder_parser_process("end_filament_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config().end_filament_gcode.values.front()), &config)); | 				int extruder_id = (unsigned int)(&end_gcode - &print.config().end_filament_gcode.values.front()); | ||||||
|  |                 config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id)); | ||||||
|  |                 _writeln(file, this->placeholder_parser_process("end_filament_gcode", end_gcode, extruder_id, &config)); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|         _writeln(file, this->placeholder_parser_process("end_gcode", print.config().end_gcode, m_writer.extruder()->id(), &config)); |         _writeln(file, this->placeholder_parser_process("end_gcode", print.config().end_gcode, m_writer.extruder()->id(), &config)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -1194,7 +1194,7 @@ Vec3d extract_euler_angles(const Eigen::Matrix<double, 3, 3, Eigen::DontAlign>& | ||||||
|     } |     } | ||||||
|     else |     else | ||||||
|     { |     { | ||||||
|         angles(1) = 0.0; |         angles(0) = 0.0; | ||||||
|         angles(1) = ::atan2(-rotation_matrix(2, 0), sy); |         angles(1) = ::atan2(-rotation_matrix(2, 0), sy); | ||||||
|         angles(2) = (angles(1) >-0.0) ? ::atan2(rotation_matrix(1, 2), rotation_matrix(1, 1)) : ::atan2(-rotation_matrix(1, 2), rotation_matrix(1, 1)); |         angles(2) = (angles(1) >-0.0) ? ::atan2(rotation_matrix(1, 2), rotation_matrix(1, 1)) : ::atan2(-rotation_matrix(1, 2), rotation_matrix(1, 1)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								src/platform/unix/fhs.hpp.in
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/platform/unix/fhs.hpp.in
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | ||||||
|  | #cmakedefine SLIC3R_FHS @SLIC3R_FHS@ | ||||||
|  | #define SLIC3R_FHS_RESOURCES "@SLIC3R_FHS_RESOURCES@" | ||||||
|  | @ -22,6 +22,8 @@ | ||||||
| #include <boost/nowide/cenv.hpp> | #include <boost/nowide/cenv.hpp> | ||||||
| #include <boost/nowide/iostream.hpp> | #include <boost/nowide/iostream.hpp> | ||||||
| 
 | 
 | ||||||
|  | #include "unix/fhs.hpp"  // Generated by CMake from ../platform/unix/fhs.hpp.in
 | ||||||
|  | 
 | ||||||
| #include "libslic3r/libslic3r.h" | #include "libslic3r/libslic3r.h" | ||||||
| #include "libslic3r/Config.hpp" | #include "libslic3r/Config.hpp" | ||||||
| #include "libslic3r/Geometry.hpp" | #include "libslic3r/Geometry.hpp" | ||||||
|  | @ -77,6 +79,10 @@ int main(int argc, char **argv) | ||||||
|     // The resources are packed to 'resources'
 |     // The resources are packed to 'resources'
 | ||||||
|     // Path from Slic3r binary to resources:
 |     // Path from Slic3r binary to resources:
 | ||||||
|     boost::filesystem::path path_resources = path_to_binary.parent_path() / "resources"; |     boost::filesystem::path path_resources = path_to_binary.parent_path() / "resources"; | ||||||
|  | #elif defined SLIC3R_FHS | ||||||
|  |     // The application is packaged according to the Linux Filesystem Hierarchy Standard
 | ||||||
|  |     // Resources are set to the 'Architecture-independent (shared) data', typically /usr/share or /usr/local/share
 | ||||||
|  |     boost::filesystem::path path_resources = SLIC3R_FHS_RESOURCES; | ||||||
| #else | #else | ||||||
|     // The application is packed in the .tar.bz archive (or in AppImage) as 'bin/slic3r',
 |     // The application is packed in the .tar.bz archive (or in AppImage) as 'bin/slic3r',
 | ||||||
|     // The resources are packed to 'resources'
 |     // The resources are packed to 'resources'
 | ||||||
|  |  | ||||||
|  | @ -66,12 +66,8 @@ void Field::PostInitialize() | ||||||
| 	BUILD(); | 	BUILD(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Field::on_kill_focus(wxEvent& event) | void Field::on_kill_focus() | ||||||
| { | { | ||||||
|     // Without this, there will be nasty focus bugs on Windows.
 |  | ||||||
|     // Also, docs for wxEvent::Skip() say "In general, it is recommended to skip all 
 |  | ||||||
|     // non-command events to allow the default handling to take place."
 |  | ||||||
| 	event.Skip(); |  | ||||||
| 	// call the registered function if it is available
 | 	// call the registered function if it is available
 | ||||||
|     if (m_on_kill_focus!=nullptr)  |     if (m_on_kill_focus!=nullptr)  | ||||||
|         m_on_kill_focus(m_opt_id); |         m_on_kill_focus(m_opt_id); | ||||||
|  | @ -250,11 +246,23 @@ void TextCtrl::BUILD() { | ||||||
| 		break;  | 		break;  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|     const long style = m_opt.multiline ? wxTE_MULTILINE : 0; |     const long style = m_opt.multiline ? wxTE_MULTILINE : wxTE_PROCESS_ENTER/*0*/; | ||||||
| 	auto temp = new wxTextCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, style); | 	auto temp = new wxTextCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, style); | ||||||
| 
 | 
 | ||||||
| 	temp->SetToolTip(get_tooltip_text(text_value)); | 	temp->SetToolTip(get_tooltip_text(text_value)); | ||||||
| 
 | 
 | ||||||
|  |     if (style == wxTE_PROCESS_ENTER) { | ||||||
|  |         temp->Bind(wxEVT_TEXT_ENTER, ([this, temp](wxEvent& e) | ||||||
|  |         { | ||||||
|  | #if !defined(__WXGTK__) | ||||||
|  |             e.Skip(); | ||||||
|  |             temp->GetToolTip()->Enable(true); | ||||||
|  | #endif // __WXGTK__
 | ||||||
|  |             propagate_value(); | ||||||
|  |             bEnterPressed = true; | ||||||
|  |         }), temp->GetId()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     temp->Bind(wxEVT_SET_FOCUS, ([this](wxEvent& e) { on_set_focus(e); }), temp->GetId()); |     temp->Bind(wxEVT_SET_FOCUS, ([this](wxEvent& e) { on_set_focus(e); }), temp->GetId()); | ||||||
|      |      | ||||||
| 	temp->Bind(wxEVT_LEFT_DOWN, ([temp](wxEvent& event) | 	temp->Bind(wxEVT_LEFT_DOWN, ([temp](wxEvent& event) | ||||||
|  | @ -272,14 +280,15 @@ void TextCtrl::BUILD() { | ||||||
| 
 | 
 | ||||||
| 	temp->Bind(wxEVT_KILL_FOCUS, ([this, temp](wxEvent& e) | 	temp->Bind(wxEVT_KILL_FOCUS, ([this, temp](wxEvent& e) | ||||||
| 	{ | 	{ | ||||||
| #if !defined(__WXGTK__) |  | ||||||
| 		e.Skip(); | 		e.Skip(); | ||||||
|  | #if !defined(__WXGTK__) | ||||||
| 		temp->GetToolTip()->Enable(true); | 		temp->GetToolTip()->Enable(true); | ||||||
| #endif // __WXGTK__
 | #endif // __WXGTK__
 | ||||||
|         if (is_defined_input_value<wxTextCtrl>(window, m_opt.type)) |         if (bEnterPressed) { | ||||||
|             on_change_field(); |             bEnterPressed = false; | ||||||
|         else |             return; | ||||||
|             on_kill_focus(e); |         } | ||||||
|  |         propagate_value(); | ||||||
| 	}), temp->GetId()); | 	}), temp->GetId()); | ||||||
|     /*
 |     /*
 | ||||||
|         temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent& evt) |         temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent& evt) | ||||||
|  | @ -311,6 +320,14 @@ void TextCtrl::BUILD() { | ||||||
|     window = dynamic_cast<wxWindow*>(temp); |     window = dynamic_cast<wxWindow*>(temp); | ||||||
| }	 | }	 | ||||||
| 
 | 
 | ||||||
|  | void TextCtrl::propagate_value() | ||||||
|  | { | ||||||
|  |     if (is_defined_input_value<wxTextCtrl>(window, m_opt.type)) | ||||||
|  |         on_change_field(); | ||||||
|  |     else | ||||||
|  |         on_kill_focus(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| boost::any& TextCtrl::get_value() | boost::any& TextCtrl::get_value() | ||||||
| { | { | ||||||
| 	wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue(); | 	wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue(); | ||||||
|  | @ -398,7 +415,7 @@ void SpinCtrl::BUILD() { | ||||||
| 	const int max_val = m_opt.max < 2147483647 ? m_opt.max : 2147483647; | 	const int max_val = m_opt.max < 2147483647 ? m_opt.max : 2147483647; | ||||||
| 
 | 
 | ||||||
| 	auto temp = new wxSpinCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, | 	auto temp = new wxSpinCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, | ||||||
| 		0, min_val, max_val, default_value); | 		0|wxTE_PROCESS_ENTER, min_val, max_val, default_value); | ||||||
| 
 | 
 | ||||||
| #ifndef __WXOSX__ | #ifndef __WXOSX__ | ||||||
|     // #ys_FIXME_KILL_FOCUS 
 |     // #ys_FIXME_KILL_FOCUS 
 | ||||||
|  | @ -407,15 +424,23 @@ void SpinCtrl::BUILD() { | ||||||
|     // and on TEXT event under OSX
 |     // and on TEXT event under OSX
 | ||||||
| 	temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) | 	temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) | ||||||
| 	{ | 	{ | ||||||
|         if (tmp_value < 0) |         e.Skip(); | ||||||
| 	        on_kill_focus(e); |         if (bEnterPressed) { | ||||||
|         else { |             bEnterPressed = false; | ||||||
|             e.Skip(); |             return; | ||||||
|             on_change_field(); |  | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         propagate_value(); | ||||||
| 	}), temp->GetId()); | 	}), temp->GetId()); | ||||||
| 
 | 
 | ||||||
| 	temp->Bind(wxEVT_SPINCTRL, ([this](wxCommandEvent e) {  on_change_field();  }), temp->GetId()); |     temp->Bind(wxEVT_SPINCTRL, ([this](wxCommandEvent e) {  on_change_field();  }), temp->GetId());  | ||||||
|  |      | ||||||
|  |     temp->Bind(wxEVT_TEXT_ENTER, ([this](wxCommandEvent e) | ||||||
|  |     { | ||||||
|  |         e.Skip(); | ||||||
|  |         propagate_value(); | ||||||
|  |         bEnterPressed = true; | ||||||
|  |     }), temp->GetId()); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| 	temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) | 	temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) | ||||||
|  | @ -430,12 +455,7 @@ void SpinCtrl::BUILD() { | ||||||
| 			tmp_value = std::stoi(value); | 			tmp_value = std::stoi(value); | ||||||
|         else tmp_value = -9999; |         else tmp_value = -9999; | ||||||
| #ifdef __WXOSX__ | #ifdef __WXOSX__ | ||||||
|         if (tmp_value < 0) { |         propagate_value(); | ||||||
|             if (m_on_kill_focus != nullptr) |  | ||||||
|                 m_on_kill_focus(m_opt_id); |  | ||||||
|         } |  | ||||||
|         else  |  | ||||||
|             on_change_field(); |  | ||||||
| #endif | #endif | ||||||
| 	}), temp->GetId()); | 	}), temp->GetId()); | ||||||
| 	 | 	 | ||||||
|  | @ -445,6 +465,14 @@ void SpinCtrl::BUILD() { | ||||||
| 	window = dynamic_cast<wxWindow*>(temp); | 	window = dynamic_cast<wxWindow*>(temp); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void SpinCtrl::propagate_value() | ||||||
|  | { | ||||||
|  |     if (tmp_value < 0) | ||||||
|  |         on_kill_focus(); | ||||||
|  |     else | ||||||
|  |         on_change_field(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void Choice::BUILD() { | void Choice::BUILD() { | ||||||
| 	auto size = wxSize(wxDefaultSize); | 	auto size = wxSize(wxDefaultSize); | ||||||
| 	if (m_opt.height >= 0) size.SetHeight(m_opt.height); | 	if (m_opt.height >= 0) size.SetHeight(m_opt.height); | ||||||
|  | @ -474,6 +502,7 @@ void Choice::BUILD() { | ||||||
|     if (temp->GetWindowStyle() != wxCB_READONLY) { |     if (temp->GetWindowStyle() != wxCB_READONLY) { | ||||||
|         temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { |         temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { | ||||||
|             e.Skip(); |             e.Skip(); | ||||||
|  |             if (m_opt.type == coStrings) return; | ||||||
|             double old_val = !m_value.empty() ? boost::any_cast<double>(m_value) : -99999; |             double old_val = !m_value.empty() ? boost::any_cast<double>(m_value) : -99999; | ||||||
|             if (is_defined_input_value<wxComboBox>(window, m_opt.type)) { |             if (is_defined_input_value<wxComboBox>(window, m_opt.type)) { | ||||||
|                 if (fabs(old_val - boost::any_cast<double>(get_value())) <= 0.0001) |                 if (fabs(old_val - boost::any_cast<double>(get_value())) <= 0.0001) | ||||||
|  | @ -482,7 +511,7 @@ void Choice::BUILD() { | ||||||
|                     on_change_field(); |                     on_change_field(); | ||||||
|             } |             } | ||||||
|             else |             else | ||||||
|                 on_kill_focus(e); |                 on_kill_focus(); | ||||||
|         }), temp->GetId()); |         }), temp->GetId()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -692,7 +721,7 @@ boost::any& Choice::get_value() | ||||||
| 	} | 	} | ||||||
|     else if (m_opt.gui_type == "f_enum_open") { |     else if (m_opt.gui_type == "f_enum_open") { | ||||||
|         const int ret_enum = static_cast<wxComboBox*>(window)->GetSelection(); |         const int ret_enum = static_cast<wxComboBox*>(window)->GetSelection(); | ||||||
|         if (ret_enum < 0 || m_opt.enum_values.empty()) |         if (ret_enum < 0 || m_opt.enum_values.empty() || m_opt.type == coStrings) | ||||||
|             get_value_by_opt_type(ret_str); |             get_value_by_opt_type(ret_str); | ||||||
|         else  |         else  | ||||||
|             m_value = atof(m_opt.enum_values[ret_enum].c_str()); |             m_value = atof(m_opt.enum_values[ret_enum].c_str()); | ||||||
|  | @ -754,8 +783,8 @@ void PointCtrl::BUILD() | ||||||
| 	val = default_pt(1); | 	val = default_pt(1); | ||||||
| 	wxString Y = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None); | 	wxString Y = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None); | ||||||
| 
 | 
 | ||||||
| 	x_textctrl = new wxTextCtrl(m_parent, wxID_ANY, X, wxDefaultPosition, field_size); | 	x_textctrl = new wxTextCtrl(m_parent, wxID_ANY, X, wxDefaultPosition, field_size, wxEVT_TEXT_ENTER); | ||||||
| 	y_textctrl = new wxTextCtrl(m_parent, wxID_ANY, Y, wxDefaultPosition, field_size); | 	y_textctrl = new wxTextCtrl(m_parent, wxID_ANY, Y, wxDefaultPosition, field_size, wxEVT_TEXT_ENTER); | ||||||
| 
 | 
 | ||||||
| 	temp->Add(new wxStaticText(m_parent, wxID_ANY, "x : "), 0, wxALIGN_CENTER_VERTICAL, 0); | 	temp->Add(new wxStaticText(m_parent, wxID_ANY, "x : "), 0, wxALIGN_CENTER_VERTICAL, 0); | ||||||
| 	temp->Add(x_textctrl); | 	temp->Add(x_textctrl); | ||||||
|  | @ -765,8 +794,11 @@ void PointCtrl::BUILD() | ||||||
| // 	x_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), x_textctrl->GetId());
 | // 	x_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), x_textctrl->GetId());
 | ||||||
| // 	y_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), y_textctrl->GetId());
 | // 	y_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), y_textctrl->GetId());
 | ||||||
| 
 | 
 | ||||||
|     x_textctrl->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { OnKillFocus(e, x_textctrl); }), x_textctrl->GetId()); |     x_textctrl->Bind(wxEVT_TEXT_ENTER, ([this](wxCommandEvent e) { propagate_value(x_textctrl); }), x_textctrl->GetId()); | ||||||
|     y_textctrl->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { OnKillFocus(e, x_textctrl); }), y_textctrl->GetId()); | 	y_textctrl->Bind(wxEVT_TEXT_ENTER, ([this](wxCommandEvent e) { propagate_value(y_textctrl); }), y_textctrl->GetId()); | ||||||
|  | 
 | ||||||
|  |     x_textctrl->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { e.Skip(); propagate_value(x_textctrl); }), x_textctrl->GetId()); | ||||||
|  |     y_textctrl->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { e.Skip(); propagate_value(y_textctrl); }), y_textctrl->GetId()); | ||||||
| 
 | 
 | ||||||
| 	// 	// recast as a wxWindow to fit the calling convention
 | 	// 	// recast as a wxWindow to fit the calling convention
 | ||||||
| 	sizer = dynamic_cast<wxSizer*>(temp); | 	sizer = dynamic_cast<wxSizer*>(temp); | ||||||
|  | @ -775,14 +807,12 @@ void PointCtrl::BUILD() | ||||||
| 	y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y)); | 	y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void PointCtrl::OnKillFocus(wxEvent& e, wxTextCtrl* win) | void PointCtrl::propagate_value(wxTextCtrl* win) | ||||||
| { | { | ||||||
|     e.Skip(); |     if (!win->GetValue().empty())  | ||||||
|     if (!win->GetValue().empty()) { |  | ||||||
|         on_change_field(); |         on_change_field(); | ||||||
|     } |  | ||||||
|     else |     else | ||||||
|         on_kill_focus(e); |         on_kill_focus(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void PointCtrl::set_value(const Vec2d& value, bool change_event) | void PointCtrl::set_value(const Vec2d& value, bool change_event) | ||||||
|  |  | ||||||
|  | @ -74,7 +74,7 @@ protected: | ||||||
|     /// Call the attached on_kill_focus method. 
 |     /// Call the attached on_kill_focus method. 
 | ||||||
| 	//! It's important to use wxEvent instead of wxFocusEvent,
 | 	//! It's important to use wxEvent instead of wxFocusEvent,
 | ||||||
| 	//! in another case we can't unfocused control at all
 | 	//! in another case we can't unfocused control at all
 | ||||||
| 	void			on_kill_focus(wxEvent& event); | 	void			on_kill_focus(); | ||||||
|     /// Call the attached on_change method. 
 |     /// Call the attached on_change method. 
 | ||||||
|     void			on_set_focus(wxEvent& event); |     void			on_set_focus(wxEvent& event); | ||||||
|     /// Call the attached on_change method. 
 |     /// Call the attached on_change method. 
 | ||||||
|  | @ -226,6 +226,8 @@ protected: | ||||||
| 
 | 
 | ||||||
| 	// current value
 | 	// current value
 | ||||||
| 	boost::any			m_value; | 	boost::any			m_value; | ||||||
|  | 
 | ||||||
|  |     bool    bEnterPressed = false; | ||||||
|      |      | ||||||
| 	friend class OptionsGroup; | 	friend class OptionsGroup; | ||||||
| }; | }; | ||||||
|  | @ -252,6 +254,8 @@ public: | ||||||
| 	~TextCtrl() {} | 	~TextCtrl() {} | ||||||
| 
 | 
 | ||||||
|     void BUILD(); |     void BUILD(); | ||||||
|  |     // Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
 | ||||||
|  |     void propagate_value(); | ||||||
|     wxWindow* window {nullptr}; |     wxWindow* window {nullptr}; | ||||||
| 
 | 
 | ||||||
|     virtual void	set_value(const std::string& value, bool change_event = false) { |     virtual void	set_value(const std::string& value, bool change_event = false) { | ||||||
|  | @ -310,6 +314,8 @@ public: | ||||||
| 
 | 
 | ||||||
| 	wxWindow*		window{ nullptr }; | 	wxWindow*		window{ nullptr }; | ||||||
| 	void			BUILD() override; | 	void			BUILD() override; | ||||||
|  |     /// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
 | ||||||
|  |     void	        propagate_value() ; | ||||||
| 
 | 
 | ||||||
| 	void			set_value(const std::string& value, bool change_event = false) { | 	void			set_value(const std::string& value, bool change_event = false) { | ||||||
| 		m_disable_change_event = !change_event; | 		m_disable_change_event = !change_event; | ||||||
|  | @ -393,8 +399,8 @@ public: | ||||||
| 	wxTextCtrl*		y_textctrl{ nullptr }; | 	wxTextCtrl*		y_textctrl{ nullptr }; | ||||||
| 
 | 
 | ||||||
| 	void			BUILD()  override; | 	void			BUILD()  override; | ||||||
| 
 |     // Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
 | ||||||
|     void            OnKillFocus(wxEvent& e, wxTextCtrl* win); |     void            propagate_value(wxTextCtrl* win); | ||||||
| 	void			set_value(const Vec2d& value, bool change_event = false); | 	void			set_value(const Vec2d& value, bool change_event = false); | ||||||
| 	void			set_value(const boost::any& value, bool change_event = false); | 	void			set_value(const boost::any& value, bool change_event = false); | ||||||
| 	boost::any&		get_value() override; | 	boost::any&		get_value() override; | ||||||
|  |  | ||||||
|  | @ -2513,7 +2513,9 @@ void GLCanvas3D::Selection::_render_sidebar_rotation_hints(const std::string& si | ||||||
| 
 | 
 | ||||||
| void GLCanvas3D::Selection::_render_sidebar_scale_hints(const std::string& sidebar_field) const | void GLCanvas3D::Selection::_render_sidebar_scale_hints(const std::string& sidebar_field) const | ||||||
| { | { | ||||||
|     if (boost::ends_with(sidebar_field, "x") || requires_uniform_scale()) |     bool uniform_scale = requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling(); | ||||||
|  | 
 | ||||||
|  |     if (boost::ends_with(sidebar_field, "x") || uniform_scale) | ||||||
|     { |     { | ||||||
|         ::glPushMatrix(); |         ::glPushMatrix(); | ||||||
|         ::glRotated(-90.0, 0.0, 0.0, 1.0); |         ::glRotated(-90.0, 0.0, 0.0, 1.0); | ||||||
|  | @ -2521,14 +2523,14 @@ void GLCanvas3D::Selection::_render_sidebar_scale_hints(const std::string& sideb | ||||||
|         ::glPopMatrix(); |         ::glPopMatrix(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (boost::ends_with(sidebar_field, "y") || requires_uniform_scale()) |     if (boost::ends_with(sidebar_field, "y") || uniform_scale) | ||||||
|     { |     { | ||||||
|         ::glPushMatrix(); |         ::glPushMatrix(); | ||||||
|         _render_sidebar_scale_hint(Y); |         _render_sidebar_scale_hint(Y); | ||||||
|         ::glPopMatrix(); |         ::glPopMatrix(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (boost::ends_with(sidebar_field, "z") || requires_uniform_scale()) |     if (boost::ends_with(sidebar_field, "z") || uniform_scale) | ||||||
|     { |     { | ||||||
|         ::glPushMatrix(); |         ::glPushMatrix(); | ||||||
|         ::glRotated(90.0, 1.0, 0.0, 0.0); |         ::glRotated(90.0, 1.0, 0.0, 0.0); | ||||||
|  | @ -2559,7 +2561,7 @@ void GLCanvas3D::Selection::_render_sidebar_rotation_hint(Axis axis) const | ||||||
| 
 | 
 | ||||||
| void GLCanvas3D::Selection::_render_sidebar_scale_hint(Axis axis) const | void GLCanvas3D::Selection::_render_sidebar_scale_hint(Axis axis) const | ||||||
| { | { | ||||||
|     m_arrow.set_color((requires_uniform_scale() ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis]), 3); |     m_arrow.set_color(((requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling()) ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis]), 3); | ||||||
| 
 | 
 | ||||||
|     ::glTranslated(0.0, 5.0, 0.0); |     ::glTranslated(0.0, 5.0, 0.0); | ||||||
|     m_arrow.render(); |     m_arrow.render(); | ||||||
|  | @ -3979,6 +3981,12 @@ BoundingBoxf3 GLCanvas3D::scene_bounding_box() const | ||||||
| { | { | ||||||
|     BoundingBoxf3 bb = volumes_bounding_box(); |     BoundingBoxf3 bb = volumes_bounding_box(); | ||||||
|     bb.merge(m_bed.get_bounding_box()); |     bb.merge(m_bed.get_bounding_box()); | ||||||
|  |     if (m_config != nullptr) | ||||||
|  |     { | ||||||
|  |         double h = m_config->opt_float("max_print_height"); | ||||||
|  |         bb.min(2) = std::min(bb.min(2), -h); | ||||||
|  |         bb.max(2) = std::max(bb.max(2), h); | ||||||
|  |     } | ||||||
|     return bb; |     return bb; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -4939,8 +4947,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) | ||||||
| 
 | 
 | ||||||
|     if (evt.Entering()) |     if (evt.Entering()) | ||||||
|     { |     { | ||||||
| #if defined(__WXMSW__) || defined(__linux__) | //#if defined(__WXMSW__) || defined(__linux__)
 | ||||||
|         // On Windows and Linux needs focus in order to catch key events
 | //        // On Windows and Linux needs focus in order to catch key events
 | ||||||
|  |         // Set focus in order to remove it from sidebar fields
 | ||||||
|         if (m_canvas != nullptr) { |         if (m_canvas != nullptr) { | ||||||
|             // Only set focus, if the top level window of this canvas is active.
 |             // Only set focus, if the top level window of this canvas is active.
 | ||||||
| 			auto p = dynamic_cast<wxWindow*>(evt.GetEventObject()); | 			auto p = dynamic_cast<wxWindow*>(evt.GetEventObject()); | ||||||
|  | @ -4952,7 +4961,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         m_mouse.set_start_position_2D_as_invalid(); |         m_mouse.set_start_position_2D_as_invalid(); | ||||||
| #endif | //#endif
 | ||||||
|     } |     } | ||||||
|     else if (evt.Leaving()) |     else if (evt.Leaving()) | ||||||
|     { |     { | ||||||
|  |  | ||||||
|  | @ -1523,7 +1523,7 @@ void GLGizmoFlatten::update_planes() | ||||||
|     const Transform3d& inst_matrix = m_model_object->instances.front()->get_matrix(); |     const Transform3d& inst_matrix = m_model_object->instances.front()->get_matrix(); | ||||||
| 
 | 
 | ||||||
|     // Following constants are used for discarding too small polygons.
 |     // Following constants are used for discarding too small polygons.
 | ||||||
|     const float minimal_area = 20.f; // in square mm (world coordinates)
 |     const float minimal_area = 5.f; // in square mm (world coordinates)
 | ||||||
|     const float minimal_side = 1.f; // mm
 |     const float minimal_side = 1.f; // mm
 | ||||||
| 
 | 
 | ||||||
|     // Now we'll go through all the facets and append Points of facets sharing the same normal.
 |     // Now we'll go through all the facets and append Points of facets sharing the same normal.
 | ||||||
|  |  | ||||||
|  | @ -148,19 +148,21 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt | ||||||
| 			config.set_key_value(opt_key, new ConfigOptionString(boost::any_cast<std::string>(value))); | 			config.set_key_value(opt_key, new ConfigOptionString(boost::any_cast<std::string>(value))); | ||||||
| 			break; | 			break; | ||||||
| 		case coStrings:{ | 		case coStrings:{ | ||||||
| 			if (opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "post_process") { | 			if (opt_key == "compatible_prints" || opt_key == "compatible_printers") { | ||||||
| 				config.option<ConfigOptionStrings>(opt_key)->values =  | 				config.option<ConfigOptionStrings>(opt_key)->values =  | ||||||
| 					boost::any_cast<std::vector<std::string>>(value); | 					boost::any_cast<std::vector<std::string>>(value); | ||||||
| 			} | 			} | ||||||
| 			else if (config.def()->get(opt_key)->gui_flags.compare("serialized") == 0) { | 			else if (config.def()->get(opt_key)->gui_flags.compare("serialized") == 0) { | ||||||
| 				std::string str = boost::any_cast<std::string>(value); | 				std::string str = boost::any_cast<std::string>(value); | ||||||
| 				if (str.back() == ';') str.pop_back(); |                 std::vector<std::string> values {}; | ||||||
| 				// Split a string to multiple strings by a semi - colon.This is the old way of storing multi - string values.
 |                 if (!str.empty()) { | ||||||
| 				// Currently used for the post_process config value only.
 | 				    if (str.back() == ';') str.pop_back(); | ||||||
| 				std::vector<std::string> values; | 				    // Split a string to multiple strings by a semi - colon.This is the old way of storing multi - string values.
 | ||||||
| 				boost::split(values, str, boost::is_any_of(";")); | 				    // Currently used for the post_process config value only.
 | ||||||
| 				if (values.size() == 1 && values[0] == "")  | 				    boost::split(values, str, boost::is_any_of(";")); | ||||||
| 					values.resize(0);//break;
 | 				    if (values.size() == 1 && values[0] == "") | ||||||
|  | 					    values.resize(0); | ||||||
|  |                 } | ||||||
| 				config.option<ConfigOptionStrings>(opt_key)->values = values; | 				config.option<ConfigOptionStrings>(opt_key)->values = values; | ||||||
| 			} | 			} | ||||||
| 			else{ | 			else{ | ||||||
|  |  | ||||||
|  | @ -5,7 +5,6 @@ | ||||||
| 
 | 
 | ||||||
| #include <boost/lexical_cast.hpp> | #include <boost/lexical_cast.hpp> | ||||||
| #include <boost/algorithm/string.hpp> | #include <boost/algorithm/string.hpp> | ||||||
| #include <boost/filesystem.hpp> |  | ||||||
| 
 | 
 | ||||||
| #include <wx/stdpaths.h> | #include <wx/stdpaths.h> | ||||||
| #include <wx/imagpng.h> | #include <wx/imagpng.h> | ||||||
|  | @ -15,6 +14,7 @@ | ||||||
| #include <wx/filedlg.h> | #include <wx/filedlg.h> | ||||||
| #include <wx/dir.h> | #include <wx/dir.h> | ||||||
| #include <wx/wupdlock.h> | #include <wx/wupdlock.h> | ||||||
|  | #include <wx/filefn.h> | ||||||
| 
 | 
 | ||||||
| #include "libslic3r/Utils.hpp" | #include "libslic3r/Utils.hpp" | ||||||
| #include "libslic3r/Model.hpp" | #include "libslic3r/Model.hpp" | ||||||
|  | @ -83,6 +83,11 @@ GUI_App::GUI_App() | ||||||
| 
 | 
 | ||||||
| bool GUI_App::OnInit() | bool GUI_App::OnInit() | ||||||
| { | { | ||||||
|  |     // Verify resources path
 | ||||||
|  |     const wxString resources_dir = from_u8(Slic3r::resources_dir()); | ||||||
|  |     wxCHECK_MSG(wxDirExists(resources_dir), false, | ||||||
|  |         wxString::Format("Resources path does not exist or is not a directory: %s", resources_dir)); | ||||||
|  | 
 | ||||||
| #if ENABLE_IMGUI | #if ENABLE_IMGUI | ||||||
|     wxCHECK_MSG(m_imgui->init(), false, "Failed to initialize ImGui"); |     wxCHECK_MSG(m_imgui->init(), false, "Failed to initialize ImGui"); | ||||||
| #endif // ENABLE_IMGUI
 | #endif // ENABLE_IMGUI
 | ||||||
|  |  | ||||||
|  | @ -635,7 +635,7 @@ void ObjectList::get_settings_choice(const wxString& category_name) | ||||||
|     // Add settings item for object
 |     // Add settings item for object
 | ||||||
|     const auto item = GetSelection(); |     const auto item = GetSelection(); | ||||||
|     if (item) { |     if (item) { | ||||||
|         const auto settings_item = m_objects_model->GetSettingsItem(item); |         const auto settings_item = m_objects_model->IsSettingsItem(item) ? item : m_objects_model->GetSettingsItem(item); | ||||||
|         select_item(settings_item ? settings_item : |         select_item(settings_item ? settings_item : | ||||||
|             m_objects_model->AddSettingsChild(item)); |             m_objects_model->AddSettingsChild(item)); | ||||||
|     } |     } | ||||||
|  | @ -849,7 +849,7 @@ void ObjectList::load_part( ModelObject* model_object, | ||||||
|                 new_volume->set_type(static_cast<ModelVolume::Type>(type)); |                 new_volume->set_type(static_cast<ModelVolume::Type>(type)); | ||||||
|                 new_volume->name = boost::filesystem::path(input_file).filename().string(); |                 new_volume->name = boost::filesystem::path(input_file).filename().string(); | ||||||
| 
 | 
 | ||||||
|                 part_names.Add(new_volume->name); |                 part_names.Add(from_u8(new_volume->name)); | ||||||
| 
 | 
 | ||||||
|                 // set a default extruder value, since user can't add it manually
 |                 // set a default extruder value, since user can't add it manually
 | ||||||
|                 new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); |                 new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); | ||||||
|  | @ -903,7 +903,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const int | ||||||
|     m_parts_changed = true; |     m_parts_changed = true; | ||||||
|     parts_changed(obj_idx); |     parts_changed(obj_idx); | ||||||
| 
 | 
 | ||||||
|     select_item(m_objects_model->AddVolumeChild(GetSelection(), name, type)); |     select_item(m_objects_model->AddVolumeChild(GetSelection(), from_u8(name), type)); | ||||||
| #ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
 | #ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
 | ||||||
|     selection_changed(); |     selection_changed(); | ||||||
| #endif //no __WXOSX__ //__WXMSW__
 | #endif //no __WXOSX__ //__WXMSW__
 | ||||||
|  | @ -1031,7 +1031,7 @@ void ObjectList::split() | ||||||
|         parent = item; |         parent = item; | ||||||
| 
 | 
 | ||||||
|     for (auto id = 0; id < model_object->volumes.size(); id++) { |     for (auto id = 0; id < model_object->volumes.size(); id++) { | ||||||
|         const auto vol_item = m_objects_model->AddVolumeChild(parent, model_object->volumes[id]->name, |         const auto vol_item = m_objects_model->AddVolumeChild(parent, from_u8(model_object->volumes[id]->name), | ||||||
|                                             model_object->volumes[id]->is_modifier() ?  |                                             model_object->volumes[id]->is_modifier() ?  | ||||||
|                                                 ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART, |                                                 ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART, | ||||||
|                                             model_object->volumes[id]->config.has("extruder") ? |                                             model_object->volumes[id]->config.has("extruder") ? | ||||||
|  | @ -1188,7 +1188,7 @@ void ObjectList::part_selection_changed() | ||||||
| void ObjectList::add_object_to_list(size_t obj_idx) | void ObjectList::add_object_to_list(size_t obj_idx) | ||||||
| { | { | ||||||
|     auto model_object = (*m_objects)[obj_idx]; |     auto model_object = (*m_objects)[obj_idx]; | ||||||
|     wxString item_name = model_object->name; |     wxString item_name = from_u8(model_object->name); | ||||||
|     const auto item = m_objects_model->Add(item_name, |     const auto item = m_objects_model->Add(item_name, | ||||||
|                       !model_object->config.has("extruder") ? 0 : |                       !model_object->config.has("extruder") ? 0 : | ||||||
|                       model_object->config.option<ConfigOptionInt>("extruder")->value); |                       model_object->config.option<ConfigOptionInt>("extruder")->value); | ||||||
|  | @ -1207,8 +1207,8 @@ void ObjectList::add_object_to_list(size_t obj_idx) | ||||||
|     if (model_object->volumes.size() > 1) { |     if (model_object->volumes.size() > 1) { | ||||||
|         for (auto id = 0; id < model_object->volumes.size(); id++) { |         for (auto id = 0; id < model_object->volumes.size(); id++) { | ||||||
|             auto vol_item = m_objects_model->AddVolumeChild(item, |             auto vol_item = m_objects_model->AddVolumeChild(item, | ||||||
|                 model_object->volumes[id]->name, |                 from_u8(model_object->volumes[id]->name), | ||||||
|                 model_object->volumes[id]->type()/*ModelVolume::MODEL_PART*/, |                 model_object->volumes[id]->type(), | ||||||
|                 !model_object->volumes[id]->config.has("extruder") ? 0 : |                 !model_object->volumes[id]->config.has("extruder") ? 0 : | ||||||
|                 model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value, |                 model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value, | ||||||
|                 false); |                 false); | ||||||
|  |  | ||||||
|  | @ -17,15 +17,9 @@ namespace GUI | ||||||
| 
 | 
 | ||||||
| ObjectManipulation::ObjectManipulation(wxWindow* parent) : | ObjectManipulation::ObjectManipulation(wxWindow* parent) : | ||||||
|     OG_Settings(parent, true) |     OG_Settings(parent, true) | ||||||
| #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION |  | ||||||
|     , m_cache_position(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX)) |  | ||||||
|     , m_cache_rotation(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX)) |  | ||||||
|     , m_cache_scale(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX)) |  | ||||||
|     , m_cache_size(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX)) |  | ||||||
| #endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
 |  | ||||||
| { | { | ||||||
|     m_og->set_name(_(L("Object Manipulation"))); |     m_og->set_name(_(L("Object Manipulation"))); | ||||||
|     m_og->label_width = 100; |     m_og->label_width = 125; | ||||||
|     m_og->set_grid_vgap(5); |     m_og->set_grid_vgap(5); | ||||||
|      |      | ||||||
|     m_og->m_on_change = [this](const std::string& opt_key, const boost::any& value) { |     m_og->m_on_change = [this](const std::string& opt_key, const boost::any& value) { | ||||||
|  | @ -80,25 +74,41 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : | ||||||
|             int axis = opt_key.back() == 'x' ? 0 : |             int axis = opt_key.back() == 'x' ? 0 : | ||||||
|                        opt_key.back() == 'y' ? 1 : 2; |                        opt_key.back() == 'y' ? 1 : 2; | ||||||
| 
 | 
 | ||||||
|  | #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | ||||||
|  |             value = m_cache.position(axis); | ||||||
|  | #else | ||||||
|             value = m_cache_position(axis); |             value = m_cache_position(axis); | ||||||
|  | #endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
 | ||||||
|         } |         } | ||||||
|         else if (param == "rotation") { |         else if (param == "rotation") { | ||||||
|             int axis = opt_key.back() == 'x' ? 0 : |             int axis = opt_key.back() == 'x' ? 0 : | ||||||
|                 opt_key.back() == 'y' ? 1 : 2; |                 opt_key.back() == 'y' ? 1 : 2; | ||||||
| 
 | 
 | ||||||
|  | #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | ||||||
|  |             value = m_cache.rotation(axis); | ||||||
|  | #else | ||||||
|             value = m_cache_rotation(axis); |             value = m_cache_rotation(axis); | ||||||
|  | #endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
 | ||||||
|         } |         } | ||||||
|         else if (param == "scale") { |         else if (param == "scale") { | ||||||
|             int axis = opt_key.back() == 'x' ? 0 : |             int axis = opt_key.back() == 'x' ? 0 : | ||||||
|                 opt_key.back() == 'y' ? 1 : 2; |                 opt_key.back() == 'y' ? 1 : 2; | ||||||
| 
 | 
 | ||||||
|  | #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | ||||||
|  |             value = m_cache.scale(axis); | ||||||
|  | #else | ||||||
|             value = m_cache_scale(axis); |             value = m_cache_scale(axis); | ||||||
|  | #endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
 | ||||||
|         } |         } | ||||||
|         else if (param == "size") { |         else if (param == "size") { | ||||||
|             int axis = opt_key.back() == 'x' ? 0 : |             int axis = opt_key.back() == 'x' ? 0 : | ||||||
|                 opt_key.back() == 'y' ? 1 : 2; |                 opt_key.back() == 'y' ? 1 : 2; | ||||||
| 
 | 
 | ||||||
|  | #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | ||||||
|  |             value = m_cache.size(axis); | ||||||
|  | #else | ||||||
|             value = m_cache_size(axis); |             value = m_cache_size(axis); | ||||||
|  | #endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
 | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         m_og->set_value(opt_key, double_to_string(value)); |         m_og->set_value(opt_key, double_to_string(value)); | ||||||
|  | @ -140,7 +150,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : | ||||||
|     m_og->append_line(line); |     m_og->append_line(line); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     auto add_og_to_object_settings = [](const std::string& option_name, const std::string& sidetext) |     auto add_og_to_object_settings = [this](const std::string& option_name, const std::string& sidetext) | ||||||
|     { |     { | ||||||
|         Line line = { _(option_name), "" }; |         Line line = { _(option_name), "" }; | ||||||
|         ConfigOptionDef def; |         ConfigOptionDef def; | ||||||
|  | @ -154,6 +164,26 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : | ||||||
|             def.max = 360; |             def.max = 360; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         // Add "uniform scaling" button in front of "Scale" option 
 | ||||||
|  |         else if (option_name == "Scale") { | ||||||
|  |             line.near_label_widget = [this](wxWindow* parent) { | ||||||
|  |                 auto btn = new PrusaLockButton(parent, wxID_ANY); | ||||||
|  |                 btn->Bind(wxEVT_BUTTON, [btn, this](wxCommandEvent &event){ | ||||||
|  |                     event.Skip(); | ||||||
|  |                     wxTheApp->CallAfter([btn, this]() { set_uniform_scaling(btn->IsLocked()); }); | ||||||
|  |                 }); | ||||||
|  |                 return btn; | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Add empty bmp (Its size have to be equal to PrusaLockButton) in front of "Size" option to label alignment
 | ||||||
|  |         else if (option_name == "Size") { | ||||||
|  |             line.near_label_widget = [this](wxWindow* parent) { | ||||||
|  |                 return new wxStaticBitmap(parent, wxID_ANY, wxNullBitmap, wxDefaultPosition,  | ||||||
|  |                                           wxBitmap(from_u8(var("one_layer_lock_on.png")), wxBITMAP_TYPE_PNG).GetSize()); | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         const std::string lower_name = boost::algorithm::to_lower_copy(option_name); |         const std::string lower_name = boost::algorithm::to_lower_copy(option_name); | ||||||
| 
 | 
 | ||||||
|         std::vector<std::string> axes{ "x", "y", "z" }; |         std::vector<std::string> axes{ "x", "y", "z" }; | ||||||
|  | @ -229,16 +259,38 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele | ||||||
|         m_new_rotation = volume->get_instance_rotation(); |         m_new_rotation = volume->get_instance_rotation(); | ||||||
|         m_new_scale    = volume->get_instance_scaling_factor(); |         m_new_scale    = volume->get_instance_scaling_factor(); | ||||||
|         int obj_idx = volume->object_idx(); |         int obj_idx = volume->object_idx(); | ||||||
|  | #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | ||||||
|  |         int instance_idx = volume->instance_idx(); | ||||||
|  |         if ((0 <= obj_idx) && (obj_idx < (int)wxGetApp().model_objects()->size())) | ||||||
|  |         { | ||||||
|  |             bool changed_box = false; | ||||||
|  |             if (!m_cache.instance.matches_object(obj_idx)) | ||||||
|  |             { | ||||||
|  |                 m_cache.instance.set(obj_idx, instance_idx, (*wxGetApp().model_objects())[obj_idx]->raw_mesh().bounding_box().size()); | ||||||
|  |                 changed_box = true; | ||||||
|  |             } | ||||||
|  |             if (changed_box || !m_cache.instance.matches_instance(instance_idx) || !m_cache.scale.isApprox(100.0 * m_new_scale)) | ||||||
|  |                 m_new_size = volume->get_instance_transformation().get_matrix(true, true) * m_cache.instance.box_size; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |             // this should never happen
 | ||||||
|  |             m_new_size = Vec3d::Zero(); | ||||||
|  | #else | ||||||
|         if ((0 <= obj_idx) && (obj_idx < (int)wxGetApp().model_objects()->size())) |         if ((0 <= obj_idx) && (obj_idx < (int)wxGetApp().model_objects()->size())) | ||||||
|             m_new_size = volume->get_instance_transformation().get_matrix(true, true) * (*wxGetApp().model_objects())[obj_idx]->raw_mesh().bounding_box().size(); |             m_new_size = volume->get_instance_transformation().get_matrix(true, true) * (*wxGetApp().model_objects())[obj_idx]->raw_mesh().bounding_box().size(); | ||||||
|         else |         else | ||||||
|             // this should never happen
 |             // this should never happen
 | ||||||
|             m_new_size = Vec3d::Zero(); |             m_new_size = Vec3d::Zero(); | ||||||
|  | #endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
 | ||||||
| 
 | 
 | ||||||
|         m_new_enabled  = true; |         m_new_enabled  = true; | ||||||
|     } |     } | ||||||
|     else if (selection.is_single_full_object()) |     else if (selection.is_single_full_object()) | ||||||
|     { |     { | ||||||
|  | #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | ||||||
|  |         m_cache.instance.reset(); | ||||||
|  | #endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
 | ||||||
|  | 
 | ||||||
|         const BoundingBoxf3& box = selection.get_bounding_box(); |         const BoundingBoxf3& box = selection.get_bounding_box(); | ||||||
|         m_new_position = box.center(); |         m_new_position = box.center(); | ||||||
|         m_new_rotation = Vec3d::Zero(); |         m_new_rotation = Vec3d::Zero(); | ||||||
|  | @ -250,6 +302,10 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele | ||||||
|     } |     } | ||||||
|     else if (selection.is_single_modifier() || selection.is_single_volume()) |     else if (selection.is_single_modifier() || selection.is_single_volume()) | ||||||
|     { |     { | ||||||
|  | #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | ||||||
|  |         m_cache.instance.reset(); | ||||||
|  | #endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
 | ||||||
|  | 
 | ||||||
|         // the selection contains a single volume
 |         // the selection contains a single volume
 | ||||||
|         const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); |         const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); | ||||||
|         m_new_position = volume->get_volume_offset(); |         m_new_position = volume->get_volume_offset(); | ||||||
|  | @ -280,59 +336,71 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele | ||||||
| void ObjectManipulation::update_if_dirty() | void ObjectManipulation::update_if_dirty() | ||||||
| { | { | ||||||
| #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | ||||||
|     if (_(m_new_move_label_string) != m_move_Label->GetLabel()) |     if (m_cache.move_label_string != _(m_new_move_label_string)) | ||||||
|         m_move_Label->SetLabel(_(m_new_move_label_string)); |     { | ||||||
|  |         m_cache.move_label_string = _(m_new_move_label_string); | ||||||
|  |         m_move_Label->SetLabel(m_cache.move_label_string); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     if (_(m_new_rotate_label_string) != m_rotate_Label->GetLabel()) |     if (m_cache.rotate_label_string != _(m_new_rotate_label_string)) | ||||||
|         m_rotate_Label->SetLabel(_(m_new_rotate_label_string)); |     { | ||||||
|  |         m_cache.rotate_label_string = _(m_new_rotate_label_string); | ||||||
|  |         m_rotate_Label->SetLabel(m_cache.rotate_label_string); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     if (_(m_new_scale_label_string) != m_scale_Label->GetLabel()) |     if (m_cache.scale_label_string != _(m_new_scale_label_string)) | ||||||
|         m_scale_Label->SetLabel(_(m_new_scale_label_string)); |     { | ||||||
|  |         m_cache.scale_label_string = _(m_new_scale_label_string); | ||||||
|  |         m_scale_Label->SetLabel(m_cache.scale_label_string); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     if (m_cache_position(0) != m_new_position(0)) |     if (m_cache.position(0) != m_new_position(0)) | ||||||
|         m_og->set_value("position_x", double_to_string(m_new_position(0), 2)); |         m_og->set_value("position_x", double_to_string(m_new_position(0), 2)); | ||||||
| 
 | 
 | ||||||
|     if (m_cache_position(1) != m_new_position(1)) |     if (m_cache.position(1) != m_new_position(1)) | ||||||
|         m_og->set_value("position_y", double_to_string(m_new_position(1), 2)); |         m_og->set_value("position_y", double_to_string(m_new_position(1), 2)); | ||||||
| 
 | 
 | ||||||
|     if (m_cache_position(2) != m_new_position(2)) |     if (m_cache.position(2) != m_new_position(2)) | ||||||
|         m_og->set_value("position_z", double_to_string(m_new_position(2), 2)); |         m_og->set_value("position_z", double_to_string(m_new_position(2), 2)); | ||||||
| 
 | 
 | ||||||
|     m_cache_position = m_new_position; |     m_cache.position = m_new_position; | ||||||
| 
 | 
 | ||||||
|     auto scale = m_new_scale * 100.0; |     auto scale = m_new_scale * 100.0; | ||||||
|     if (m_cache_scale(0) != scale(0)) |     if (m_cache.scale(0) != scale(0)) | ||||||
|         m_og->set_value("scale_x", double_to_string(scale(0), 2)); |         m_og->set_value("scale_x", double_to_string(scale(0), 2)); | ||||||
| 
 | 
 | ||||||
|     if (m_cache_scale(1) != scale(1)) |     if (m_cache.scale(1) != scale(1)) | ||||||
|         m_og->set_value("scale_y", double_to_string(scale(1), 2)); |         m_og->set_value("scale_y", double_to_string(scale(1), 2)); | ||||||
| 
 | 
 | ||||||
|     if (m_cache_scale(2) != scale(2)) |     if (m_cache.scale(2) != scale(2)) | ||||||
|         m_og->set_value("scale_z", double_to_string(scale(2), 2)); |         m_og->set_value("scale_z", double_to_string(scale(2), 2)); | ||||||
| 
 | 
 | ||||||
|     m_cache_scale = scale; |     m_cache.scale = scale; | ||||||
| 
 | 
 | ||||||
|     if (m_cache_size(0) != m_new_size(0)) |     if (m_cache.size(0) != m_new_size(0)) | ||||||
|         m_og->set_value("size_x", double_to_string(m_new_size(0), 2)); |         m_og->set_value("size_x", double_to_string(m_new_size(0), 2)); | ||||||
| 
 | 
 | ||||||
|     if (m_cache_size(1) != m_new_size(1)) |     if (m_cache.size(1) != m_new_size(1)) | ||||||
|         m_og->set_value("size_y", double_to_string(m_new_size(1), 2)); |         m_og->set_value("size_y", double_to_string(m_new_size(1), 2)); | ||||||
| 
 | 
 | ||||||
|     if (m_cache_size(2) != m_new_size(2)) |     if (m_cache.size(2) != m_new_size(2)) | ||||||
|         m_og->set_value("size_z", double_to_string(m_new_size(2), 2)); |         m_og->set_value("size_z", double_to_string(m_new_size(2), 2)); | ||||||
| 
 | 
 | ||||||
|     m_cache_size = m_new_size; |     m_cache.size = m_new_size; | ||||||
| 
 | 
 | ||||||
|     if (m_cache_rotation(0) != m_new_rotation(0)) |     if (m_cache.rotation(0) != m_new_rotation(0)) | ||||||
|         m_og->set_value("rotation_x", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(0)), 0), 2)); |         m_og->set_value("rotation_x", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(0)), 0), 2)); | ||||||
| 
 | 
 | ||||||
|     if (m_cache_rotation(1) != m_new_rotation(1)) |     if (m_cache.rotation(1) != m_new_rotation(1)) | ||||||
|         m_og->set_value("rotation_y", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(1)), 0), 2)); |         m_og->set_value("rotation_y", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(1)), 0), 2)); | ||||||
| 
 | 
 | ||||||
|     if (m_cache_rotation(2) != m_new_rotation(2)) |     if (m_cache.rotation(2) != m_new_rotation(2)) | ||||||
|         m_og->set_value("rotation_z", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(2)), 0), 2)); |         m_og->set_value("rotation_z", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(2)), 0), 2)); | ||||||
| 
 | 
 | ||||||
|     m_cache_rotation = m_new_rotation; |     m_cache.rotation = m_new_rotation; | ||||||
|  | 
 | ||||||
|  | //    if (wxGetApp().plater()->canvas3D()->get_selection().requires_uniform_scale())
 | ||||||
|  | //        m_uniform_scale = true;
 | ||||||
| 
 | 
 | ||||||
|     if (m_new_enabled) |     if (m_new_enabled) | ||||||
|         m_og->enable(); |         m_og->enable(); | ||||||
|  | @ -378,11 +446,14 @@ void ObjectManipulation::update_if_dirty() | ||||||
| 
 | 
 | ||||||
| void ObjectManipulation::reset_settings_value() | void ObjectManipulation::reset_settings_value() | ||||||
| { | { | ||||||
|     m_new_position  = Vec3d::Zero(); |     m_new_position = Vec3d::Zero(); | ||||||
|     m_new_rotation  = Vec3d::Zero(); |     m_new_rotation = Vec3d::Zero(); | ||||||
|     m_new_scale = Vec3d::Ones(); |     m_new_scale = Vec3d::Ones(); | ||||||
|     m_new_size = Vec3d::Zero(); |     m_new_size = Vec3d::Zero(); | ||||||
|     m_new_enabled   = false; |     m_new_enabled = false; | ||||||
|  | #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | ||||||
|  |     m_cache.instance.reset(); | ||||||
|  | #endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
 | ||||||
| #if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | #if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | ||||||
|     m_dirty = true; |     m_dirty = true; | ||||||
| #endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
 | #endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
 | ||||||
|  | @ -393,10 +464,18 @@ void ObjectManipulation::change_position_value(const Vec3d& position) | ||||||
|     auto canvas = wxGetApp().plater()->canvas3D(); |     auto canvas = wxGetApp().plater()->canvas3D(); | ||||||
|     GLCanvas3D::Selection& selection = canvas->get_selection(); |     GLCanvas3D::Selection& selection = canvas->get_selection(); | ||||||
|     selection.start_dragging(); |     selection.start_dragging(); | ||||||
|  | #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | ||||||
|  |     selection.translate(position - m_cache.position, selection.requires_local_axes()); | ||||||
|  | #else | ||||||
|     selection.translate(position - m_cache_position, selection.requires_local_axes()); |     selection.translate(position - m_cache_position, selection.requires_local_axes()); | ||||||
|  | #endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
 | ||||||
|     canvas->do_move(); |     canvas->do_move(); | ||||||
| 
 | 
 | ||||||
|  | #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | ||||||
|  |     m_cache.position = position; | ||||||
|  | #else | ||||||
|     m_cache_position = position; |     m_cache_position = position; | ||||||
|  | #endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ObjectManipulation::change_rotation_value(const Vec3d& rotation) | void ObjectManipulation::change_rotation_value(const Vec3d& rotation) | ||||||
|  | @ -415,7 +494,7 @@ void ObjectManipulation::change_rotation_value(const Vec3d& rotation) | ||||||
|     canvas->do_rotate(); |     canvas->do_rotate(); | ||||||
| 
 | 
 | ||||||
| #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | ||||||
|     m_cache_rotation = rotation; |     m_cache.rotation = rotation; | ||||||
| #endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
 | #endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -423,9 +502,13 @@ void ObjectManipulation::change_scale_value(const Vec3d& scale) | ||||||
| { | { | ||||||
|     Vec3d scaling_factor = scale; |     Vec3d scaling_factor = scale; | ||||||
|     const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); |     const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); | ||||||
|     if (selection.requires_uniform_scale()) |     if (m_uniform_scale || selection.requires_uniform_scale()) | ||||||
|     { |     { | ||||||
|  | #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | ||||||
|  |         Vec3d abs_scale_diff = (scale - m_cache.scale).cwiseAbs(); | ||||||
|  | #else | ||||||
|         Vec3d abs_scale_diff = (scale - m_cache_scale).cwiseAbs(); |         Vec3d abs_scale_diff = (scale - m_cache_scale).cwiseAbs(); | ||||||
|  | #endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
 | ||||||
|         double max_diff = abs_scale_diff(X); |         double max_diff = abs_scale_diff(X); | ||||||
|         Axis max_diff_axis = X; |         Axis max_diff_axis = X; | ||||||
|         if (max_diff < abs_scale_diff(Y)) |         if (max_diff < abs_scale_diff(Y)) | ||||||
|  | @ -449,7 +532,10 @@ void ObjectManipulation::change_scale_value(const Vec3d& scale) | ||||||
|     canvas->do_scale(); |     canvas->do_scale(); | ||||||
| 
 | 
 | ||||||
| #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | ||||||
|     m_cache_scale = scale; |     if (!m_cache.scale.isApprox(scale)) | ||||||
|  |         m_cache.instance.instance_idx = -1; | ||||||
|  | 
 | ||||||
|  |     m_cache.scale = scale; | ||||||
| #endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
 | #endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -457,7 +543,11 @@ void ObjectManipulation::change_size_value(const Vec3d& size) | ||||||
| { | { | ||||||
|     const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); |     const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); | ||||||
| 
 | 
 | ||||||
|  | #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | ||||||
|  |     Vec3d ref_size = m_cache.size; | ||||||
|  | #else | ||||||
|     Vec3d ref_size = m_cache_size; |     Vec3d ref_size = m_cache_size; | ||||||
|  | #endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
 | ||||||
|     if (selection.is_single_full_instance()) |     if (selection.is_single_full_instance()) | ||||||
|     { |     { | ||||||
|         const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); |         const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); | ||||||
|  | @ -470,7 +560,7 @@ void ObjectManipulation::change_size_value(const Vec3d& size) | ||||||
| 
 | 
 | ||||||
|     if (selection.requires_uniform_scale()) |     if (selection.requires_uniform_scale()) | ||||||
|     { |     { | ||||||
|         Vec3d abs_scale_diff = (scale - m_cache_scale).cwiseAbs(); |         Vec3d abs_scale_diff = (scale - m_cache.scale).cwiseAbs(); | ||||||
|         double max_diff = abs_scale_diff(X); |         double max_diff = abs_scale_diff(X); | ||||||
|         Axis max_diff_axis = X; |         Axis max_diff_axis = X; | ||||||
|         if (max_diff < abs_scale_diff(Y)) |         if (max_diff < abs_scale_diff(Y)) | ||||||
|  | @ -493,7 +583,7 @@ void ObjectManipulation::change_size_value(const Vec3d& size) | ||||||
|     canvas->get_selection().scale(scaling_factor, false); |     canvas->get_selection().scale(scaling_factor, false); | ||||||
|     canvas->do_scale(); |     canvas->do_scale(); | ||||||
| 
 | 
 | ||||||
|     m_cache_size = size; |     m_cache.size = size; | ||||||
| #else | #else | ||||||
|     change_scale_value(100.0 * Vec3d(size(0) / ref_size(0), size(1) / ref_size(1), size(2) / ref_size(2))); |     change_scale_value(100.0 * Vec3d(size(0) / ref_size(0), size(1) / ref_size(1), size(2) / ref_size(2))); | ||||||
| #endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
 | #endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
 | ||||||
|  |  | ||||||
|  | @ -15,10 +15,41 @@ namespace GUI { | ||||||
| class ObjectManipulation : public OG_Settings | class ObjectManipulation : public OG_Settings | ||||||
| { | { | ||||||
| #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION | ||||||
|     Vec3d m_cache_position; |     struct Cache | ||||||
|     Vec3d m_cache_rotation; |     { | ||||||
|     Vec3d m_cache_scale; |         Vec3d position; | ||||||
|     Vec3d m_cache_size; |         Vec3d rotation; | ||||||
|  |         Vec3d scale; | ||||||
|  |         Vec3d size; | ||||||
|  | 
 | ||||||
|  |         std::string move_label_string; | ||||||
|  |         std::string rotate_label_string; | ||||||
|  |         std::string scale_label_string; | ||||||
|  | 
 | ||||||
|  |         struct Instance | ||||||
|  |         { | ||||||
|  |             int object_idx; | ||||||
|  |             int instance_idx; | ||||||
|  |             Vec3d box_size; | ||||||
|  | 
 | ||||||
|  |             Instance() { reset(); } | ||||||
|  |             void reset() { this->object_idx = -1; this->instance_idx = -1; this->box_size = Vec3d::Zero(); } | ||||||
|  |             void set(int object_idx, int instance_idx, const Vec3d& box_size) { this->object_idx = object_idx; this->instance_idx = instance_idx; this->box_size = box_size; } | ||||||
|  |             bool matches(int object_idx, int instance_idx) const { return (this->object_idx == object_idx) && (this->instance_idx == instance_idx); } | ||||||
|  |             bool matches_object(int object_idx) const { return (this->object_idx == object_idx); } | ||||||
|  |             bool matches_instance(int instance_idx) const { return (this->instance_idx == instance_idx); } | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         Instance instance; | ||||||
|  | 
 | ||||||
|  |         Cache() : position(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX)) , rotation(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX)) | ||||||
|  |             , scale(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX)) , size(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX)) | ||||||
|  |             , move_label_string("") , rotate_label_string("") , scale_label_string("") | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     Cache m_cache; | ||||||
| #else | #else | ||||||
|     Vec3d       m_cache_position{ 0., 0., 0. }; |     Vec3d       m_cache_position{ 0., 0., 0. }; | ||||||
|     Vec3d       m_cache_rotation{ 0., 0., 0. }; |     Vec3d       m_cache_rotation{ 0., 0., 0. }; | ||||||
|  | @ -43,6 +74,7 @@ class ObjectManipulation : public OG_Settings | ||||||
|     Vec3d           m_new_scale; |     Vec3d           m_new_scale; | ||||||
|     Vec3d           m_new_size; |     Vec3d           m_new_size; | ||||||
|     bool            m_new_enabled; |     bool            m_new_enabled; | ||||||
|  |     bool            m_uniform_scale {false}; | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     ObjectManipulation(wxWindow* parent); |     ObjectManipulation(wxWindow* parent); | ||||||
|  | @ -57,6 +89,9 @@ public: | ||||||
| 	// Called from the App to update the UI if dirty.
 | 	// Called from the App to update the UI if dirty.
 | ||||||
| 	void		update_if_dirty(); | 	void		update_if_dirty(); | ||||||
| 
 | 
 | ||||||
|  |     void        set_uniform_scaling(const bool uniform_scale) { m_uniform_scale = uniform_scale;} | ||||||
|  |     bool        get_uniform_scaling() const { return m_uniform_scale; } | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     void reset_settings_value(); |     void reset_settings_value(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1053,7 +1053,7 @@ std::vector<std::string> PresetCollection::dirty_options(const Preset *edited, c | ||||||
|     std::vector<std::string> changed; |     std::vector<std::string> changed; | ||||||
| 	if (edited != nullptr && reference != nullptr) { | 	if (edited != nullptr && reference != nullptr) { | ||||||
|         changed = deep_compare ? |         changed = deep_compare ? | ||||||
| 				deep_diff(reference->config, edited->config) : | 				deep_diff(edited->config, reference->config) : | ||||||
| 				reference->config.diff(edited->config); | 				reference->config.diff(edited->config); | ||||||
|         // The "compatible_printers" option key is handled differently from the others:
 |         // The "compatible_printers" option key is handled differently from the others:
 | ||||||
|         // It is not mandatory. If the key is missing, it means it is compatible with any printer.
 |         // It is not mandatory. If the key is missing, it means it is compatible with any printer.
 | ||||||
|  | @ -1161,6 +1161,21 @@ std::string PresetCollection::name() const | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | std::vector<std::string> PresetCollection::system_preset_names() const | ||||||
|  | { | ||||||
|  |     size_t num = 0; | ||||||
|  |     for (const Preset &preset : m_presets) | ||||||
|  |         if (preset.is_system) | ||||||
|  |             ++ num; | ||||||
|  |     std::vector<std::string> out; | ||||||
|  |     out.reserve(num); | ||||||
|  | 	for (const Preset &preset : m_presets) | ||||||
|  | 		if (preset.is_system) | ||||||
|  | 			out.emplace_back(preset.name); | ||||||
|  |     std::sort(out.begin(), out.end()); | ||||||
|  |     return out; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Generate a file path from a profile name. Add the ".ini" suffix if it is missing.
 | // Generate a file path from a profile name. Add the ".ini" suffix if it is missing.
 | ||||||
| std::string PresetCollection::path_from_name(const std::string &new_name) const | std::string PresetCollection::path_from_name(const std::string &new_name) const | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -365,6 +365,9 @@ public: | ||||||
|     std::vector<std::string>    current_different_from_parent_options(const bool deep_compare = false) const |     std::vector<std::string>    current_different_from_parent_options(const bool deep_compare = false) const | ||||||
|         { return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), deep_compare); } |         { return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), deep_compare); } | ||||||
| 
 | 
 | ||||||
|  |     // Return a sorted list of system preset names.
 | ||||||
|  |     std::vector<std::string>    system_preset_names() const; | ||||||
|  | 
 | ||||||
|     // Update the choice UI from the list of presets.
 |     // Update the choice UI from the list of presets.
 | ||||||
|     // If show_incompatible, all presets are shown, otherwise only the compatible presets are shown.
 |     // If show_incompatible, all presets are shown, otherwise only the compatible presets are shown.
 | ||||||
|     // If an incompatible preset is selected, it is shown as well.
 |     // If an incompatible preset is selected, it is shown as well.
 | ||||||
|  |  | ||||||
|  | @ -876,7 +876,8 @@ void PresetBundle::load_config_file_config_bundle(const std::string &path, const | ||||||
| // The presets starting with '*' are considered non-terminal and they are
 | // The presets starting with '*' are considered non-terminal and they are
 | ||||||
| // removed through the flattening process by this function.
 | // removed through the flattening process by this function.
 | ||||||
| // This function will never fail, but it will produce error messages through boost::log.
 | // This function will never fail, but it will produce error messages through boost::log.
 | ||||||
| static void flatten_configbundle_hierarchy(boost::property_tree::ptree &tree, const std::string &group_name) | // system_profiles will not be flattened, and they will be kept inside the "inherits" field
 | ||||||
|  | static void flatten_configbundle_hierarchy(boost::property_tree::ptree &tree, const std::string &group_name, const std::vector<std::string> &system_profiles) | ||||||
| { | { | ||||||
|     namespace pt = boost::property_tree; |     namespace pt = boost::property_tree; | ||||||
| 
 | 
 | ||||||
|  | @ -911,23 +912,38 @@ static void flatten_configbundle_hierarchy(boost::property_tree::ptree &tree, co | ||||||
|     for (const Prst &prst : presets) { |     for (const Prst &prst : presets) { | ||||||
|         // Parse the list of comma separated values, possibly enclosed in quotes.
 |         // Parse the list of comma separated values, possibly enclosed in quotes.
 | ||||||
|         std::vector<std::string> inherits_names; |         std::vector<std::string> inherits_names; | ||||||
|  |         std::vector<std::string> inherits_system; | ||||||
|         if (Slic3r::unescape_strings_cstyle(prst.node->get<std::string>("inherits", ""), inherits_names)) { |         if (Slic3r::unescape_strings_cstyle(prst.node->get<std::string>("inherits", ""), inherits_names)) { | ||||||
|             // Resolve the inheritance by name.
 |             // Resolve the inheritance by name.
 | ||||||
|             std::vector<Prst*> &inherits_nodes = const_cast<Prst&>(prst).inherits; |             std::vector<Prst*> &inherits_nodes = const_cast<Prst&>(prst).inherits; | ||||||
|             for (const std::string &node_name : inherits_names) { |             for (const std::string &node_name : inherits_names) { | ||||||
|                 auto it = presets.find(Prst(node_name, nullptr)); |                 auto it_system = std::lower_bound(system_profiles.begin(), system_profiles.end(), node_name); | ||||||
|                 if (it == presets.end()) |                 if (it_system != system_profiles.end() && *it_system == node_name) { | ||||||
|                     BOOST_LOG_TRIVIAL(error) << "flatten_configbundle_hierarchy: The preset " << prst.name << " inherits an unknown preset \"" << node_name << "\""; |                     // Loading a user config budnle, this preset is derived from a system profile.
 | ||||||
|                 else { |                     inherits_system.emplace_back(node_name); | ||||||
|                     inherits_nodes.emplace_back(const_cast<Prst*>(&(*it))); |                 } else { | ||||||
|                     inherits_nodes.back()->parent_of.emplace_back(const_cast<Prst*>(&prst)); |                     auto it = presets.find(Prst(node_name, nullptr)); | ||||||
|  |                     if (it == presets.end()) | ||||||
|  |                         BOOST_LOG_TRIVIAL(error) << "flatten_configbundle_hierarchy: The preset " << prst.name << " inherits an unknown preset \"" << node_name << "\""; | ||||||
|  |                     else { | ||||||
|  |                         inherits_nodes.emplace_back(const_cast<Prst*>(&(*it))); | ||||||
|  |                         inherits_nodes.back()->parent_of.emplace_back(const_cast<Prst*>(&prst)); | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             BOOST_LOG_TRIVIAL(error) << "flatten_configbundle_hierarchy: The preset " << prst.name << " has an invalid \"inherits\" field"; |             BOOST_LOG_TRIVIAL(error) << "flatten_configbundle_hierarchy: The preset " << prst.name << " has an invalid \"inherits\" field"; | ||||||
|         } |         } | ||||||
|         // Remove the "inherits" key, it has no meaning outside the config bundle.
 |         // Remove the "inherits" key, it has no meaning outside of the config bundle.
 | ||||||
|         const_cast<pt::ptree*>(prst.node)->erase("inherits"); |         const_cast<pt::ptree*>(prst.node)->erase("inherits"); | ||||||
|  |         if (! inherits_system.empty()) { | ||||||
|  |             // Loaded a user config bundle, where a profile inherits a system profile.
 | ||||||
|  | 			// User profile should be derived from a single system profile only.
 | ||||||
|  | 			assert(inherits_system.size() == 1); | ||||||
|  | 			if (inherits_system.size() > 1) | ||||||
|  | 				BOOST_LOG_TRIVIAL(error) << "flatten_configbundle_hierarchy: The preset " << prst.name << " inherits from more than single system preset"; | ||||||
|  | 			prst.node->put("inherits", Slic3r::escape_string_cstyle(inherits_system.front())); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // 2) Create a linear ordering for the directed acyclic graph of preset inheritance.
 |     // 2) Create a linear ordering for the directed acyclic graph of preset inheritance.
 | ||||||
|  | @ -983,13 +999,14 @@ static void flatten_configbundle_hierarchy(boost::property_tree::ptree &tree, co | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void flatten_configbundle_hierarchy(boost::property_tree::ptree &tree) | // preset_bundle is set when loading user config bundles, which must not overwrite the system profiles.
 | ||||||
|  | static void flatten_configbundle_hierarchy(boost::property_tree::ptree &tree, const PresetBundle *preset_bundle) | ||||||
| { | { | ||||||
|     flatten_configbundle_hierarchy(tree, "print"); |     flatten_configbundle_hierarchy(tree, "print",           preset_bundle ? preset_bundle->prints.system_preset_names()        : std::vector<std::string>()); | ||||||
|     flatten_configbundle_hierarchy(tree, "filament"); |     flatten_configbundle_hierarchy(tree, "filament",        preset_bundle ? preset_bundle->filaments.system_preset_names()     : std::vector<std::string>()); | ||||||
|     flatten_configbundle_hierarchy(tree, "sla_print"); |     flatten_configbundle_hierarchy(tree, "sla_print",       preset_bundle ? preset_bundle->sla_prints.system_preset_names()    : std::vector<std::string>()); | ||||||
|     flatten_configbundle_hierarchy(tree, "sla_material"); |     flatten_configbundle_hierarchy(tree, "sla_material",    preset_bundle ? preset_bundle->sla_materials.system_preset_names() : std::vector<std::string>()); | ||||||
|     flatten_configbundle_hierarchy(tree, "printer"); |     flatten_configbundle_hierarchy(tree, "printer",         preset_bundle ? preset_bundle->printers.system_preset_names()      : std::vector<std::string>()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Load a config bundle file, into presets and store the loaded presets into separate files
 | // Load a config bundle file, into presets and store the loaded presets into separate files
 | ||||||
|  | @ -1019,7 +1036,8 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // 1.5) Flatten the config bundle by applying the inheritance rules. Internal profiles (with names starting with '*') are removed.
 |     // 1.5) Flatten the config bundle by applying the inheritance rules. Internal profiles (with names starting with '*') are removed.
 | ||||||
|     flatten_configbundle_hierarchy(tree); |     // If loading a user config bundle, do not flatten with the system profiles, but keep the "inherits" flag intact.
 | ||||||
|  |     flatten_configbundle_hierarchy(tree, ((flags & LOAD_CFGBNDLE_SYSTEM) == 0) ? this : nullptr); | ||||||
| 
 | 
 | ||||||
|     // 2) Parse the property_tree, extract the active preset names and the profiles, save them into local config files.
 |     // 2) Parse the property_tree, extract the active preset names and the profiles, save them into local config files.
 | ||||||
|     // Parse the obsolete preset names, to be deleted when upgrading from the old configuration structure.
 |     // Parse the obsolete preset names, to be deleted when upgrading from the old configuration structure.
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 tamasmeszaros
						tamasmeszaros