Merge branch 'master' into tm_pad_improve

This commit is contained in:
tamasmeszaros 2019-01-08 16:32:54 +01:00
commit a25072f0a3
24 changed files with 618 additions and 202 deletions

View File

@ -2,6 +2,8 @@ project(Slic3r)
cmake_minimum_required(VERSION 3.2)
include("version.inc")
include(GNUInstallDirs)
set(SLIC3R_RESOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/resources")
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_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_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1)
option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1)
@ -61,6 +64,8 @@ foreach (DIR ${PREFIX_PATH_CHECK})
endif ()
endforeach ()
message(STATUS "SLIC3R_FHS: ${SLIC3R_FHS}")
# Add our own cmake module path.
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()
# 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)
include_directories(${LIBDIR})
# For generated header files
include_directories(${LIBDIR_BIN}/platform)
# For libslic3r.h
include_directories(${LIBDIR}/clipper ${LIBDIR}/polypartition)
#set(CMAKE_INCLUDE_CURRENT_DIR ON)
if(WIN32)
# 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.
add_definitions(-DTBB_USE_CAPTURED_EXCEPTION=0)
#set(CURL_DEBUG 1)
find_package(CURL REQUIRED)
include_directories(${CURL_INCLUDE_DIRS})
@ -280,7 +286,6 @@ include_directories(${GLEW_INCLUDE_DIRS})
# l10n
set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization")
add_custom_target(pot
# FIXME: file list stale
COMMAND xgettext --keyword=L --from-code=UTF-8 --debug
-f "${L10N_DIR}/list.txt"
-o "${L10N_DIR}/Slic3rPE.pot"
@ -307,5 +312,12 @@ if(SLIC3R_BUILD_TESTS)
add_subdirectory(tests)
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
View File

@ -6,6 +6,7 @@
# 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
# 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 the top-level command, ie. use `make -j 1` or `ninja -j 1` to force single-threaded top-level

View File

@ -121,7 +121,8 @@ ExternalProject_Add(dep_zlib
URL_HASH SHA256=4ff941449631ace0d4d203e3483be9dbc9da454084111f97ea0a2114e19bf066
CMAKE_GENERATOR "${DEP_MSVC_GEN}"
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_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local"
BUILD_COMMAND msbuild /P:Configuration=Release INSTALL.vcxproj
@ -136,6 +137,19 @@ if (${DEP_DEBUG})
WORKING_DIRECTORY "${BINARY_DIR}"
)
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
@ -147,6 +161,7 @@ ExternalProject_Add(dep_libpng
CMAKE_ARGS
-DPNG_SHARED=OFF
-DPNG_TESTS=OFF
-DSKIP_INSTALL_FILES=ON # Prevent installation of man pages et al.
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
"-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local"
BUILD_COMMAND msbuild /P:Configuration=Release INSTALL.vcxproj
@ -161,6 +176,20 @@ if (${DEP_DEBUG})
WORKING_DIRECTORY "${BINARY_DIR}"
)
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)

View 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.

View 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.

View File

@ -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
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).
The currently supported way of building Slic3r PE on Windows is with CMake and MS Visual Studio 2013.
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/).
Other setups (such as mingw + Strawberry Perl) _may_ work, but we cannot guarantee this will work
and cannot provide guidance.
Building with newer versions of MSVS (2015, 2017) may work too as reported by some of our users.
_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: [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 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)
- [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 and Debug mode](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=destdir-64-dev.7z) (88 MB, 1.3 GB unpacked)
- [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
- Intel TBB
- libcurl
Alternatively you can also compile the dependencies yourself, see below.
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)
- 64 bit: [slic3r-destdir-64.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=2%2Fslic3r-destdir-64.7z)
First obtain the Slic3 PE sources via either git or by extracting the source archive.
It is recommended you unpack this package into `C:\local\` as the environment
setup script expects it there.
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.
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.
There is a [powershell script](./deps-build/windows/slic3r-makedeps.ps1) which automates this process.
When ready, open the relevant Visual Studio command line and `cd` into the directory with Slic3r sources.
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
cd build
cmake .. -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release
nmake
cd ..
perl slic3r.pl
cmake .. -G "Visual Studio 12 Win64" -DCMAKE_PREFIX_PATH="<insert prefix path here>"
The above commands use `nmake` Makefiles.
You may also build Slic3r PE with other build tools:
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"`.
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.
This should open Visual Studio and load the Slic3r solution containing all the projects.
Make sure you use Visual Studio 2013 to open the solution.
- [msbuild](https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-reference?view=vs-2017&viewFallbackFrom=vs-2013)
- [Ninja](https://ninja-build.org/)
- [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).
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.
To build with msbuild, use the same CMake command as in previous paragraph and then build using
The above cmake commands generate Visual Studio project files with the `RelWithDebInfo` configuration only.
If you also want to use the `Release` configuration, you can generate Visual Studio projects with:
msbuild /P:Configuration=Release ALL_BUILD.vcxproj
-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
ninja
Open the preferred Visual Studio command line (64 or 32 bit variant) and `cd` into the directory with Slic3r sources.
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.

View File

@ -33,6 +33,7 @@ if(PNG_FOUND AND NOT RASTERIZER_FORCE_BUILTIN_LIBPNG)
else()
set(ZLIB_LIBRARY "")
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)
set(ZLIB_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/png/zlib ${CMAKE_CURRENT_BINARY_DIR}/png/zlib)
include_directories(${ZLIB_INCLUDE_DIR})
@ -188,3 +189,15 @@ else ()
VERBATIM
)
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 ()

View File

@ -218,7 +218,7 @@ public:
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.
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());
} else {
// Resize by duplicating the last value.
this->values.resize(n, this->values.back());
this->values.resize(n, this->values./*back*/front());
}
}
}

View File

@ -204,7 +204,9 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
if (! start_filament_gcode.empty()) {
// Process the start_filament_gcode for the active filament only.
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);
}
// 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.
} else {
// 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 {
for (const std::string &start_gcode : print.config().start_filament_gcode.values)
_writeln(file, this->placeholder_parser_process("start_filament_gcode", start_gcode, (unsigned int)(&start_gcode - &print.config().start_filament_gcode.values.front())));
DynamicConfig config;
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);
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 Point &copy : object->copies()) {
islands.emplace_back(expoly.contour);
islands.back().translate(- copy);
islands.back().translate(copy);
}
//FIXME Mege the islands in parallel.
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));
if (print.config().single_extruder_multi_material) {
// 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 {
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));
for (const std::string &end_gcode : print.config().end_filament_gcode.values) {
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));
}

View File

@ -1194,7 +1194,7 @@ Vec3d extract_euler_angles(const Eigen::Matrix<double, 3, 3, Eigen::DontAlign>&
}
else
{
angles(1) = 0.0;
angles(0) = 0.0;
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));
}

View File

@ -0,0 +1,2 @@
#cmakedefine SLIC3R_FHS @SLIC3R_FHS@
#define SLIC3R_FHS_RESOURCES "@SLIC3R_FHS_RESOURCES@"

View File

@ -22,6 +22,8 @@
#include <boost/nowide/cenv.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/Config.hpp"
#include "libslic3r/Geometry.hpp"
@ -77,6 +79,10 @@ int main(int argc, char **argv)
// The resources are packed to 'resources'
// Path from Slic3r binary to 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
// The application is packed in the .tar.bz archive (or in AppImage) as 'bin/slic3r',
// The resources are packed to 'resources'

View File

@ -66,12 +66,8 @@ void Field::PostInitialize()
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
if (m_on_kill_focus!=nullptr)
m_on_kill_focus(m_opt_id);
@ -250,11 +246,23 @@ void TextCtrl::BUILD() {
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);
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_LEFT_DOWN, ([temp](wxEvent& event)
@ -272,14 +280,15 @@ void TextCtrl::BUILD() {
temp->Bind(wxEVT_KILL_FOCUS, ([this, temp](wxEvent& e)
{
#if !defined(__WXGTK__)
e.Skip();
#if !defined(__WXGTK__)
temp->GetToolTip()->Enable(true);
#endif // __WXGTK__
if (is_defined_input_value<wxTextCtrl>(window, m_opt.type))
on_change_field();
else
on_kill_focus(e);
if (bEnterPressed) {
bEnterPressed = false;
return;
}
propagate_value();
}), temp->GetId());
/*
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent& evt)
@ -311,6 +320,14 @@ void TextCtrl::BUILD() {
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()
{
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;
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__
// #ys_FIXME_KILL_FOCUS
@ -407,15 +424,23 @@ void SpinCtrl::BUILD() {
// and on TEXT event under OSX
temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e)
{
if (tmp_value < 0)
on_kill_focus(e);
else {
e.Skip();
on_change_field();
e.Skip();
if (bEnterPressed) {
bEnterPressed = false;
return;
}
propagate_value();
}), 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
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e)
@ -430,12 +455,7 @@ void SpinCtrl::BUILD() {
tmp_value = std::stoi(value);
else tmp_value = -9999;
#ifdef __WXOSX__
if (tmp_value < 0) {
if (m_on_kill_focus != nullptr)
m_on_kill_focus(m_opt_id);
}
else
on_change_field();
propagate_value();
#endif
}), temp->GetId());
@ -445,6 +465,14 @@ void SpinCtrl::BUILD() {
window = dynamic_cast<wxWindow*>(temp);
}
void SpinCtrl::propagate_value()
{
if (tmp_value < 0)
on_kill_focus();
else
on_change_field();
}
void Choice::BUILD() {
auto size = wxSize(wxDefaultSize);
if (m_opt.height >= 0) size.SetHeight(m_opt.height);
@ -474,6 +502,7 @@ void Choice::BUILD() {
if (temp->GetWindowStyle() != wxCB_READONLY) {
temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) {
e.Skip();
if (m_opt.type == coStrings) return;
double old_val = !m_value.empty() ? boost::any_cast<double>(m_value) : -99999;
if (is_defined_input_value<wxComboBox>(window, m_opt.type)) {
if (fabs(old_val - boost::any_cast<double>(get_value())) <= 0.0001)
@ -482,7 +511,7 @@ void Choice::BUILD() {
on_change_field();
}
else
on_kill_focus(e);
on_kill_focus();
}), temp->GetId());
}
@ -692,7 +721,7 @@ boost::any& Choice::get_value()
}
else if (m_opt.gui_type == "f_enum_open") {
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);
else
m_value = atof(m_opt.enum_values[ret_enum].c_str());
@ -754,8 +783,8 @@ void PointCtrl::BUILD()
val = default_pt(1);
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);
y_textctrl = new wxTextCtrl(m_parent, wxID_ANY, Y, 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, wxEVT_TEXT_ENTER);
temp->Add(new wxStaticText(m_parent, wxID_ANY, "x : "), 0, wxALIGN_CENTER_VERTICAL, 0);
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());
// 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());
y_textctrl->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { OnKillFocus(e, x_textctrl); }), y_textctrl->GetId());
x_textctrl->Bind(wxEVT_TEXT_ENTER, ([this](wxCommandEvent e) { propagate_value(x_textctrl); }), x_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
sizer = dynamic_cast<wxSizer*>(temp);
@ -775,14 +807,12 @@ void PointCtrl::BUILD()
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();
}
else
on_kill_focus(e);
on_kill_focus();
}
void PointCtrl::set_value(const Vec2d& value, bool change_event)

View File

@ -74,7 +74,7 @@ protected:
/// Call the attached on_kill_focus method.
//! It's important to use wxEvent instead of wxFocusEvent,
//! 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.
void on_set_focus(wxEvent& event);
/// Call the attached on_change method.
@ -226,6 +226,8 @@ protected:
// current value
boost::any m_value;
bool bEnterPressed = false;
friend class OptionsGroup;
};
@ -252,6 +254,8 @@ public:
~TextCtrl() {}
void BUILD();
// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
void propagate_value();
wxWindow* window {nullptr};
virtual void set_value(const std::string& value, bool change_event = false) {
@ -310,6 +314,8 @@ public:
wxWindow* window{ nullptr };
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) {
m_disable_change_event = !change_event;
@ -393,8 +399,8 @@ public:
wxTextCtrl* y_textctrl{ nullptr };
void BUILD() override;
void OnKillFocus(wxEvent& e, wxTextCtrl* win);
// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
void propagate_value(wxTextCtrl* win);
void set_value(const Vec2d& value, bool change_event = false);
void set_value(const boost::any& value, bool change_event = false);
boost::any& get_value() override;

View File

@ -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
{
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();
::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();
}
if (boost::ends_with(sidebar_field, "y") || requires_uniform_scale())
if (boost::ends_with(sidebar_field, "y") || uniform_scale)
{
::glPushMatrix();
_render_sidebar_scale_hint(Y);
::glPopMatrix();
}
if (boost::ends_with(sidebar_field, "z") || requires_uniform_scale())
if (boost::ends_with(sidebar_field, "z") || uniform_scale)
{
::glPushMatrix();
::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
{
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);
m_arrow.render();
@ -3979,6 +3981,12 @@ BoundingBoxf3 GLCanvas3D::scene_bounding_box() const
{
BoundingBoxf3 bb = volumes_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;
}
@ -4939,8 +4947,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (evt.Entering())
{
#if defined(__WXMSW__) || defined(__linux__)
// On Windows and Linux needs focus in order to catch key events
//#if defined(__WXMSW__) || defined(__linux__)
// // 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) {
// Only set focus, if the top level window of this canvas is active.
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();
#endif
//#endif
}
else if (evt.Leaving())
{

View File

@ -1523,7 +1523,7 @@ void GLGizmoFlatten::update_planes()
const Transform3d& inst_matrix = m_model_object->instances.front()->get_matrix();
// 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
// Now we'll go through all the facets and append Points of facets sharing the same normal.

View File

@ -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)));
break;
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 =
boost::any_cast<std::vector<std::string>>(value);
}
else if (config.def()->get(opt_key)->gui_flags.compare("serialized") == 0) {
std::string str = boost::any_cast<std::string>(value);
if (str.back() == ';') str.pop_back();
// Split a string to multiple strings by a semi - colon.This is the old way of storing multi - string values.
// Currently used for the post_process config value only.
std::vector<std::string> values;
boost::split(values, str, boost::is_any_of(";"));
if (values.size() == 1 && values[0] == "")
values.resize(0);//break;
std::vector<std::string> values {};
if (!str.empty()) {
if (str.back() == ';') str.pop_back();
// Split a string to multiple strings by a semi - colon.This is the old way of storing multi - string values.
// Currently used for the post_process config value only.
boost::split(values, str, boost::is_any_of(";"));
if (values.size() == 1 && values[0] == "")
values.resize(0);
}
config.option<ConfigOptionStrings>(opt_key)->values = values;
}
else{

View File

@ -5,7 +5,6 @@
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <wx/stdpaths.h>
#include <wx/imagpng.h>
@ -15,6 +14,7 @@
#include <wx/filedlg.h>
#include <wx/dir.h>
#include <wx/wupdlock.h>
#include <wx/filefn.h>
#include "libslic3r/Utils.hpp"
#include "libslic3r/Model.hpp"
@ -83,6 +83,11 @@ GUI_App::GUI_App()
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
wxCHECK_MSG(m_imgui->init(), false, "Failed to initialize ImGui");
#endif // ENABLE_IMGUI

View File

@ -635,7 +635,7 @@ void ObjectList::get_settings_choice(const wxString& category_name)
// Add settings item for object
const auto item = GetSelection();
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 :
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->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
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;
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
selection_changed();
#endif //no __WXOSX__ //__WXMSW__
@ -1031,7 +1031,7 @@ void ObjectList::split()
parent = item;
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() ?
ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART,
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)
{
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,
!model_object->config.has("extruder") ? 0 :
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) {
for (auto id = 0; id < model_object->volumes.size(); id++) {
auto vol_item = m_objects_model->AddVolumeChild(item,
model_object->volumes[id]->name,
model_object->volumes[id]->type()/*ModelVolume::MODEL_PART*/,
from_u8(model_object->volumes[id]->name),
model_object->volumes[id]->type(),
!model_object->volumes[id]->config.has("extruder") ? 0 :
model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value,
false);

View File

@ -17,15 +17,9 @@ namespace GUI
ObjectManipulation::ObjectManipulation(wxWindow* parent) :
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->label_width = 100;
m_og->label_width = 125;
m_og->set_grid_vgap(5);
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 :
opt_key.back() == 'y' ? 1 : 2;
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
value = m_cache.position(axis);
#else
value = m_cache_position(axis);
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
}
else if (param == "rotation") {
int axis = opt_key.back() == 'x' ? 0 :
opt_key.back() == 'y' ? 1 : 2;
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
value = m_cache.rotation(axis);
#else
value = m_cache_rotation(axis);
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
}
else if (param == "scale") {
int axis = opt_key.back() == 'x' ? 0 :
opt_key.back() == 'y' ? 1 : 2;
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
value = m_cache.scale(axis);
#else
value = m_cache_scale(axis);
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
}
else if (param == "size") {
int axis = opt_key.back() == 'x' ? 0 :
opt_key.back() == 'y' ? 1 : 2;
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
value = m_cache.size(axis);
#else
value = m_cache_size(axis);
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
}
m_og->set_value(opt_key, double_to_string(value));
@ -140,7 +150,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
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), "" };
ConfigOptionDef def;
@ -154,6 +164,26 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
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);
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_scale = volume->get_instance_scaling_factor();
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()))
m_new_size = volume->get_instance_transformation().get_matrix(true, true) * (*wxGetApp().model_objects())[obj_idx]->raw_mesh().bounding_box().size();
else
// this should never happen
m_new_size = Vec3d::Zero();
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
m_new_enabled = true;
}
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();
m_new_position = box.center();
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())
{
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
m_cache.instance.reset();
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
// the selection contains a single volume
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
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()
{
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
if (_(m_new_move_label_string) != m_move_Label->GetLabel())
m_move_Label->SetLabel(_(m_new_move_label_string));
if (m_cache.move_label_string != _(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())
m_rotate_Label->SetLabel(_(m_new_rotate_label_string));
if (m_cache.rotate_label_string != _(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())
m_scale_Label->SetLabel(_(m_new_scale_label_string));
if (m_cache.scale_label_string != _(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));
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));
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_cache_position = m_new_position;
m_cache.position = m_new_position;
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));
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));
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_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));
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));
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_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));
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));
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_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)
m_og->enable();
@ -378,11 +446,14 @@ void ObjectManipulation::update_if_dirty()
void ObjectManipulation::reset_settings_value()
{
m_new_position = Vec3d::Zero();
m_new_rotation = Vec3d::Zero();
m_new_position = Vec3d::Zero();
m_new_rotation = Vec3d::Zero();
m_new_scale = Vec3d::Ones();
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
m_dirty = true;
#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
@ -393,10 +464,18 @@ void ObjectManipulation::change_position_value(const Vec3d& position)
auto canvas = wxGetApp().plater()->canvas3D();
GLCanvas3D::Selection& selection = canvas->get_selection();
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());
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
canvas->do_move();
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
m_cache.position = position;
#else
m_cache_position = position;
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
}
void ObjectManipulation::change_rotation_value(const Vec3d& rotation)
@ -415,7 +494,7 @@ void ObjectManipulation::change_rotation_value(const Vec3d& rotation)
canvas->do_rotate();
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
m_cache_rotation = rotation;
m_cache.rotation = rotation;
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
}
@ -423,9 +502,13 @@ void ObjectManipulation::change_scale_value(const Vec3d& scale)
{
Vec3d scaling_factor = scale;
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();
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
double max_diff = abs_scale_diff(X);
Axis max_diff_axis = X;
if (max_diff < abs_scale_diff(Y))
@ -449,7 +532,10 @@ void ObjectManipulation::change_scale_value(const Vec3d& scale)
canvas->do_scale();
#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
}
@ -457,7 +543,11 @@ void ObjectManipulation::change_size_value(const Vec3d& size)
{
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;
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
if (selection.is_single_full_instance())
{
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())
{
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);
Axis max_diff_axis = X;
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->do_scale();
m_cache_size = size;
m_cache.size = size;
#else
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

View File

@ -15,10 +15,41 @@ namespace GUI {
class ObjectManipulation : public OG_Settings
{
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
Vec3d m_cache_position;
Vec3d m_cache_rotation;
Vec3d m_cache_scale;
Vec3d m_cache_size;
struct Cache
{
Vec3d position;
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
Vec3d m_cache_position{ 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_size;
bool m_new_enabled;
bool m_uniform_scale {false};
public:
ObjectManipulation(wxWindow* parent);
@ -57,6 +89,9 @@ public:
// Called from the App to update the UI 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:
void reset_settings_value();

View File

@ -1053,7 +1053,7 @@ std::vector<std::string> PresetCollection::dirty_options(const Preset *edited, c
std::vector<std::string> changed;
if (edited != nullptr && reference != nullptr) {
changed = deep_compare ?
deep_diff(reference->config, edited->config) :
deep_diff(edited->config, reference->config) :
reference->config.diff(edited->config);
// 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.
@ -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.
std::string PresetCollection::path_from_name(const std::string &new_name) const
{

View File

@ -365,6 +365,9 @@ public:
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 a sorted list of system preset names.
std::vector<std::string> system_preset_names() const;
// Update the choice UI from the list of presets.
// 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.

View File

@ -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
// removed through the flattening process by this function.
// 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;
@ -911,23 +912,38 @@ static void flatten_configbundle_hierarchy(boost::property_tree::ptree &tree, co
for (const Prst &prst : presets) {
// Parse the list of comma separated values, possibly enclosed in quotes.
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)) {
// Resolve the inheritance by name.
std::vector<Prst*> &inherits_nodes = const_cast<Prst&>(prst).inherits;
for (const std::string &node_name : inherits_names) {
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));
auto it_system = std::lower_bound(system_profiles.begin(), system_profiles.end(), node_name);
if (it_system != system_profiles.end() && *it_system == node_name) {
// Loading a user config budnle, this preset is derived from a system profile.
inherits_system.emplace_back(node_name);
} else {
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 {
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");
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.
@ -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, "filament");
flatten_configbundle_hierarchy(tree, "sla_print");
flatten_configbundle_hierarchy(tree, "sla_material");
flatten_configbundle_hierarchy(tree, "printer");
flatten_configbundle_hierarchy(tree, "print", preset_bundle ? preset_bundle->prints.system_preset_names() : std::vector<std::string>());
flatten_configbundle_hierarchy(tree, "filament", preset_bundle ? preset_bundle->filaments.system_preset_names() : std::vector<std::string>());
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", preset_bundle ? preset_bundle->sla_materials.system_preset_names() : std::vector<std::string>());
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
@ -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.
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.
// Parse the obsolete preset names, to be deleted when upgrading from the old configuration structure.