diff --git a/README.md b/README.md
index f6c0677c48..53c8315617 100644
--- a/README.md
+++ b/README.md
@@ -1,30 +1,27 @@
-_Q: Oh cool, a new RepRap slicer?_
-A: Yes.
+
+
+# Slic3r Prusa Edition
-Slic3r
-======
Prebuilt Windows, OSX and Linux binaries are available through the [git releases page](https://github.com/prusa3d/Slic3r/releases).
-
-
-Slic3r takes 3D models (STL, OBJ, AMF) and converts them into G-code instructions for
-3D printers. It's compatible with any modern printer based on the RepRap toolchain,
-including all those based on the Marlin, Sprinter and Repetier firmware. It also works
+Slic3r takes 3D models (STL, OBJ, AMF) and converts them into G-code
+instructions for FFF printers or PNG layers for mSLA 3D printers. It's
+compatible with any modern printer based on the RepRap toolchain, including all
+those based on the Marlin, Prusa, Sprinter and Repetier firmware. It also works
with Mach3, LinuxCNC and Machinekit controllers.
-See the [project homepage](http://slic3r.org/) at slic3r.org and the
-[manual](http://manual.slic3r.org/) for more information.
+See the [project homepage](https://www.prusa3d.com/slic3r-prusa-edition/) and
+the [documentation directory](doc/) for more information.
### What language is it written in?
-The core geometric algorithms and data structures are written in C++,
-and Perl is used for high-level flow abstraction, GUI and testing.
-If you're wondering why Perl, see https://xkcd.com/224/
+All user facing code is written in C++, and some legacy code as well as unit
+tests are written in Perl. Perl is not required for either development or use
+of Slic3r.
-The C++ API is public and its use in other projects is encouraged.
-The goal is to make Slic3r fully modular so that any part of its logic
-can be used separately.
+The slicing core is the `libslic3r` library, which can be built and used in a standalone way.
+The command line interface is a thin wrapper over `libslic3r`.
### What are Slic3r's main features?
@@ -49,340 +46,33 @@ Other major features are:
* several infill patterns including honeycomb, spirals, Hilbert curves
* support material, raft, brim, skirt
* **standby temperature** and automatic wiping for multi-extruder printing
-* customizable **G-code macros** and output filename with variable placeholders
+* [customizable **G-code macros**](https://github.com/prusa3d/Slic3r/wiki/Slic3r-Prusa-Edition-Macro-Language) and output filename with variable placeholders
* support for **post-processing scripts**
* **cooling logic** controlling fan speed and dynamic print speed
-### How to install?
+### Development
-You can download a precompiled package from [slic3r.org](http://slic3r.org/);
-it will run without the need for any dependency.
-
-If you want to compile the source yourself follow the instructions on one of these wiki pages:
-* [Linux](https://github.com/alexrj/Slic3r/wiki/Running-Slic3r-from-git-on-GNU-Linux)
-* [Windows](https://github.com/prusa3d/Slic3r/wiki/How-to-compile-Slic3r-Prusa-Edition-on-MS-Windows)
-* [Mac OSX](https://github.com/alexrj/Slic3r/wiki/Running-Slic3r-from-git-on-OS-X)
+If you want to compile the source yourself, follow the instructions on one of
+these documentation pages:
+* [Linux](doc/How%20to%20build%20-%20Linux%20et%20al.md)
+* [macOS](doc/How%20to%20build%20-%20Mac%20OS.md)
+* [Windows](doc/How%20to%20build%20-%20Windows.md)
### Can I help?
Sure! You can do the following to find things that are available to help with:
-* [Pull Request Milestone](https://github.com/alexrj/Slic3r/milestone/31)
- * Please comment in the related github issue that you are working on it so that other people know.
-* Items in the [TODO](https://github.com/alexrj/Slic3r/wiki/TODO) wiki page.
- * Please comment in the related github issue that you are working on it so that other people know.
-* Drop me a line at aar@cpan.org.
-* You can also find me (rarely) in #reprap and in #slic3r on [FreeNode](https://webchat.freenode.net) with the nickname _Sound_. Another contributor, _LoH_, is also in both channels.
-* Add an [issue](https://github.com/alexrj/Slic3r/issues) to the github tracker if it isn't already present.
-
-Before sending patches and pull requests contact me (preferably through opening a github issue or commenting on an existing, related, issue) to discuss your proposed
-changes: this way we'll ensure nobody wastes their time and no conflicts arise
-in development.
+* Add an [issue](https://github.com/prusa3d/Slic3r/issues) to the github tracker if it isn't already present.
+* Look at [issues labeled "volunteer needed"](https://github.com/prusa3d/Slic3r/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3A%22volunteer+needed%22)
### What's Slic3r license?
-Slic3r is licensed under the _GNU Affero General Public License, version 3_.
-The author is Alessandro Ranellucci.
+Slic3r PE is licensed under the _GNU Affero General Public License, version 3_.
+The Prusa Edition is originally based on Slic3r by Alessandro Ranellucci.
The [Silk icon set](http://www.famfamfam.com/lab/icons/silk/) used in Slic3r is
licensed under the _Creative Commons Attribution 3.0 License_.
The author of the Silk icon set is Mark James.
-### How can I invoke slic3r.pl using the command line?
+### How can I use Slic3r PE from the command line?
- Usage: slic3r.pl [ OPTIONS ] [ file.stl ] [ file2.stl ] ...
-
- --help Output this usage screen and exit
- --version Output the version of Slic3r and exit
- --save Save configuration to the specified file
- --load Load configuration from the specified file. It can be used
- more than once to load options from multiple files.
- -o, --output File to output gcode to (by default, the file will be saved
- into the same directory as the input file using the
- --output-filename-format to generate the filename.) If a
- directory is specified for this option, the output will
- be saved under that directory, and the filename will be
- generated by --output-filename-format.
-
- Non-slicing actions (no G-code will be generated):
- --repair Repair given STL files and save them as _fixed.obj
- --cut Cut given input files at given Z (relative) and export
- them as _upper.stl and _lower.stl
- --split Split the shells contained in given STL file into several STL files
- --info Output information about the supplied file(s) and exit
-
- -j, --threads Number of threads to use (1+, default: 2)
-
- GUI options:
- --gui Forces the GUI launch instead of command line slicing (if you
- supply a model file, it will be loaded into the plater)
- --no-plater Disable the plater tab
- --no-gui Forces the command line slicing instead of gui.
- This takes precedence over --gui if both are present.
- --autosave Automatically export current configuration to the specified file
-
- Output options:
- --output-filename-format
- Output file name format; all config options enclosed in brackets
- will be replaced by their values, as well as [input_filename_base]
- and [input_filename] (default: [input_filename_base].gcode)
- --post-process Generated G-code will be processed with the supplied script;
- call this more than once to process through multiple scripts.
- --export-png Export zipped PNG files containing slices instead of G-code.
- -m, --merge If multiple files are supplied, they will be composed into a single
- print rather than processed individually.
-
- Printer options:
- --nozzle-diameter Diameter of nozzle in mm (default: 0.5)
- --print-center Coordinates in mm of the point to center the print around
- (default: 100,100)
- --z-offset Additional height in mm to add to vertical coordinates
- (+/-, default: 0)
- --gcode-flavor The type of G-code to generate (reprap/teacup/repetier/makerware/sailfish/mach3/machinekit/smoothie/no-extrusion,
- default: reprap)
- --use-relative-e-distances Enable this to get relative E values (default: no)
- --use-firmware-retraction Enable firmware-controlled retraction using G10/G11 (default: no)
- --use-volumetric-e Express E in cubic millimeters and prepend M200 (default: no)
- --gcode-comments Make G-code verbose by adding comments (default: no)
-
- Filament options:
- --filament-diameter Diameter in mm of your raw filament (default: 3)
- --extrusion-multiplier
- Change this to alter the amount of plastic extruded. There should be
- very little need to change this value, which is only useful to
- compensate for filament packing (default: 1)
- --temperature Extrusion temperature in degree Celsius, set 0 to disable (default: 200)
- --first-layer-temperature Extrusion temperature for the first layer, in degree Celsius,
- set 0 to disable (default: same as --temperature)
- --bed-temperature Heated bed temperature in degree Celsius, set 0 to disable (default: 0)
- --first-layer-bed-temperature Heated bed temperature for the first layer, in degree Celsius,
- set 0 to disable (default: same as --bed-temperature)
-
- Speed options:
- --travel-speed Speed of non-print moves in mm/s (default: 130)
- --perimeter-speed Speed of print moves for perimeters in mm/s (default: 30)
- --small-perimeter-speed
- Speed of print moves for small perimeters in mm/s or % over perimeter speed
- (default: 30)
- --external-perimeter-speed
- Speed of print moves for the external perimeter in mm/s or % over perimeter speed
- (default: 70%)
- --infill-speed Speed of print moves in mm/s (default: 60)
- --solid-infill-speed Speed of print moves for solid surfaces in mm/s or % over infill speed
- (default: 60)
- --top-solid-infill-speed Speed of print moves for top surfaces in mm/s or % over solid infill speed
- (default: 50)
- --support-material-speed
- Speed of support material print moves in mm/s (default: 60)
- --support-material-interface-speed
- Speed of support material interface print moves in mm/s or % over support material
- speed (default: 100%)
- --bridge-speed Speed of bridge print moves in mm/s (default: 60)
- --gap-fill-speed Speed of gap fill print moves in mm/s (default: 20)
- --first-layer-speed Speed of print moves for bottom layer, expressed either as an absolute
- value or as a percentage over normal speeds (default: 30%)
-
- Acceleration options:
- --perimeter-acceleration
- Overrides firmware's default acceleration for perimeters. (mm/s^2, set zero
- to disable; default: 0)
- --infill-acceleration
- Overrides firmware's default acceleration for infill. (mm/s^2, set zero
- to disable; default: 0)
- --bridge-acceleration
- Overrides firmware's default acceleration for bridges. (mm/s^2, set zero
- to disable; default: 0)
- --first-layer-acceleration
- Overrides firmware's default acceleration for first layer. (mm/s^2, set zero
- to disable; default: 0)
- --default-acceleration
- Acceleration will be reset to this value after the specific settings above
- have been applied. (mm/s^2, set zero to disable; default: 0)
-
- Accuracy options:
- --layer-height Layer height in mm (default: 0.3)
- --first-layer-height Layer height for first layer (mm or %, default: 0.35)
- --infill-every-layers
- Infill every N layers (default: 1)
- --solid-infill-every-layers
- Force a solid layer every N layers (default: 0)
-
- Print options:
- --perimeters Number of perimeters/horizontal skins (range: 0+, default: 3)
- --top-solid-layers Number of solid layers to do for top surfaces (range: 0+, default: 3)
- --bottom-solid-layers Number of solid layers to do for bottom surfaces (range: 0+, default: 3)
- --solid-layers Shortcut for setting the two options above at once
- --fill-density Infill density (range: 0%-100%, default: 40%)
- --fill-angle Infill angle in degrees (range: 0-90, default: 45)
- --fill-pattern Pattern to use to fill non-solid layers (default: honeycomb)
- --solid-fill-pattern Pattern to use to fill solid layers (default: rectilinear)
- --start-gcode Load initial G-code from the supplied file. This will overwrite
- the default command (home all axes [G28]).
- --end-gcode Load final G-code from the supplied file. This will overwrite
- the default commands (turn off temperature [M104 S0],
- home X axis [G28 X], disable motors [M84]).
- --before-layer-gcode Load before-layer-change G-code from the supplied file (default: nothing).
- --layer-gcode Load after-layer-change G-code from the supplied file (default: nothing).
- --toolchange-gcode Load tool-change G-code from the supplied file (default: nothing).
- --seam-position Position of loop starting points (random/nearest/aligned, default: aligned).
- --external-perimeters-first Reverse perimeter order. (default: no)
- --spiral-vase Experimental option to raise Z gradually when printing single-walled vases
- (default: no)
- --only-retract-when-crossing-perimeters
- Disable retraction when travelling between infill paths inside the same island.
- (default: no)
- --solid-infill-below-area
- Force solid infill when a region has a smaller area than this threshold
- (mm^2, default: 70)
- --infill-only-where-needed
- Only infill under ceilings (default: no)
- --infill-first Make infill before perimeters (default: no)
-
- Quality options (slower slicing):
- --extra-perimeters Add more perimeters when needed (default: yes)
- --avoid-crossing-perimeters Optimize travel moves so that no perimeters are crossed (default: no)
- --thin-walls Detect single-width walls (default: yes)
- --overhangs Experimental option to use bridge flow, speed and fan for overhangs
- (default: yes)
-
- Support material options:
- --support-material Generate support material for overhangs
- --support-material-threshold
- Overhang threshold angle (range: 0-90, set 0 for automatic detection,
- default: 0)
- --support-material-pattern
- Pattern to use for support material (default: honeycomb)
- --support-material-spacing
- Spacing between pattern lines (mm, default: 2.5)
- --support-material-angle
- Support material angle in degrees (range: 0-90, default: 0)
- --support-material-contact-distance
- Vertical distance between object and support material
- (0+, default: 0.2)
- --support-material-interface-layers
- Number of perpendicular layers between support material and object (0+, default: 3)
- --support-material-interface-spacing
- Spacing between interface pattern lines (mm, set 0 to get a solid layer, default: 0)
- --raft-layers Number of layers to raise the printed objects by (range: 0+, default: 0)
- --support-material-enforce-layers
- Enforce support material on the specified number of layers from bottom,
- regardless of --support-material and threshold (0+, default: 0)
- --dont-support-bridges
- Experimental option for preventing support material from being generated under bridged areas (default: yes)
-
- Retraction options:
- --retract-length Length of retraction in mm when pausing extrusion (default: 1)
- --retract-speed Speed for retraction in mm/s (default: 30)
- --retract-restart-extra
- Additional amount of filament in mm to push after
- compensating retraction (default: 0)
- --retract-before-travel
- Only retract before travel moves of this length in mm (default: 2)
- --retract-lift Lift Z by the given distance in mm when retracting (default: 0)
- --retract-lift-above Only lift Z when above the specified height (default: 0)
- --retract-lift-below Only lift Z when below the specified height (default: 0)
- --retract-layer-change
- Enforce a retraction before each Z move (default: no)
- --wipe Wipe the nozzle while doing a retraction (default: no)
-
- Retraction options for multi-extruder setups:
- --retract-length-toolchange
- Length of retraction in mm when disabling tool (default: 10)
- --retract-restart-extra-toolchange
- Additional amount of filament in mm to push after
- switching tool (default: 0)
-
- Cooling options:
- --cooling Enable fan and cooling control
- --min-fan-speed Minimum fan speed (default: 35%)
- --max-fan-speed Maximum fan speed (default: 100%)
- --bridge-fan-speed Fan speed to use when bridging (default: 100%)
- --fan-below-layer-time Enable fan if layer print time is below this approximate number
- of seconds (default: 60)
- --slowdown-below-layer-time Slow down if layer print time is below this approximate number
- of seconds (default: 30)
- --min-print-speed Minimum print speed (mm/s, default: 10)
- --disable-fan-first-layers Disable fan for the first N layers (default: 1)
- --fan-always-on Keep fan always on at min fan speed, even for layers that don't need
- cooling
-
- Skirt options:
- --skirts Number of skirts to draw (0+, default: 1)
- --skirt-distance Distance in mm between innermost skirt and object
- (default: 6)
- --skirt-height Height of skirts to draw (expressed in layers, 0+, default: 1)
- --min-skirt-length Generate no less than the number of loops required to consume this length
- of filament on the first layer, for each extruder (mm, 0+, default: 0)
- --brim-width Width of the brim that will get added to each object to help adhesion
- (mm, default: 0)
-
- Transform options:
- --scale Factor for scaling input object (default: 1)
- --rotate Rotation angle in degrees (0-360, default: 0)
- --duplicate Number of items with auto-arrange (1+, default: 1)
- --duplicate-grid Number of items with grid arrangement (default: 1,1)
- --duplicate-distance Distance in mm between copies (default: 6)
- --dont-arrange Don't arrange the objects on the build plate. The model coordinates
- define the absolute positions on the build plate.
- The option --print-center will be ignored.
- --xy-size-compensation
- Grow/shrink objects by the configured absolute distance (mm, default: 0)
-
- Sequential printing options:
- --complete-objects When printing multiple objects and/or copies, complete each one before
- starting the next one; watch out for extruder collisions (default: no)
- --extruder-clearance-radius Radius in mm above which extruder won't collide with anything
- (default: 20)
- --extruder-clearance-height Maximum vertical extruder depth; i.e. vertical distance from
- extruder tip and carriage bottom (default: 20)
-
- Miscellaneous options:
- --notes Notes to be added as comments to the output file
- --resolution Minimum detail resolution (mm, set zero for full resolution, default: 0)
-
- Flow options (advanced):
- --extrusion-width Set extrusion width manually; it accepts either an absolute value in mm
- (like 0.65) or a percentage over layer height (like 200%)
- --first-layer-extrusion-width
- Set a different extrusion width for first layer
- --perimeter-extrusion-width
- Set a different extrusion width for perimeters
- --external-perimeter-extrusion-width
- Set a different extrusion width for external perimeters
- --infill-extrusion-width
- Set a different extrusion width for infill
- --solid-infill-extrusion-width
- Set a different extrusion width for solid infill
- --top-infill-extrusion-width
- Set a different extrusion width for top infill
- --support-material-extrusion-width
- Set a different extrusion width for support material
- --infill-overlap Overlap between infill and perimeters (default: 15%)
- --bridge-flow-ratio Multiplier for extrusion when bridging (> 0, default: 1)
-
- Multiple extruder options:
- --extruder-offset Offset of each extruder, if firmware doesn't handle the displacement
- (can be specified multiple times, default: 0x0)
- --perimeter-extruder
- Extruder to use for perimeters and brim (1+, default: 1)
- --infill-extruder Extruder to use for infill (1+, default: 1)
- --solid-infill-extruder Extruder to use for solid infill (1+, default: 1)
- --support-material-extruder
- Extruder to use for support material, raft and skirt (1+, default: 1)
- --support-material-interface-extruder
- Extruder to use for support material interface (1+, default: 1)
- --ooze-prevention Drop temperature and park extruders outside a full skirt for automatic wiping
- (default: no)
- --ooze-prevention Drop temperature and park extruders outside a full skirt for automatic wiping
- (default: no)
- --standby-temperature-delta
- Temperature difference to be applied when an extruder is not active and
- --ooze-prevention is enabled (default: -5)
-
-
-If you want to change a preset file, just do
-
- slic3r.pl --load config.ini --layer-height 0.25 --save config.ini
-
-If you want to slice a file overriding an option contained in your preset file:
-
- slic3r.pl --load config.ini --layer-height 0.25 file.stl
+Please refer to the [Command Line Interface](https://github.com/prusa3d/Slic3r/wiki/Command-Line-Interface) wiki page.
diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake
index a7397316f4..0806c23887 100644
--- a/deps/deps-windows.cmake
+++ b/deps/deps-windows.cmake
@@ -62,7 +62,7 @@ ExternalProject_Add(dep_tbb
-DTBB_BUILD_SHARED=OFF
-DTBB_BUILD_TESTS=OFF
"-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local"
- BUILD_COMMAND msbuild /P:Configuration=Release INSTALL.vcxproj
+ BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj
INSTALL_COMMAND ""
)
if (${DEP_DEBUG})
@@ -70,7 +70,7 @@ if (${DEP_DEBUG})
ExternalProject_Add_Step(dep_tbb build_debug
DEPENDEES build
DEPENDERS install
- COMMAND msbuild /P:Configuration=Debug INSTALL.vcxproj
+ COMMAND msbuild /m /P:Configuration=Debug INSTALL.vcxproj
WORKING_DIRECTORY "${BINARY_DIR}"
)
endif ()
@@ -86,7 +86,7 @@ ExternalProject_Add(dep_gtest
-Dgtest_force_shared_crt=ON
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
"-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local"
- BUILD_COMMAND msbuild /P:Configuration=Release INSTALL.vcxproj
+ BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj
INSTALL_COMMAND ""
)
if (${DEP_DEBUG})
@@ -94,7 +94,7 @@ if (${DEP_DEBUG})
ExternalProject_Add_Step(dep_gtest build_debug
DEPENDEES build
DEPENDERS install
- COMMAND msbuild /P:Configuration=Debug INSTALL.vcxproj
+ COMMAND msbuild /m /P:Configuration=Debug INSTALL.vcxproj
WORKING_DIRECTORY "${BINARY_DIR}"
)
endif ()
@@ -114,7 +114,7 @@ ExternalProject_Add(dep_nlopt
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
-DCMAKE_DEBUG_POSTFIX=d
"-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local"
- BUILD_COMMAND msbuild /P:Configuration=Release INSTALL.vcxproj
+ BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj
INSTALL_COMMAND ""
)
if (${DEP_DEBUG})
@@ -122,7 +122,7 @@ if (${DEP_DEBUG})
ExternalProject_Add_Step(dep_nlopt build_debug
DEPENDEES build
DEPENDERS install
- COMMAND msbuild /P:Configuration=Debug INSTALL.vcxproj
+ COMMAND msbuild /m /P:Configuration=Debug INSTALL.vcxproj
WORKING_DIRECTORY "${BINARY_DIR}"
)
endif ()
@@ -138,7 +138,7 @@ ExternalProject_Add(dep_zlib
"-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
+ BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj
INSTALL_COMMAND ""
)
if (${DEP_DEBUG})
@@ -146,7 +146,7 @@ if (${DEP_DEBUG})
ExternalProject_Add_Step(dep_zlib build_debug
DEPENDEES build
DEPENDERS install
- COMMAND msbuild /P:Configuration=Debug INSTALL.vcxproj
+ COMMAND msbuild /m /P:Configuration=Debug INSTALL.vcxproj
WORKING_DIRECTORY "${BINARY_DIR}"
)
endif ()
diff --git a/doc/How to build - Windows.md b/doc/How to build - Windows.md
index 5de983dd77..331a3533a1 100644
--- a/doc/How to build - Windows.md
+++ b/doc/How to build - Windows.md
@@ -12,7 +12,7 @@ _Note:_ Thanks to [**@supermerill**](https://github.com/supermerill) for testing
### Dependencies
On Windows Slic3r is built against statically built libraries.
-We provide a prebuilt package of all the needed dependencies.
+We provide a prebuilt package of all the needed dependencies. This package only works on Visual Studio 2013, so if you are using a newer version of Visual Studio, you need to compile the dependencies yourself as per [below](#building-the-dependencies-package-yourself).
The package comes in a several variants:
- [64 bit, Release mode only](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=destdir-64.7z) (41 MB, 578 MB unpacked)
@@ -28,7 +28,7 @@ Alternatively you can also compile the dependencies yourself, see below.
### Building Slic3r PE with Visual Studio
-First obtain the Slic3 PE sources via either git or by extracting the source archive.
+First obtain the Slic3r PE sources via either git or by extracting the source archive.
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.
@@ -66,7 +66,7 @@ There are several options for building from the command line:
To build with msbuild, use the same CMake command as in previous paragraph and then build using
- msbuild /P:Configuration=Release ALL_BUILD.vcxproj
+ msbuild /m /P:Configuration=Release ALL_BUILD.vcxproj
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.
@@ -84,7 +84,7 @@ 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
+ msbuild /m ALL_BUILD.vcxproj
You can also use the Visual Studio GUI or other generators as mentioned above.
@@ -97,7 +97,11 @@ place the `build` directory relatively close to the drive root.
Note that the build variant that you may choose using Visual Studio (i.e. _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.
+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.
diff --git a/resources/icons/cog.svg b/resources/icons/cog.svg
new file mode 100644
index 0000000000..07adb66101
--- /dev/null
+++ b/resources/icons/cog.svg
@@ -0,0 +1,17 @@
+
+
+
diff --git a/resources/icons/cooling.svg b/resources/icons/cooling.svg
new file mode 100644
index 0000000000..b5d80e434a
--- /dev/null
+++ b/resources/icons/cooling.svg
@@ -0,0 +1,25 @@
+
+
+
diff --git a/resources/icons/funnel.svg b/resources/icons/funnel.svg
new file mode 100644
index 0000000000..8877722e33
--- /dev/null
+++ b/resources/icons/funnel.svg
@@ -0,0 +1,15 @@
+
+
+
diff --git a/resources/icons/infill.svg b/resources/icons/infill.svg
new file mode 100644
index 0000000000..fcb1f99c9e
--- /dev/null
+++ b/resources/icons/infill.svg
@@ -0,0 +1,33 @@
+
+
+
diff --git a/resources/icons/layers.svg b/resources/icons/layers.svg
index 7718a8cbd5..cd71fab3a3 100644
--- a/resources/icons/layers.svg
+++ b/resources/icons/layers.svg
@@ -1,25 +1,27 @@
-
+
diff --git a/resources/icons/note.svg b/resources/icons/note.svg
new file mode 100644
index 0000000000..c142142010
--- /dev/null
+++ b/resources/icons/note.svg
@@ -0,0 +1,25 @@
+
+
+
diff --git a/resources/icons/output+page_white.svg b/resources/icons/output+page_white.svg
new file mode 100644
index 0000000000..ec1518f252
--- /dev/null
+++ b/resources/icons/output+page_white.svg
@@ -0,0 +1,21 @@
+
+
+
diff --git a/resources/icons/printer.svg b/resources/icons/printer.svg
new file mode 100644
index 0000000000..91e103ec70
--- /dev/null
+++ b/resources/icons/printer.svg
@@ -0,0 +1,14 @@
+
+
+
diff --git a/resources/icons/skirt+brim.svg b/resources/icons/skirt+brim.svg
new file mode 100644
index 0000000000..9242761b61
--- /dev/null
+++ b/resources/icons/skirt+brim.svg
@@ -0,0 +1,19 @@
+
+
+
diff --git a/resources/icons/spool.svg b/resources/icons/spool.svg
new file mode 100644
index 0000000000..8403659381
--- /dev/null
+++ b/resources/icons/spool.svg
@@ -0,0 +1,21 @@
+
+
+
diff --git a/resources/icons/support.svg b/resources/icons/support.svg
new file mode 100644
index 0000000000..65c7592c83
--- /dev/null
+++ b/resources/icons/support.svg
@@ -0,0 +1,25 @@
+
+
+
diff --git a/resources/icons/time.svg b/resources/icons/time.svg
new file mode 100644
index 0000000000..5d8f23cfc3
--- /dev/null
+++ b/resources/icons/time.svg
@@ -0,0 +1,16 @@
+
+
+
diff --git a/resources/icons/wrench.svg b/resources/icons/wrench.svg
new file mode 100644
index 0000000000..7966da8d8f
--- /dev/null
+++ b/resources/icons/wrench.svg
@@ -0,0 +1,24 @@
+
+
+
diff --git a/src/admesh/connect.cpp b/src/admesh/connect.cpp
index 5fc992b63f..fb32132194 100644
--- a/src/admesh/connect.cpp
+++ b/src/admesh/connect.cpp
@@ -39,8 +39,7 @@ static void stl_record_neighbors(stl_file *stl,
stl_hash_edge *edge_a, stl_hash_edge *edge_b);
static void stl_initialize_facet_check_exact(stl_file *stl);
static void stl_initialize_facet_check_nearby(stl_file *stl);
-static void stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge,
- stl_vertex *a, stl_vertex *b);
+static void stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, const stl_vertex *a, const stl_vertex *b);
static int stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge,
stl_vertex *a, stl_vertex *b, float tolerance);
static void insert_hash_edge(stl_file *stl, stl_hash_edge edge,
@@ -60,41 +59,40 @@ extern int stl_check_normal_vector(stl_file *stl,
int facet_num, int normal_fix_flag);
static void stl_update_connects_remove_1(stl_file *stl, int facet_num);
-
-void
-stl_check_facets_exact(stl_file *stl) {
- /* This function builds the neighbors list. No modifications are made
- * to any of the facets. The edges are said to match only if all six
- * floats of the first edge matches all six floats of the second edge.
- */
-
- stl_hash_edge edge;
- stl_facet facet;
- int i;
- int j;
-
- if (stl->error) return;
+// This function builds the neighbors list. No modifications are made
+// to any of the facets. The edges are said to match only if all six
+// floats of the first edge matches all six floats of the second edge.
+void stl_check_facets_exact(stl_file *stl)
+{
+ if (stl->error)
+ return;
stl->stats.connected_edges = 0;
stl->stats.connected_facets_1_edge = 0;
stl->stats.connected_facets_2_edge = 0;
stl->stats.connected_facets_3_edge = 0;
- stl_initialize_facet_check_exact(stl);
+ // If any two of the three vertices are found to be exactally the same, call them degenerate and remove the facet.
+ // Do it before the next step, as the next step stores references to the face indices in the hash tables and removing a facet
+ // will break the references.
+ for (int i = 0; i < stl->stats.number_of_facets;) {
+ stl_facet &facet = stl->facet_start[i];
+ if (facet.vertex[0] == facet.vertex[1] || facet.vertex[1] == facet.vertex[2] || facet.vertex[0] == facet.vertex[2]) {
+ // Remove the degenerate facet.
+ facet = stl->facet_start[--stl->stats.number_of_facets];
+ stl->stats.facets_removed += 1;
+ stl->stats.degenerate_facets += 1;
+ } else
+ ++ i;
+ }
- for(i = 0; i < stl->stats.number_of_facets; i++) {
- facet = stl->facet_start[i];
- // If any two of the three vertices are found to be exactally the same, call them degenerate and remove the facet.
- if (facet.vertex[0] == facet.vertex[1] ||
- facet.vertex[1] == facet.vertex[2] ||
- facet.vertex[0] == facet.vertex[2]) {
- stl->stats.degenerate_facets += 1;
- stl_remove_facet(stl, i);
- -- i;
- continue;
- }
- for(j = 0; j < 3; j++) {
- edge.facet_number = i;
+ // Connect neighbor edges.
+ stl_initialize_facet_check_exact(stl);
+ for (int i = 0; i < stl->stats.number_of_facets; i++) {
+ const stl_facet &facet = stl->facet_start[i];
+ for (int j = 0; j < 3; j++) {
+ stl_hash_edge edge;
+ edge.facet_number = i;
edge.which_edge = j;
stl_load_edge_exact(stl, &edge, &facet.vertex[j], &facet.vertex[(j + 1) % 3]);
insert_hash_edge(stl, edge, stl_record_neighbors);
@@ -109,9 +107,7 @@ stl_check_facets_exact(stl_file *stl) {
#endif
}
-static void
-stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge,
- stl_vertex *a, stl_vertex *b) {
+static void stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, const stl_vertex *a, const stl_vertex *b) {
if (stl->error) return;
@@ -333,7 +329,9 @@ static void stl_free_edges(stl_file *stl)
}
}
free(stl->heads);
+ stl->heads = nullptr;
free(stl->tail);
+ stl->tail = nullptr;
}
static void stl_initialize_facet_check_nearby(stl_file *stl)
diff --git a/src/admesh/stl.h b/src/admesh/stl.h
index afff3deac1..2c436b426b 100644
--- a/src/admesh/stl.h
+++ b/src/admesh/stl.h
@@ -127,7 +127,6 @@ typedef struct {
typedef struct {
FILE *fp;
stl_facet *facet_start;
- stl_edge *edge_start;
stl_hash_edge **heads;
stl_hash_edge *tail;
int M;
@@ -142,7 +141,6 @@ typedef struct {
extern void stl_open(stl_file *stl, const char *file);
extern void stl_close(stl_file *stl);
extern void stl_stats_out(stl_file *stl, FILE *file, char *input_file);
-extern void stl_print_edges(stl_file *stl, FILE *file);
extern void stl_print_neighbors(stl_file *stl, char *file);
extern void stl_put_little_int(FILE *fp, int value_in);
extern void stl_put_little_float(FILE *fp, float value_in);
diff --git a/src/admesh/stl_io.cpp b/src/admesh/stl_io.cpp
index 71e434cbcd..85f66785b3 100644
--- a/src/admesh/stl_io.cpp
+++ b/src/admesh/stl_io.cpp
@@ -33,24 +33,6 @@
#define SEEK_END 2
#endif
-void
-stl_print_edges(stl_file *stl, FILE *file) {
- int i;
- int edges_allocated;
-
- if (stl->error) return;
-
- edges_allocated = stl->stats.number_of_facets * 3;
- for(i = 0; i < edges_allocated; i++) {
- fprintf(file, "%d, %f, %f, %f, %f, %f, %f\n",
- stl->edge_start[i].facet_number,
- stl->edge_start[i].p1(0), stl->edge_start[i].p1(1),
- stl->edge_start[i].p1(2), stl->edge_start[i].p2(0),
- stl->edge_start[i].p2(1), stl->edge_start[i].p2(2));
- }
-}
-
-
void
stl_stats_out(stl_file *stl, FILE *file, char *input_file) {
if (stl->error) return;
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index d9f907f034..dc017f737f 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -1034,6 +1034,15 @@ void GCode::_do_export(Print &print, FILE *file)
}
_write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
_write(file, m_writer.postamble());
+
+ // adds tags for time estimators
+ if (print.config().remaining_times.value)
+ {
+ _writeln(file, GCodeTimeEstimator::Normal_Last_M73_Output_Placeholder_Tag);
+ if (m_silent_time_estimator_enabled)
+ _writeln(file, GCodeTimeEstimator::Silent_Last_M73_Output_Placeholder_Tag);
+ }
+
print.throw_if_canceled();
// calculates estimated printing time
diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp
index d1ad4f5752..321d9a3427 100644
--- a/src/libslic3r/GCode/Analyzer.cpp
+++ b/src/libslic3r/GCode/Analyzer.cpp
@@ -776,6 +776,9 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
preview_data.ranges.width.update_from(width_range);
preview_data.ranges.feedrate.update_from(feedrate_range);
preview_data.ranges.volumetric_rate.update_from(volumetric_rate_range);
+
+ // we need to sort the layers by their z as they can be shuffled in case of sequential prints
+ std::sort(preview_data.extrusion.layers.begin(), preview_data.extrusion.layers.end(), [](const GCodePreviewData::Extrusion::Layer& l1, const GCodePreviewData::Extrusion::Layer& l2)->bool { return l1.z < l2.z; });
}
void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data, std::function cancel_callback)
@@ -855,6 +858,11 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data, s
preview_data.ranges.height.update_from(height_range);
preview_data.ranges.width.update_from(width_range);
preview_data.ranges.feedrate.update_from(feedrate_range);
+
+ // we need to sort the polylines by their min z as they can be shuffled in case of sequential prints
+ std::sort(preview_data.travel.polylines.begin(), preview_data.travel.polylines.end(),
+ [](const GCodePreviewData::Travel::Polyline& p1, const GCodePreviewData::Travel::Polyline& p2)->bool
+ { return unscale(p1.polyline.bounding_box().min(2)) < unscale(p2.polyline.bounding_box().min(2)); });
}
void GCodeAnalyzer::_calc_gcode_preview_retractions(GCodePreviewData& preview_data, std::function cancel_callback)
@@ -877,6 +885,11 @@ void GCodeAnalyzer::_calc_gcode_preview_retractions(GCodePreviewData& preview_da
Vec3crd position(scale_(move.start_position.x()), scale_(move.start_position.y()), scale_(move.start_position.z()));
preview_data.retraction.positions.emplace_back(position, move.data.width, move.data.height);
}
+
+ // we need to sort the positions by their z as they can be shuffled in case of sequential prints
+ std::sort(preview_data.retraction.positions.begin(), preview_data.retraction.positions.end(),
+ [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2)->bool
+ { return unscale(p1.position(2)) < unscale(p2.position(2)); });
}
void GCodeAnalyzer::_calc_gcode_preview_unretractions(GCodePreviewData& preview_data, std::function cancel_callback)
@@ -899,6 +912,11 @@ void GCodeAnalyzer::_calc_gcode_preview_unretractions(GCodePreviewData& preview_
Vec3crd position(scale_(move.start_position.x()), scale_(move.start_position.y()), scale_(move.start_position.z()));
preview_data.unretraction.positions.emplace_back(position, move.data.width, move.data.height);
}
+
+ // we need to sort the positions by their z as they can be shuffled in case of sequential prints
+ std::sort(preview_data.unretraction.positions.begin(), preview_data.unretraction.positions.end(),
+ [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2)->bool
+ { return unscale(p1.position(2)) < unscale(p2.position(2)); });
}
// Return an estimate of the memory consumed by the time estimator.
diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp
index 461b4cd356..4b4e9f587e 100644
--- a/src/libslic3r/GCodeTimeEstimator.cpp
+++ b/src/libslic3r/GCodeTimeEstimator.cpp
@@ -171,6 +171,8 @@ namespace Slic3r {
const std::string GCodeTimeEstimator::Normal_First_M73_Output_Placeholder_Tag = "; NORMAL_FIRST_M73_OUTPUT_PLACEHOLDER";
const std::string GCodeTimeEstimator::Silent_First_M73_Output_Placeholder_Tag = "; SILENT_FIRST_M73_OUTPUT_PLACEHOLDER";
+ const std::string GCodeTimeEstimator::Normal_Last_M73_Output_Placeholder_Tag = "; NORMAL_LAST_M73_OUTPUT_PLACEHOLDER";
+ const std::string GCodeTimeEstimator::Silent_Last_M73_Output_Placeholder_Tag = "; SILENT_LAST_M73_OUTPUT_PLACEHOLDER";
GCodeTimeEstimator::GCodeTimeEstimator(EMode mode)
: _mode(mode)
@@ -306,9 +308,17 @@ namespace Slic3r {
sprintf(time_line, time_mask.c_str(), "0", _get_time_minutes(_time).c_str());
gcode_line = time_line;
}
+ // replaces placeholders for final line M73 with the real lines
+ else if (((_mode == Normal) && (gcode_line == Normal_Last_M73_Output_Placeholder_Tag)) ||
+ ((_mode == Silent) && (gcode_line == Silent_Last_M73_Output_Placeholder_Tag)))
+ {
+ sprintf(time_line, time_mask.c_str(), "100", "0");
+ gcode_line = time_line;
+ }
else
gcode_line += "\n";
+
// add remaining time lines where needed
_parser.parse_line(gcode_line,
[this, &it_line_id, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line)
diff --git a/src/libslic3r/GCodeTimeEstimator.hpp b/src/libslic3r/GCodeTimeEstimator.hpp
index ef91d5ff1d..1fbc1c14bf 100644
--- a/src/libslic3r/GCodeTimeEstimator.hpp
+++ b/src/libslic3r/GCodeTimeEstimator.hpp
@@ -19,6 +19,8 @@ namespace Slic3r {
public:
static const std::string Normal_First_M73_Output_Placeholder_Tag;
static const std::string Silent_First_M73_Output_Placeholder_Tag;
+ static const std::string Normal_Last_M73_Output_Placeholder_Tag;
+ static const std::string Silent_Last_M73_Output_Placeholder_Tag;
enum EMode : unsigned char
{
diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp
index 8f3423a3db..a16b754a4c 100644
--- a/src/libslic3r/Geometry.cpp
+++ b/src/libslic3r/Geometry.cpp
@@ -1455,4 +1455,28 @@ Transformation Transformation::operator * (const Transformation& other) const
return Transformation(get_matrix() * other.get_matrix());
}
+Eigen::Quaterniond rotation_xyz_diff(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to)
+{
+ return
+ // From the current coordinate system to world.
+ Eigen::AngleAxisd(rot_xyz_to(2), Vec3d::UnitZ()) * Eigen::AngleAxisd(rot_xyz_to(1), Vec3d::UnitY()) * Eigen::AngleAxisd(rot_xyz_to(0), Vec3d::UnitX()) *
+ // From world to the initial coordinate system.
+ Eigen::AngleAxisd(-rot_xyz_from(0), Vec3d::UnitX()) * Eigen::AngleAxisd(-rot_xyz_from(1), Vec3d::UnitY()) * Eigen::AngleAxisd(-rot_xyz_from(2), Vec3d::UnitZ());
+}
+
+// This should only be called if it is known, that the two rotations only differ in rotation around the Z axis.
+double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to)
+{
+ Eigen::AngleAxisd angle_axis(rotation_xyz_diff(rot_xyz_from, rot_xyz_to));
+ Vec3d axis = angle_axis.axis();
+ double angle = angle_axis.angle();
+#ifndef NDEBUG
+ if (std::abs(angle) > 1e-8) {
+ assert(std::abs(axis.x()) < 1e-8);
+ assert(std::abs(axis.y()) < 1e-8);
+ }
+#endif /* NDEBUG */
+ return (axis.z() < 0) ? -angle : angle;
+}
+
} }
diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp
index d556f664c8..25b849d8ce 100644
--- a/src/libslic3r/Geometry.hpp
+++ b/src/libslic3r/Geometry.hpp
@@ -262,6 +262,13 @@ public:
Transformation operator * (const Transformation& other) const;
};
+// Rotation when going from the first coordinate system with rotation rot_xyz_from applied
+// to a coordinate system with rot_xyz_to applied.
+extern Eigen::Quaterniond rotation_xyz_diff(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to);
+// Rotation by Z to align rot_xyz_from to rot_xyz_to.
+// This should only be called if it is known, that the two rotations only differ in rotation around the Z axis.
+extern double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to);
+
} }
#endif
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index ba898d9d52..e634dd1383 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -556,19 +556,9 @@ std::string Model::propose_export_file_name_and_path() const
for (const ModelObject *model_object : this->objects)
for (ModelInstance *model_instance : model_object->instances)
if (model_instance->is_printable()) {
- input_file = model_object->input_file;
- if (! model_object->name.empty()) {
- if (input_file.empty())
- // model_object->input_file was empty, just use model_object->name
- input_file = model_object->name;
- else {
- // Replace file name in input_file with model_object->name, but keep the path and file extension.
- input_file = (boost::filesystem::path(model_object->name).parent_path().empty()) ?
- (boost::filesystem::path(input_file).parent_path() / model_object->name).make_preferred().string() :
- model_object->name;
- }
- }
- if (! input_file.empty())
+ input_file = model_object->get_export_filename();
+
+ if (!input_file.empty())
goto end;
// Other instances will produce the same name, skip them.
break;
@@ -1433,6 +1423,26 @@ void ModelObject::print_info() const
cout << "volume = " << mesh.volume() << endl;
}
+std::string ModelObject::get_export_filename() const
+{
+ std::string ret = input_file;
+
+ if (!name.empty())
+ {
+ if (ret.empty())
+ // input_file was empty, just use name
+ ret = name;
+ else
+ {
+ // Replace file name in input_file with name, but keep the path and file extension.
+ ret = (boost::filesystem::path(name).parent_path().empty()) ?
+ (boost::filesystem::path(ret).parent_path() / name).make_preferred().string() : name;
+ }
+ }
+
+ return ret;
+}
+
void ModelVolume::set_material_id(t_model_material_id material_id)
{
m_material_id = material_id;
diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp
index 5cf7f49cad..9514012434 100644
--- a/src/libslic3r/Model.hpp
+++ b/src/libslic3r/Model.hpp
@@ -275,6 +275,8 @@ public:
// Print object statistics to console.
void print_info() const;
+ std::string get_export_filename() const;
+
protected:
friend class Print;
friend class SLAPrint;
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index 7943901339..c13f0bc2a3 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -10,7 +10,7 @@
#include "GCode/WipeTowerPrusaMM.hpp"
#include "Utils.hpp"
-#include "PrintExport.hpp"
+//#include "PrintExport.hpp"
#include
#include
diff --git a/src/libslic3r/PrintExport.hpp b/src/libslic3r/PrintExport.hpp
index 04b993a529..ce62f7cb0f 100644
--- a/src/libslic3r/PrintExport.hpp
+++ b/src/libslic3r/PrintExport.hpp
@@ -7,6 +7,7 @@
#include
#include
+#include
#include "Rasterizer/Rasterizer.hpp"
//#include
@@ -72,7 +73,8 @@ public:
void finish_layer();
// Save all the layers into the file (or dir) specified in the path argument
- void save(const std::string& path);
+ // An optional project name can be added to be used for the layer file names
+ void save(const std::string& path, const std::string& projectname = "");
// Save only the selected layer to the file specified in path argument.
void save_layer(unsigned lyr, const std::string& path);
@@ -86,7 +88,8 @@ template struct VeryFalse { static const bool value = false; };
template class LayerWriter {
public:
- LayerWriter(const std::string& /*zipfile_path*/) {
+ LayerWriter(const std::string& /*zipfile_path*/)
+ {
static_assert(VeryFalse::value,
"No layer writer implementation provided!");
}
@@ -99,10 +102,6 @@ public:
void binary_entry(const std::string& /*fname*/,
const std::uint8_t* buf, size_t len);
- // Get the name of the archive but only the name part without the path or
- // the extension.
- std::string get_name() { return ""; }
-
// Test whether the object can still be used for writing.
bool is_ok() { return false; }
@@ -253,12 +252,14 @@ public:
}
template
- inline void save(const std::string& path) {
+ inline void save(const std::string& fpath, const std::string& prjname = "")
+ {
try {
- LayerWriter writer(path);
+ LayerWriter writer(fpath);
if(!writer.is_ok()) return;
- std::string project = writer.get_name();
+ std::string project = prjname.empty()?
+ boost::filesystem::path(fpath).stem().string() : prjname;
writer.next_entry("config.ini");
if(!writer.is_ok()) return;
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index 42c6fbf754..954e583f7c 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -1790,8 +1790,13 @@ std::vector PrintObject::_slice_volumes(const std::vector &z,
if (! volumes.empty()) {
// Compose mesh.
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
- TriangleMesh mesh(volumes.front()->mesh);
+ TriangleMesh mesh(volumes.front()->mesh);
mesh.transform(volumes.front()->get_matrix(), true);
+ assert(mesh.repaired);
+ if (volumes.size() == 1 && mesh.repaired) {
+ //FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
+ stl_check_facets_exact(&mesh.stl);
+ }
for (size_t idx_volume = 1; idx_volume < volumes.size(); ++ idx_volume) {
const ModelVolume &model_volume = *volumes[idx_volume];
TriangleMesh vol_mesh(model_volume.mesh);
@@ -1821,6 +1826,10 @@ std::vector PrintObject::_slice_volume(const std::vector &z,
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
TriangleMesh mesh(volume.mesh);
mesh.transform(volume.get_matrix(), true);
+ if (mesh.repaired) {
+ //FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
+ stl_check_facets_exact(&mesh.stl);
+ }
if (mesh.stl.stats.number_of_facets > 0) {
mesh.transform(m_trafo, true);
// apply XY shift
diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SLAAutoSupports.hpp
index d32d182bc8..3b696212ea 100644
--- a/src/libslic3r/SLA/SLAAutoSupports.hpp
+++ b/src/libslic3r/SLA/SLAAutoSupports.hpp
@@ -19,7 +19,7 @@ public:
float minimal_distance;
float head_diameter;
///////////////
- inline float support_force() const { return 10.f / density_relative; } // a force one point can support (arbitrary force unit)
+ inline float support_force() const { return 7.7f / density_relative; } // a force one point can support (arbitrary force unit)
inline float tear_pressure() const { return 1.f; } // pressure that the display exerts (the force unit per mm2)
};
diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index 9b68df5327..24b571544b 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -3,6 +3,7 @@
#include "SLA/SLABasePool.hpp"
#include "SLA/SLAAutoSupports.hpp"
#include "ClipperUtils.hpp"
+#include "Geometry.hpp"
#include "MTUtils.hpp"
#include
@@ -118,13 +119,18 @@ static Transform3d sla_trafo(const SLAPrint& p, const ModelObject &model_object)
static std::vector sla_instances(const ModelObject &model_object)
{
std::vector instances;
- for (ModelInstance *model_instance : model_object.instances)
- if (model_instance->is_printable()) {
- instances.emplace_back(
- model_instance->id(),
- Point::new_scale(model_instance->get_offset(X), model_instance->get_offset(Y)),
- float(model_instance->get_rotation(Z)));
- }
+ assert(! model_object.instances.empty());
+ if (! model_object.instances.empty()) {
+ Vec3d rotation0 = model_object.instances.front()->get_rotation();
+ rotation0(2) = 0.;
+ for (ModelInstance *model_instance : model_object.instances)
+ if (model_instance->is_printable()) {
+ instances.emplace_back(
+ model_instance->id(),
+ Point::new_scale(model_instance->get_offset(X), model_instance->get_offset(Y)),
+ float(Geometry::rotation_diff_z(rotation0, model_instance->get_rotation())));
+ }
+ }
return instances;
}
@@ -1078,11 +1084,11 @@ void SLAPrint::process()
hole.reserve(h.points.size() + 1);
if(needreverse)
- for(auto& p : h.points)
- hole.emplace_back(p.x(), p.y());
- else
for(auto it = h.points.rbegin(); it != h.points.rend(); ++it)
hole.emplace_back(it->x(), it->y());
+ else
+ for(auto& p : h.points)
+ hole.emplace_back(p.x(), p.y());
}
if(is_lefthanded) {
diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp
index 378e2756a7..69e7e23dc1 100644
--- a/src/libslic3r/SLAPrint.hpp
+++ b/src/libslic3r/SLAPrint.hpp
@@ -320,10 +320,8 @@ struct SLAPrintStatistics
}
};
-struct SLAminzZipper {};
-
// The implementation of creating zipped archives with wxWidgets
-template<> class LayerWriter {
+template<> class LayerWriter {
Zipper m_zip;
public:
@@ -332,16 +330,12 @@ public:
void next_entry(const std::string& fname) { m_zip.add_entry(fname); }
void binary_entry(const std::string& fname,
- const std::uint8_t* buf,
- size_t l)
+ const std::uint8_t* buf,
+ size_t l)
{
m_zip.add_entry(fname, buf, l);
}
- std::string get_name() const {
- return m_zip.get_name();
- }
-
template inline LayerWriter& operator<<(T&& arg) {
m_zip << std::forward(arg); return *this;
}
@@ -389,9 +383,11 @@ public:
// Returns true if the last step was finished with success.
bool finished() const override { return this->is_step_done(slaposSliceSupports) && this->Inherited::is_step_done(slapsRasterize); }
- template
- void export_raster(const std::string& fname) {
- if(m_printer) m_printer->save(fname);
+ template
+ inline void export_raster(const std::string& fpath,
+ const std::string& projectname = "")
+ {
+ if(m_printer) m_printer->save(fpath, projectname);
}
const PrintObjects& objects() const { return m_objects; }
diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp
index 0d9a79978d..bfba364af6 100644
--- a/src/libslic3r/TriangleMesh.cpp
+++ b/src/libslic3r/TriangleMesh.cpp
@@ -273,6 +273,11 @@ void TriangleMesh::translate(float x, float y, float z)
stl_invalidate_shared_vertices(&this->stl);
}
+void TriangleMesh::translate(const Vec3f &displacement)
+{
+ translate(displacement(0), displacement(1), displacement(2));
+}
+
void TriangleMesh::rotate(float angle, const Axis &axis)
{
if (angle == 0.f)
diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp
index 527846f9d9..d389500c69 100644
--- a/src/libslic3r/TriangleMesh.hpp
+++ b/src/libslic3r/TriangleMesh.hpp
@@ -40,6 +40,7 @@ public:
void scale(float factor);
void scale(const Vec3d &versor);
void translate(float x, float y, float z);
+ void translate(const Vec3f &displacement);
void rotate(float angle, const Axis &axis);
void rotate(float angle, const Vec3d& axis);
void rotate_x(float angle) { this->rotate(angle, X); }
diff --git a/src/libslic3r/Zipper.cpp b/src/libslic3r/Zipper.cpp
index 6b7faaddcb..4466f1b045 100644
--- a/src/libslic3r/Zipper.cpp
+++ b/src/libslic3r/Zipper.cpp
@@ -4,7 +4,6 @@
#include "Zipper.hpp"
#include "miniz/miniz_zip.h"
-#include
#include
#include "I18N.hpp"
@@ -213,10 +212,6 @@ void Zipper::finish_entry()
m_entry.clear();
}
-std::string Zipper::get_name() const {
- return boost::filesystem::path(m_impl->m_zipname).stem().string();
-}
-
void Zipper::finalize()
{
finish_entry();
diff --git a/src/libslic3r/Zipper.hpp b/src/libslic3r/Zipper.hpp
index 6566dad426..7d95ffdac7 100644
--- a/src/libslic3r/Zipper.hpp
+++ b/src/libslic3r/Zipper.hpp
@@ -81,9 +81,6 @@ public:
/// file is up to minz after the erroneous write.
void finish_entry();
- /// Gets the name of the archive without the path or extension.
- std::string get_name() const;
-
void finalize();
};
diff --git a/src/slic3r.cpp b/src/slic3r.cpp
index 958b663059..780efea7b3 100644
--- a/src/slic3r.cpp
+++ b/src/slic3r.cpp
@@ -397,8 +397,9 @@ int CLI::run(int argc, char **argv)
outfile_final = fff_print.print_statistics().finalize_output_path(outfile);
} else {
outfile = sla_print.output_filepath(outfile);
- sla_print.export_raster(outfile);
- outfile_final = sla_print.print_statistics().finalize_output_path(outfile);
+ // We need to finalize the filename beforehand because the export function sets the filename inside the zip metadata
+ outfile_final = sla_print.print_statistics().finalize_output_path(outfile);
+ sla_print.export_raster(outfile_final);
}
if (outfile != outfile_final && Slic3r::rename_file(outfile, outfile_final) != 0) {
boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl;
diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index ef60ab3fa9..9d25318136 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -30,6 +30,9 @@ set(SLIC3R_GUI_SOURCES
GUI/GLCanvas3DManager.cpp
GUI/Selection.hpp
GUI/Selection.cpp
+ GUI/Gizmos/GLGizmos.hpp
+ GUI/Gizmos/GLGizmosManager.cpp
+ GUI/Gizmos/GLGizmosManager.hpp
GUI/Gizmos/GLGizmoBase.cpp
GUI/Gizmos/GLGizmoBase.hpp
GUI/Gizmos/GLGizmoMove.cpp
diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp
index 2367938147..05f4fd8fdb 100644
--- a/src/slic3r/GUI/3DBed.cpp
+++ b/src/slic3r/GUI/3DBed.cpp
@@ -495,11 +495,11 @@ void Bed3D::render_prusa(const std::string &key, bool bottom) const
// use anisotropic filter if graphic card allows
GLfloat max_anisotropy = 0.0f;
if (glewIsSupported("GL_EXT_texture_filter_anisotropic"))
- ::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy);
+ glsafe(::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy));
// use higher resolution images if graphic card allows
GLint max_tex_size;
- ::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
+ glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size));
// clamp or the texture generation becomes too slow
max_tex_size = std::min(max_tex_size, 8192);
@@ -621,7 +621,6 @@ void Bed3D::render_prusa_shader(bool transparent) const
m_shader.stop_using();
}
}
-
#else
void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) const
{
@@ -629,7 +628,7 @@ void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) cons
// use higher resolution images if graphic card allows
GLint max_tex_size;
- ::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
+ glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size));
// temporary set to lowest resolution
max_tex_size = 2048;
@@ -644,7 +643,7 @@ void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) cons
// use anisotropic filter if graphic card allows
GLfloat max_anisotropy = 0.0f;
if (glewIsSupported("GL_EXT_texture_filter_anisotropic"))
- ::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy);
+ glsafe(::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy));
std::string filename = tex_path + "_top.png";
if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename))
diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp
index 7ba61bdb69..66d173fc56 100644
--- a/src/slic3r/GUI/3DScene.cpp
+++ b/src/slic3r/GUI/3DScene.cpp
@@ -41,6 +41,7 @@ void glAssertRecentCallImpl(const char *file_name, unsigned int line, const char
switch (err) {
case GL_INVALID_ENUM: sErr = "Invalid Enum"; break;
case GL_INVALID_VALUE: sErr = "Invalid Value"; break;
+ // be aware that GL_INVALID_OPERATION is generated if glGetError is executed between the execution of glBegin and the corresponding execution of glEnd
case GL_INVALID_OPERATION: sErr = "Invalid Operation"; break;
case GL_STACK_OVERFLOW: sErr = "Stack Overflow"; break;
case GL_STACK_UNDERFLOW: sErr = "Stack Underflow"; break;
@@ -98,25 +99,25 @@ void GLIndexedVertexArray::finalize_geometry(bool use_VBOs)
if (use_VBOs) {
if (! empty()) {
- glsafe(glGenBuffers(1, &this->vertices_and_normals_interleaved_VBO_id));
- glsafe(glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id));
- glsafe(glBufferData(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved.size() * 4, this->vertices_and_normals_interleaved.data(), GL_STATIC_DRAW));
- glsafe(glBindBuffer(GL_ARRAY_BUFFER, 0));
+ glsafe(::glGenBuffers(1, &this->vertices_and_normals_interleaved_VBO_id));
+ glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id));
+ glsafe(::glBufferData(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved.size() * 4, this->vertices_and_normals_interleaved.data(), GL_STATIC_DRAW));
+ glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
this->vertices_and_normals_interleaved.clear();
}
if (! this->triangle_indices.empty()) {
- glsafe(glGenBuffers(1, &this->triangle_indices_VBO_id));
- glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id));
- glsafe(glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices.size() * 4, this->triangle_indices.data(), GL_STATIC_DRAW));
+ glsafe(::glGenBuffers(1, &this->triangle_indices_VBO_id));
+ glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id));
+ glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices.size() * 4, this->triangle_indices.data(), GL_STATIC_DRAW));
this->triangle_indices.clear();
}
if (! this->quad_indices.empty()) {
- glsafe(glGenBuffers(1, &this->quad_indices_VBO_id));
- glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id));
- glsafe(glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices.size() * 4, this->quad_indices.data(), GL_STATIC_DRAW));
+ glsafe(::glGenBuffers(1, &this->quad_indices_VBO_id));
+ glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id));
+ glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices.size() * 4, this->quad_indices.data(), GL_STATIC_DRAW));
this->quad_indices.clear();
}
- glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
}
this->shrink_to_fit();
}
@@ -124,15 +125,15 @@ void GLIndexedVertexArray::finalize_geometry(bool use_VBOs)
void GLIndexedVertexArray::release_geometry()
{
if (this->vertices_and_normals_interleaved_VBO_id) {
- glsafe(glDeleteBuffers(1, &this->vertices_and_normals_interleaved_VBO_id));
+ glsafe(::glDeleteBuffers(1, &this->vertices_and_normals_interleaved_VBO_id));
this->vertices_and_normals_interleaved_VBO_id = 0;
}
if (this->triangle_indices_VBO_id) {
- glsafe(glDeleteBuffers(1, &this->triangle_indices_VBO_id));
+ glsafe(::glDeleteBuffers(1, &this->triangle_indices_VBO_id));
this->triangle_indices_VBO_id = 0;
}
if (this->quad_indices_VBO_id) {
- glsafe(glDeleteBuffers(1, &this->quad_indices_VBO_id));
+ glsafe(::glDeleteBuffers(1, &this->quad_indices_VBO_id));
this->quad_indices_VBO_id = 0;
}
this->clear();
@@ -142,42 +143,42 @@ void GLIndexedVertexArray::release_geometry()
void GLIndexedVertexArray::render() const
{
if (this->vertices_and_normals_interleaved_VBO_id) {
- glsafe(glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id));
- glsafe(glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))));
- glsafe(glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr));
+ glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id));
+ glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))));
+ glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr));
} else {
- glsafe(glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), this->vertices_and_normals_interleaved.data() + 3));
- glsafe(glNormalPointer(GL_FLOAT, 6 * sizeof(float), this->vertices_and_normals_interleaved.data()));
+ glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), this->vertices_and_normals_interleaved.data() + 3));
+ glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), this->vertices_and_normals_interleaved.data()));
}
- glsafe(glEnableClientState(GL_VERTEX_ARRAY));
- glsafe(glEnableClientState(GL_NORMAL_ARRAY));
+ glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
+ glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
if (this->indexed()) {
if (this->vertices_and_normals_interleaved_VBO_id) {
// Render using the Vertex Buffer Objects.
if (this->triangle_indices_size > 0) {
- glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id));
- glsafe(glDrawElements(GL_TRIANGLES, GLsizei(this->triangle_indices_size), GL_UNSIGNED_INT, nullptr));
+ glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id));
+ glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(this->triangle_indices_size), GL_UNSIGNED_INT, nullptr));
}
if (this->quad_indices_size > 0) {
- glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id));
- glsafe(glDrawElements(GL_QUADS, GLsizei(this->quad_indices_size), GL_UNSIGNED_INT, nullptr));
+ glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id));
+ glsafe(::glDrawElements(GL_QUADS, GLsizei(this->quad_indices_size), GL_UNSIGNED_INT, nullptr));
}
glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
} else {
// Render in an immediate mode.
if (! this->triangle_indices.empty())
- glsafe(glDrawElements(GL_TRIANGLES, GLsizei(this->triangle_indices_size), GL_UNSIGNED_INT, this->triangle_indices.data()));
+ glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(this->triangle_indices_size), GL_UNSIGNED_INT, this->triangle_indices.data()));
if (! this->quad_indices.empty())
- glsafe(glDrawElements(GL_QUADS, GLsizei(this->quad_indices_size), GL_UNSIGNED_INT, this->quad_indices.data()));
+ glsafe(::glDrawElements(GL_QUADS, GLsizei(this->quad_indices_size), GL_UNSIGNED_INT, this->quad_indices.data()));
}
} else
- glsafe(glDrawArrays(GL_TRIANGLES, 0, GLsizei(this->vertices_and_normals_interleaved_size / 6)));
+ glsafe(::glDrawArrays(GL_TRIANGLES, 0, GLsizei(this->vertices_and_normals_interleaved_size / 6)));
if (this->vertices_and_normals_interleaved_VBO_id)
- glsafe(glBindBuffer(GL_ARRAY_BUFFER, 0));
- glsafe(glDisableClientState(GL_VERTEX_ARRAY));
- glsafe(glDisableClientState(GL_NORMAL_ARRAY));
+ glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
+ glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
+ glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
}
void GLIndexedVertexArray::render(
@@ -190,35 +191,35 @@ void GLIndexedVertexArray::render(
if (this->vertices_and_normals_interleaved_VBO_id) {
// Render using the Vertex Buffer Objects.
- glsafe(glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id));
- glsafe(glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))));
- glsafe(glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr));
- glsafe(glEnableClientState(GL_VERTEX_ARRAY));
- glsafe(glEnableClientState(GL_NORMAL_ARRAY));
+ glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id));
+ glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))));
+ glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr));
+ glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
+ glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
if (this->triangle_indices_size > 0) {
- glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id));
- glsafe(glDrawElements(GL_TRIANGLES, GLsizei(std::min(this->triangle_indices_size, tverts_range.second - tverts_range.first)), GL_UNSIGNED_INT, (const void*)(tverts_range.first * 4)));
+ glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id));
+ glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(std::min(this->triangle_indices_size, tverts_range.second - tverts_range.first)), GL_UNSIGNED_INT, (const void*)(tverts_range.first * 4)));
}
if (this->quad_indices_size > 0) {
- glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id));
- glsafe(glDrawElements(GL_QUADS, GLsizei(std::min(this->quad_indices_size, qverts_range.second - qverts_range.first)), GL_UNSIGNED_INT, (const void*)(qverts_range.first * 4)));
+ glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id));
+ glsafe(::glDrawElements(GL_QUADS, GLsizei(std::min(this->quad_indices_size, qverts_range.second - qverts_range.first)), GL_UNSIGNED_INT, (const void*)(qverts_range.first * 4)));
}
- glsafe(glBindBuffer(GL_ARRAY_BUFFER, 0));
- glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
+ glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
} else {
// Render in an immediate mode.
- glsafe(glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), this->vertices_and_normals_interleaved.data() + 3));
- glsafe(glNormalPointer(GL_FLOAT, 6 * sizeof(float), this->vertices_and_normals_interleaved.data()));
- glsafe(glEnableClientState(GL_VERTEX_ARRAY));
- glsafe(glEnableClientState(GL_NORMAL_ARRAY));
+ glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), this->vertices_and_normals_interleaved.data() + 3));
+ glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), this->vertices_and_normals_interleaved.data()));
+ glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
+ glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
if (! this->triangle_indices.empty())
- glsafe(glDrawElements(GL_TRIANGLES, GLsizei(std::min(this->triangle_indices_size, tverts_range.second - tverts_range.first)), GL_UNSIGNED_INT, (const void*)(this->triangle_indices.data() + tverts_range.first)));
+ glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(std::min(this->triangle_indices_size, tverts_range.second - tverts_range.first)), GL_UNSIGNED_INT, (const void*)(this->triangle_indices.data() + tverts_range.first)));
if (! this->quad_indices.empty())
- glsafe(glDrawElements(GL_QUADS, GLsizei(std::min(this->quad_indices_size, qverts_range.second - qverts_range.first)), GL_UNSIGNED_INT, (const void*)(this->quad_indices.data() + qverts_range.first)));
+ glsafe(::glDrawElements(GL_QUADS, GLsizei(std::min(this->quad_indices_size, qverts_range.second - qverts_range.first)), GL_UNSIGNED_INT, (const void*)(this->quad_indices.data() + qverts_range.first)));
}
- glsafe(glDisableClientState(GL_VERTEX_ARRAY));
- glsafe(glDisableClientState(GL_NORMAL_ARRAY));
+ glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
+ glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
}
const float GLVolume::SELECTED_COLOR[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
@@ -736,7 +737,7 @@ int GLVolumeCollection::load_wipe_tower_preview(
typedef std::pair GLVolumeWithZ;
typedef std::vector GLVolumesWithZList;
-static GLVolumesWithZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, std::function filter_func)
+static GLVolumesWithZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function filter_func)
{
GLVolumesWithZList list;
list.reserve(volumes.size());
@@ -753,12 +754,9 @@ static GLVolumesWithZList volumes_to_render(const GLVolumePtrs& volumes, GLVolum
if ((type == GLVolumeCollection::Transparent) && (list.size() > 1))
{
- Transform3d modelview_matrix;
- glsafe(::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data()));
-
for (GLVolumeWithZ& volume : list)
{
- volume.second = volume.first->bounding_box.transformed(modelview_matrix * volume.first->world_matrix()).max(2);
+ volume.second = volume.first->bounding_box.transformed(view_matrix * volume.first->world_matrix()).max(2);
}
std::sort(list.begin(), list.end(),
@@ -769,7 +767,7 @@ static GLVolumesWithZList volumes_to_render(const GLVolumePtrs& volumes, GLVolum
return list;
}
-void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool disable_cullface, std::function filter_func) const
+void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function filter_func) const
{
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
@@ -783,12 +781,13 @@ void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool
GLint current_program_id;
glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id));
- GLint color_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "uniform_color") : -1;
- GLint z_range_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "z_range") : -1;
- GLint print_box_min_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.min") : -1;
- GLint print_box_max_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.max") : -1;
- GLint print_box_detection_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_detection") : -1;
- GLint print_box_worldmatrix_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_world_matrix") : -1;
+ GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1;
+ GLint z_range_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "z_range") : -1;
+ GLint print_box_min_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.min") : -1;
+ GLint print_box_max_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.max") : -1;
+ GLint print_box_detection_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.volume_detection") : -1;
+ GLint print_box_worldmatrix_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.volume_world_matrix") : -1;
+ glcheck();
if (print_box_min_id != -1)
glsafe(::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min));
@@ -799,7 +798,7 @@ void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool
if (z_range_id != -1)
glsafe(::glUniform2fv(z_range_id, 1, (const GLfloat*)z_range));
- GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, filter_func);
+ GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
for (GLVolumeWithZ& volume : to_render) {
volume.first->set_render_color();
volume.first->render_VBOs(color_id, print_box_detection_id, print_box_worldmatrix_id);
@@ -817,32 +816,32 @@ void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool
glsafe(::glDisable(GL_BLEND));
}
-void GLVolumeCollection::render_legacy(ERenderType type, bool disable_cullface, std::function filter_func) const
+void GLVolumeCollection::render_legacy(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function filter_func) const
{
- glsafe(glEnable(GL_BLEND));
- glsafe(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
+ glsafe(::glEnable(GL_BLEND));
+ glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
- glsafe(glCullFace(GL_BACK));
+ glsafe(::glCullFace(GL_BACK));
if (disable_cullface)
glsafe(::glDisable(GL_CULL_FACE));
- glsafe(glEnableClientState(GL_VERTEX_ARRAY));
- glsafe(glEnableClientState(GL_NORMAL_ARRAY));
+ glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
+ glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
- GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, filter_func);
+ GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
for (GLVolumeWithZ& volume : to_render)
{
volume.first->set_render_color();
volume.first->render_legacy();
}
- glsafe(glDisableClientState(GL_VERTEX_ARRAY));
- glsafe(glDisableClientState(GL_NORMAL_ARRAY));
+ glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
+ glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
if (disable_cullface)
glsafe(::glEnable(GL_CULL_FACE));
- glsafe(glDisable(GL_BLEND));
+ glsafe(::glDisable(GL_BLEND));
}
bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstance::EPrintVolumeState* out_state)
@@ -1771,7 +1770,8 @@ void GLModel::render_VBOs() const
GLint current_program_id;
glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id));
- GLint color_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "uniform_color") : -1;
+ GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1;
+ glcheck();
m_volume.render_VBOs(color_id, -1, -1);
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp
index 5cc301a393..0b32707c93 100644
--- a/src/slic3r/GUI/3DScene.hpp
+++ b/src/slic3r/GUI/3DScene.hpp
@@ -19,9 +19,11 @@
extern void glAssertRecentCallImpl(const char *file_name, unsigned int line, const char *function_name);
inline void glAssertRecentCall() { glAssertRecentCallImpl(__FILE__, __LINE__, __FUNCTION__); }
#define glsafe(cmd) do { cmd; glAssertRecentCallImpl(__FILE__, __LINE__, __FUNCTION__); } while (false)
+#define glcheck() do { glAssertRecentCallImpl(__FILE__, __LINE__, __FUNCTION__); } while (false)
#else
inline void glAssertRecentCall() { }
#define glsafe(cmd) cmd
+#define glcheck()
#endif
namespace Slic3r {
@@ -463,8 +465,8 @@ public:
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width);
// Render the volumes by OpenGL.
- void render_VBOs(ERenderType type, bool disable_cullface, std::function filter_func = std::function()) const;
- void render_legacy(ERenderType type, bool disable_cullface, std::function filter_func = std::function()) const;
+ void render_VBOs(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function filter_func = std::function()) const;
+ void render_legacy(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function filter_func = std::function()) const;
// Finalize the initialization of the geometry & indices,
// upload the geometry and indices to OpenGL VBO objects
diff --git a/src/slic3r/GUI/AboutDialog.cpp b/src/slic3r/GUI/AboutDialog.cpp
index db2e76736e..9825f0cf90 100644
--- a/src/slic3r/GUI/AboutDialog.cpp
+++ b/src/slic3r/GUI/AboutDialog.cpp
@@ -42,9 +42,7 @@ AboutDialog::AboutDialog()
main_sizer->Add(hsizer, 0, wxEXPAND | wxALL, 20);
// logo
-// wxBitmap logo_bmp = wxBitmap(from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG);
-// auto *logo = new wxStaticBitmap(this, wxID_ANY, std::move(logo_bmp));
- auto *logo = new wxStaticBitmap(this, wxID_ANY, create_scaled_bitmap("Slic3r_192px.png"));
+ auto *logo = new wxStaticBitmap(this, wxID_ANY, create_scaled_bitmap("Slic3r_192px.png", 192));
hsizer->Add(logo, 1, wxALIGN_CENTER_VERTICAL);
wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL);
diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
index 2601842ef4..c6a73864d6 100644
--- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp
+++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
@@ -98,8 +98,9 @@ void BackgroundSlicingProcess::process_sla()
m_print->process();
if (this->set_step_started(bspsGCodeFinalize)) {
if (! m_export_path.empty()) {
- m_sla_print->export_raster(m_export_path);
- m_print->set_status(100, "Masked SLA file exported to " + m_export_path);
+ const std::string export_path = m_sla_print->print_statistics().finalize_output_path(m_export_path);
+ m_sla_print->export_raster(export_path);
+ m_print->set_status(100, "Masked SLA file exported to " + export_path);
} else if (! m_upload_job.empty()) {
prepare_upload();
} else {
@@ -389,7 +390,7 @@ void BackgroundSlicingProcess::prepare_upload()
// Generate a unique temp path to which the gcode/zip file is copied/exported
boost::filesystem::path source_path = boost::filesystem::temp_directory_path()
- / boost::filesystem::unique_path(".printhost.%%%%-%%%%-%%%%-%%%%.gcode");
+ / boost::filesystem::unique_path(".Slic3rPE.upload.%%%%-%%%%-%%%%-%%%%");
if (m_print == m_fff_print) {
m_print->set_status(95, "Running post-processing scripts");
@@ -399,8 +400,8 @@ void BackgroundSlicingProcess::prepare_upload()
run_post_process_scripts(source_path.string(), m_fff_print->config());
m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string());
} else {
- m_sla_print->export_raster(source_path.string());
- // TODO: Also finalize upload path like with FFF when there are statistics for SLA print
+ m_upload_job.upload_data.upload_path = m_sla_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string());
+ m_sla_print->export_raster(source_path.string(), m_upload_job.upload_data.upload_path.string());
}
m_print->set_status(100, (boost::format("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue") % m_upload_job.printhost->get_host()).str());
diff --git a/src/slic3r/GUI/BitmapCache.cpp b/src/slic3r/GUI/BitmapCache.cpp
index 16baa1629e..7ad36f09bb 100644
--- a/src/slic3r/GUI/BitmapCache.cpp
+++ b/src/slic3r/GUI/BitmapCache.cpp
@@ -1,5 +1,7 @@
#include "BitmapCache.hpp"
+#include "libslic3r/Utils.hpp"
+
#if ! defined(WIN32) && ! defined(__APPLE__)
#define BROKEN_ALPHA
#endif
@@ -9,6 +11,12 @@
#include
#endif /* BROKEN_ALPHA */
+#define NANOSVG_IMPLEMENTATION
+#include "nanosvg/nanosvg.h"
+#define NANOSVGRAST_IMPLEMENTATION
+#include "nanosvg/nanosvgrast.h"
+#include "GUI_App.hpp"
+
namespace Slic3r { namespace GUI {
void BitmapCache::clear()
@@ -155,8 +163,91 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *beg
#endif
}
+wxBitmap* BitmapCache::insert_raw_rgba(const std::string &bitmap_key, unsigned int width, unsigned int height, const unsigned char *raw_data)
+{
+ wxImage image(width, height);
+ image.InitAlpha();
+ unsigned char *rgb = image.GetData();
+ unsigned char *alpha = image.GetAlpha();
+ unsigned int pixels = width * height;
+ for (unsigned int i = 0; i < pixels; ++ i) {
+ *rgb ++ = *raw_data ++;
+ *rgb ++ = *raw_data ++;
+ *rgb ++ = *raw_data ++;
+ *alpha ++ = *raw_data ++;
+ }
+ return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image)));
+}
+
+wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned int width, unsigned int height)
+{
+ std::string bitmap_key = bitmap_name + ( height !=0 ?
+ "-h" + std::to_string(height) :
+ "-w" + std::to_string(width));
+ auto it = m_map.find(bitmap_key);
+ if (it != m_map.end())
+ return it->second;
+
+ wxImage image;
+ if (! image.LoadFile(Slic3r::GUI::from_u8(Slic3r::var(bitmap_name + ".png")), wxBITMAP_TYPE_PNG) ||
+ image.GetWidth() == 0 || image.GetHeight() == 0)
+ return nullptr;
+
+ if (height != 0 && image.GetHeight() != height)
+ width = int(0.5f + float(image.GetWidth()) * height / image.GetHeight());
+ else if (width != 0 && image.GetWidth() != width)
+ height = int(0.5f + float(image.GetHeight()) * width / image.GetWidth());
+
+ if (height != 0 && width != 0)
+ image.Rescale(width, height, wxIMAGE_QUALITY_BILINEAR);
+
+ return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image)));
+}
+
+wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned int target_width, unsigned int target_height)
+{
+ std::string bitmap_key = bitmap_name + (target_height != 0 ?
+ "-h" + std::to_string(target_height) :
+ "-w" + std::to_string(target_width));
+ auto it = m_map.find(bitmap_key);
+ if (it != m_map.end())
+ return it->second;
+
+ NSVGimage *image = ::nsvgParseFromFile(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f);
+ if (image == nullptr)
+ return nullptr;
+
+ float scale = target_height != 0 ?
+ (float)target_height / image->height : target_width != 0 ?
+ (float)target_width / image->width : 1;
+
+ int width = (int)(scale * image->width + 0.5f);
+ int height = (int)(scale * image->height + 0.5f);
+ int n_pixels = width * height;
+ if (n_pixels <= 0) {
+ ::nsvgDelete(image);
+ return nullptr;
+ }
+
+ NSVGrasterizer *rast = ::nsvgCreateRasterizer();
+ if (rast == nullptr) {
+ ::nsvgDelete(image);
+ return nullptr;
+ }
+
+ std::vector data(n_pixels * 4, 0);
+ ::nsvgRasterize(rast, image, 0, 0, scale, data.data(), width, height, width * 4);
+ ::nsvgDeleteRasterizer(rast);
+ ::nsvgDelete(image);
+
+ return this->insert_raw_rgba(bitmap_key, width, height, data.data());
+}
+
wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency)
{
+ width = width * 0.1f * Slic3r::GUI::wxGetApp().em_unit() + 0.5f;
+ height = height * 0.1f * Slic3r::GUI::wxGetApp().em_unit() + 0.5f;
+
wxImage image(width, height);
image.InitAlpha();
unsigned char* imgdata = image.GetData();
diff --git a/src/slic3r/GUI/BitmapCache.hpp b/src/slic3r/GUI/BitmapCache.hpp
index 0cb70d28b9..8915783a34 100644
--- a/src/slic3r/GUI/BitmapCache.hpp
+++ b/src/slic3r/GUI/BitmapCache.hpp
@@ -29,6 +29,12 @@ public:
wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3);
wxBitmap* insert(const std::string &name, const std::vector &bmps) { return this->insert(name, &bmps.front(), &bmps.front() + bmps.size()); }
wxBitmap* insert(const std::string &name, const wxBitmap *begin, const wxBitmap *end);
+ wxBitmap* insert_raw_rgba(const std::string &bitmap_key, unsigned int width, unsigned int height, const unsigned char *raw_data);
+
+ // Load png from resources/icons. bitmap_key is given without the .png suffix. Bitmap will be rescaled to provided height/width if nonzero.
+ wxBitmap* load_png(const std::string &bitmap_key, unsigned int width = 0, unsigned int height = 0);
+ // Load svg from resources/icons. bitmap_key is given without the .svg suffix. SVG will be rasterized to provided height/width.
+ wxBitmap* load_svg(const std::string &bitmap_key, unsigned int width = 0, unsigned int height = 0);
static wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency);
static wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3]) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE); }
diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp
index c748efa64d..7a1023e621 100644
--- a/src/slic3r/GUI/Camera.cpp
+++ b/src/slic3r/GUI/Camera.cpp
@@ -1,9 +1,21 @@
#include "libslic3r/libslic3r.h"
#include "Camera.hpp"
+#include "3DScene.hpp"
+
+#include
static const float GIMBALL_LOCK_THETA_MAX = 180.0f;
+// phi / theta angles to orient the camera.
+static const float VIEW_DEFAULT[2] = { 45.0f, 45.0f };
+static const float VIEW_LEFT[2] = { 90.0f, 90.0f };
+static const float VIEW_RIGHT[2] = { -90.0f, 90.0f };
+static const float VIEW_TOP[2] = { 0.0f, 0.0f };
+static const float VIEW_BOTTOM[2] = { 0.0f, 180.0f };
+static const float VIEW_FRONT[2] = { 0.0f, 90.0f };
+static const float VIEW_REAR[2] = { 180.0f, 90.0f };
+
namespace Slic3r {
namespace GUI {
@@ -57,6 +69,64 @@ void Camera::set_scene_box(const BoundingBoxf3& box)
m_scene_box = box;
}
+bool Camera::select_view(const std::string& direction)
+{
+ const float* dir_vec = nullptr;
+
+ if (direction == "iso")
+ dir_vec = VIEW_DEFAULT;
+ else if (direction == "left")
+ dir_vec = VIEW_LEFT;
+ else if (direction == "right")
+ dir_vec = VIEW_RIGHT;
+ else if (direction == "top")
+ dir_vec = VIEW_TOP;
+ else if (direction == "bottom")
+ dir_vec = VIEW_BOTTOM;
+ else if (direction == "front")
+ dir_vec = VIEW_FRONT;
+ else if (direction == "rear")
+ dir_vec = VIEW_REAR;
+
+ if (dir_vec != nullptr)
+ {
+ phi = dir_vec[0];
+ set_theta(dir_vec[1], false);
+ return true;
+ }
+ else
+ return false;
+}
+
+void Camera::apply_viewport(int x, int y, unsigned int w, unsigned int h) const
+{
+ glsafe(::glViewport(0, 0, w, h));
+ glsafe(::glGetIntegerv(GL_VIEWPORT, m_viewport.data()));
+}
+
+void Camera::apply_view_matrix() const
+{
+ glsafe(::glMatrixMode(GL_MODELVIEW));
+ glsafe(::glLoadIdentity());
+
+ glsafe(::glRotatef(-m_theta, 1.0f, 0.0f, 0.0f)); // pitch
+ glsafe(::glRotatef(phi, 0.0f, 0.0f, 1.0f)); // yaw
+ glsafe(::glTranslated(-m_target(0), -m_target(1), -m_target(2)));
+
+ glsafe(::glGetDoublev(GL_MODELVIEW_MATRIX, m_view_matrix.data()));
+}
+
+void Camera::apply_ortho_projection(float x_min, float x_max, float y_min, float y_max, float z_min, float z_max) const
+{
+ glsafe(::glMatrixMode(GL_PROJECTION));
+ glsafe(::glLoadIdentity());
+
+ glsafe(::glOrtho(x_min, x_max, y_min, y_max, z_min, z_max));
+ glsafe(::glGetDoublev(GL_PROJECTION_MATRIX, m_projection_matrix.data()));
+
+ glsafe(::glMatrixMode(GL_MODELVIEW));
+}
+
} // GUI
} // Slic3r
diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp
index d50dc6e4d9..d19bc870ef 100644
--- a/src/slic3r/GUI/Camera.hpp
+++ b/src/slic3r/GUI/Camera.hpp
@@ -2,6 +2,7 @@
#define slic3r_Camera_hpp_
#include "libslic3r/BoundingBox.hpp"
+#include
namespace Slic3r {
namespace GUI {
@@ -26,6 +27,10 @@ private:
Vec3d m_target;
float m_theta;
+ mutable std::array m_viewport;
+ mutable Transform3d m_view_matrix;
+ mutable Transform3d m_projection_matrix;
+
BoundingBoxf3 m_scene_box;
public:
@@ -41,6 +46,22 @@ public:
const BoundingBoxf3& get_scene_box() const { return m_scene_box; }
void set_scene_box(const BoundingBoxf3& box);
+
+ bool select_view(const std::string& direction);
+
+ const std::array& get_viewport() const { return m_viewport; }
+ const Transform3d& get_view_matrix() const { return m_view_matrix; }
+ const Transform3d& get_projection_matrix() const { return m_projection_matrix; }
+
+ Vec3d get_dir_right() const { return m_view_matrix.matrix().block(0, 0, 3, 3).row(0); }
+ Vec3d get_dir_up() const { return m_view_matrix.matrix().block(0, 0, 3, 3).row(1); }
+ Vec3d get_dir_forward() const { return m_view_matrix.matrix().block(0, 0, 3, 3).row(2); }
+
+ Vec3d get_position() const { return m_view_matrix.matrix().block(0, 0, 3, 3).row(3); }
+
+ void apply_viewport(int x, int y, unsigned int w, unsigned int h) const;
+ void apply_view_matrix() const;
+ void apply_ortho_projection(float x_min, float x_max, float y_min, float y_max, float z_min, float z_max) const;
};
} // GUI
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 79a3684ef5..3f52a0b9fd 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -56,15 +56,6 @@
static const float TRACKBALLSIZE = 0.8f;
static const float GROUND_Z = -0.02f;
-// phi / theta angles to orient the camera.
-static const float VIEW_DEFAULT[2] = { 45.0f, 45.0f };
-static const float VIEW_LEFT[2] = { 90.0f, 90.0f };
-static const float VIEW_RIGHT[2] = { -90.0f, 90.0f };
-static const float VIEW_TOP[2] = { 0.0f, 0.0f };
-static const float VIEW_BOTTOM[2] = { 0.0f, 180.0f };
-static const float VIEW_FRONT[2] = { 0.0f, 90.0f };
-static const float VIEW_REAR[2] = { 180.0f, 90.0f };
-
static const float GIZMO_RESET_BUTTON_HEIGHT = 22.0f;
static const float GIZMO_RESET_BUTTON_WIDTH = 70.f;
@@ -125,62 +116,6 @@ void Size::set_scale_factor(int scale_factor)
m_scale_factor = scale_factor;
}
-Rect::Rect()
- : m_left(0.0f)
- , m_top(0.0f)
- , m_right(0.0f)
- , m_bottom(0.0f)
-{
-}
-
-Rect::Rect(float left, float top, float right, float bottom)
- : m_left(left)
- , m_top(top)
- , m_right(right)
- , m_bottom(bottom)
-{
-}
-
-float Rect::get_left() const
-{
- return m_left;
-}
-
-void Rect::set_left(float left)
-{
- m_left = left;
-}
-
-float Rect::get_top() const
-{
- return m_top;
-}
-
-void Rect::set_top(float top)
-{
- m_top = top;
-}
-
-float Rect::get_right() const
-{
- return m_right;
-}
-
-void Rect::set_right(float right)
-{
- m_right = right;
-}
-
-float Rect::get_bottom() const
-{
- return m_bottom;
-}
-
-void Rect::set_bottom(float bottom)
-{
- m_bottom = bottom;
-}
-
#if !ENABLE_TEXTURES_FROM_SVG
GLCanvas3D::Shader::Shader()
: m_shader(nullptr)
@@ -283,7 +218,7 @@ GLCanvas3D::LayersEditing::~LayersEditing()
{
if (m_z_texture_id != 0)
{
- ::glDeleteTextures(1, &m_z_texture_id);
+ glsafe(::glDeleteTextures(1, &m_z_texture_id));
m_z_texture_id = 0;
}
delete m_slicing_parameters;
@@ -297,14 +232,14 @@ bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename,
if (!m_shader.init(vertex_shader_filename, fragment_shader_filename))
return false;
- ::glGenTextures(1, (GLuint*)&m_z_texture_id);
- ::glBindTexture(GL_TEXTURE_2D, m_z_texture_id);
- ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
- ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
- ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
- ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
- ::glBindTexture(GL_TEXTURE_2D, 0);
+ glsafe(::glGenTextures(1, (GLuint*)&m_z_texture_id));
+ glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id));
+ glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP));
+ glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP));
+ glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+ glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST));
+ glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1));
+ glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
return true;
}
@@ -360,12 +295,12 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
const Rect& bar_rect = get_bar_rect_viewport(canvas);
const Rect& reset_rect = get_reset_rect_viewport(canvas);
- ::glDisable(GL_DEPTH_TEST);
+ glsafe(::glDisable(GL_DEPTH_TEST));
// The viewport and camera are set to complete view and glOrtho(-$x / 2, $x / 2, -$y / 2, $y / 2, -$depth, $depth),
// where x, y is the window size divided by $self->_zoom.
- ::glPushMatrix();
- ::glLoadIdentity();
+ glsafe(::glPushMatrix());
+ glsafe(::glLoadIdentity());
_render_tooltip_texture(canvas, bar_rect, reset_rect);
_render_reset_texture(reset_rect);
@@ -373,9 +308,9 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
_render_profile(bar_rect);
// Revert the matrices.
- ::glPopMatrix();
+ glsafe(::glPopMatrix());
- ::glEnable(GL_DEPTH_TEST);
+ glsafe(::glEnable(GL_DEPTH_TEST));
}
float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas)
@@ -430,7 +365,7 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas)
float half_w = 0.5f * (float)cnv_size.get_width();
float half_h = 0.5f * (float)cnv_size.get_height();
- float zoom = canvas.get_camera_zoom();
+ float zoom = canvas.get_camera().zoom;
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom);
@@ -442,7 +377,7 @@ Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas
float half_w = 0.5f * (float)cnv_size.get_width();
float half_h = 0.5f * (float)cnv_size.get_height();
- float zoom = canvas.get_camera_zoom();
+ float zoom = canvas.get_camera().zoom;
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom);
@@ -473,7 +408,7 @@ void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas
const float width = (float)m_tooltip_texture.get_width() * scale;
const float height = (float)m_tooltip_texture.get_height() * scale;
- float zoom = canvas.get_camera_zoom();
+ float zoom = canvas.get_camera().zoom;
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float gap = 10.0f * inv_zoom;
@@ -511,8 +446,8 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas
// The shader requires the original model coordinates when rendering to the texture, so we pass it the unit matrix
m_shader.set_uniform("volume_world_matrix", UNIT_MATRIX);
- ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- ::glBindTexture(GL_TEXTURE_2D, m_z_texture_id);
+ glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
+ glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id));
// Render the color bar
float l = bar_rect.get_left();
@@ -526,8 +461,8 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas
::glVertex3f(r, b, 0.0f);
::glVertex3f(r, t, m_object_max_z);
::glVertex3f(l, t, m_object_max_z);
- ::glEnd();
- ::glBindTexture(GL_TEXTURE_2D, 0);
+ glsafe(::glEnd());
+ glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
m_shader.stop_using();
}
@@ -543,18 +478,18 @@ void GLCanvas3D::LayersEditing::_render_profile(const Rect& bar_rect) const
float x = bar_rect.get_left() + (float)m_slicing_parameters->layer_height * scale_x;
// Baseline
- ::glColor3f(0.0f, 0.0f, 0.0f);
+ glsafe(::glColor3f(0.0f, 0.0f, 0.0f));
::glBegin(GL_LINE_STRIP);
::glVertex2f(x, bar_rect.get_bottom());
::glVertex2f(x, bar_rect.get_top());
- ::glEnd();
+ glsafe(::glEnd());
// Curve
- ::glColor3f(0.0f, 0.0f, 1.0f);
+ glsafe(::glColor3f(0.0f, 0.0f, 1.0f));
::glBegin(GL_LINE_STRIP);
for (unsigned int i = 0; i < m_layer_height_profile.size(); i += 2)
::glVertex2f(bar_rect.get_left() + (float)m_layer_height_profile[i + 1] * scale_x, bar_rect.get_bottom() + (float)m_layer_height_profile[i] * scale_y);
- ::glEnd();
+ glsafe(::glEnd());
}
void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const GLVolumeCollection &volumes) const
@@ -565,51 +500,52 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G
assert(shader_id > 0);
GLint current_program_id;
- glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id);
+ glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id));
if (shader_id > 0 && shader_id != current_program_id)
// The layer editing shader is not yet active. Activate it.
- glUseProgram(shader_id);
+ glsafe(::glUseProgram(shader_id));
else
// The layer editing shader was already active.
current_program_id = -1;
- GLint z_to_texture_row_id = glGetUniformLocation(shader_id, "z_to_texture_row");
- GLint z_texture_row_to_normalized_id = glGetUniformLocation(shader_id, "z_texture_row_to_normalized");
- GLint z_cursor_id = glGetUniformLocation(shader_id, "z_cursor");
- GLint z_cursor_band_width_id = glGetUniformLocation(shader_id, "z_cursor_band_width");
- GLint world_matrix_id = glGetUniformLocation(shader_id, "volume_world_matrix");
+ GLint z_to_texture_row_id = ::glGetUniformLocation(shader_id, "z_to_texture_row");
+ GLint z_texture_row_to_normalized_id = ::glGetUniformLocation(shader_id, "z_texture_row_to_normalized");
+ GLint z_cursor_id = ::glGetUniformLocation(shader_id, "z_cursor");
+ GLint z_cursor_band_width_id = ::glGetUniformLocation(shader_id, "z_cursor_band_width");
+ GLint world_matrix_id = ::glGetUniformLocation(shader_id, "volume_world_matrix");
+ glcheck();
if (z_to_texture_row_id != -1 && z_texture_row_to_normalized_id != -1 && z_cursor_id != -1 && z_cursor_band_width_id != -1 && world_matrix_id != -1)
{
const_cast(this)->generate_layer_height_texture();
// Uniforms were resolved, go ahead using the layer editing shader.
- glUniform1f(z_to_texture_row_id, GLfloat(m_layers_texture.cells - 1) / (GLfloat(m_layers_texture.width) * GLfloat(m_object_max_z)));
- glUniform1f(z_texture_row_to_normalized_id, GLfloat(1.0f / m_layers_texture.height));
- glUniform1f(z_cursor_id, GLfloat(m_object_max_z) * GLfloat(this->get_cursor_z_relative(canvas)));
- glUniform1f(z_cursor_band_width_id, GLfloat(this->band_width));
+ glsafe(::glUniform1f(z_to_texture_row_id, GLfloat(m_layers_texture.cells - 1) / (GLfloat(m_layers_texture.width) * GLfloat(m_object_max_z))));
+ glsafe(::glUniform1f(z_texture_row_to_normalized_id, GLfloat(1.0f / m_layers_texture.height)));
+ glsafe(::glUniform1f(z_cursor_id, GLfloat(m_object_max_z) * GLfloat(this->get_cursor_z_relative(canvas))));
+ glsafe(::glUniform1f(z_cursor_band_width_id, GLfloat(this->band_width)));
// Initialize the layer height texture mapping.
GLsizei w = (GLsizei)m_layers_texture.width;
GLsizei h = (GLsizei)m_layers_texture.height;
GLsizei half_w = w / 2;
GLsizei half_h = h / 2;
- ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- glBindTexture(GL_TEXTURE_2D, m_z_texture_id);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
- glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data());
- glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data() + m_layers_texture.width * m_layers_texture.height * 4);
+ glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
+ glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id));
+ glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
+ glsafe(::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
+ glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data()));
+ glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data() + m_layers_texture.width * m_layers_texture.height * 4));
for (const GLVolume *glvolume : volumes.volumes) {
// Render the object using the layer editing shader and texture.
if (! glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier)
continue;
- ::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)glvolume->world_matrix().cast().data());
+ glsafe(::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)glvolume->world_matrix().cast().data()));
glvolume->render();
}
// Revert back to the previous shader.
glBindTexture(GL_TEXTURE_2D, 0);
if (current_program_id > 0)
- glUseProgram(current_program_id);
+ glsafe(::glUseProgram(current_program_id));
}
else
{
@@ -619,7 +555,7 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G
// Render the object using the layer editing shader and texture.
if (!glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier)
continue;
- ::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)glvolume->world_matrix().cast().data());
+ glsafe(::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)glvolume->world_matrix().cast().data()));
glvolume->render();
}
}
@@ -731,885 +667,9 @@ GLCanvas3D::Mouse::Mouse()
: dragging(false)
, position(DBL_MAX, DBL_MAX)
, scene_position(DBL_MAX, DBL_MAX, DBL_MAX)
- , ignore_up_event(false)
{
}
-#if ENABLE_SVG_ICONS
-const float GLCanvas3D::Gizmos::Default_Icons_Size = 64;
-#endif // ENABLE_SVG_ICONS
-
-GLCanvas3D::Gizmos::Gizmos()
- : m_enabled(false)
-#if ENABLE_SVG_ICONS
- , m_icons_texture_dirty(true)
-#endif // ENABLE_SVG_ICONS
- , m_current(Undefined)
-#if ENABLE_SVG_ICONS
- , m_overlay_icons_size(Default_Icons_Size)
- , m_overlay_scale(1.0f)
- , m_overlay_border(5.0f)
- , m_overlay_gap_y(5.0f)
-{
-}
-#else
-{
- set_overlay_scale(1.0);
-}
-#endif // ENABLE_SVG_ICONS
-
-GLCanvas3D::Gizmos::~Gizmos()
-{
- reset();
-}
-
-bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent)
-{
-#if !ENABLE_SVG_ICONS
- m_icons_texture.metadata.filename = "gizmos.png";
- m_icons_texture.metadata.icon_size = 64;
-
- if (!m_icons_texture.metadata.filename.empty())
- {
- if (!m_icons_texture.texture.load_from_file(resources_dir() + "/icons/" + m_icons_texture.metadata.filename, false))
- {
- reset();
- return false;
- }
- }
-#endif // !ENABLE_SVG_ICONS
-
- m_background_texture.metadata.filename = "toolbar_background.png";
- m_background_texture.metadata.left = 16;
- m_background_texture.metadata.top = 16;
- m_background_texture.metadata.right = 16;
- m_background_texture.metadata.bottom = 16;
-
- if (!m_background_texture.metadata.filename.empty())
- {
- if (!m_background_texture.texture.load_from_file(resources_dir() + "/icons/" + m_background_texture.metadata.filename, false))
- {
- reset();
- return false;
- }
- }
-
-#if ENABLE_SVG_ICONS
- GLGizmoBase* gizmo = new GLGizmoMove3D(parent, "move.svg", 0);
-#else
- GLGizmoBase* gizmo = new GLGizmoMove3D(parent, 0);
-#endif // ENABLE_SVG_ICONS
- if (gizmo == nullptr)
- return false;
-
- if (!gizmo->init())
- return false;
-
- m_gizmos.insert(GizmosMap::value_type(Move, gizmo));
-
-#if ENABLE_SVG_ICONS
- gizmo = new GLGizmoScale3D(parent, "scale.svg", 1);
-#else
- gizmo = new GLGizmoScale3D(parent, 1);
-#endif // ENABLE_SVG_ICONS
- if (gizmo == nullptr)
- return false;
-
- if (!gizmo->init())
- return false;
-
- m_gizmos.insert(GizmosMap::value_type(Scale, gizmo));
-
-#if ENABLE_SVG_ICONS
- gizmo = new GLGizmoRotate3D(parent, "rotate.svg", 2);
-#else
- gizmo = new GLGizmoRotate3D(parent, 2);
-#endif // ENABLE_SVG_ICONS
- if (gizmo == nullptr)
- {
- reset();
- return false;
- }
-
- if (!gizmo->init())
- {
- reset();
- return false;
- }
-
- m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo));
-
-#if ENABLE_SVG_ICONS
- gizmo = new GLGizmoFlatten(parent, "place.svg", 3);
-#else
- gizmo = new GLGizmoFlatten(parent, 3);
-#endif // ENABLE_SVG_ICONS
- if (gizmo == nullptr)
- return false;
-
- if (!gizmo->init()) {
- reset();
- return false;
- }
-
- m_gizmos.insert(GizmosMap::value_type(Flatten, gizmo));
-
-#if ENABLE_SVG_ICONS
- gizmo = new GLGizmoCut(parent, "cut.svg", 4);
-#else
- gizmo = new GLGizmoCut(parent, 4);
-#endif // ENABLE_SVG_ICONS
- if (gizmo == nullptr)
- return false;
-
- if (!gizmo->init()) {
- reset();
- return false;
- }
-
- m_gizmos.insert(GizmosMap::value_type(Cut, gizmo));
-
-#if ENABLE_SVG_ICONS
- gizmo = new GLGizmoSlaSupports(parent, "sla_supports.svg", 5);
-#else
- gizmo = new GLGizmoSlaSupports(parent, 5);
-#endif // ENABLE_SVG_ICONS
- if (gizmo == nullptr)
- return false;
-
- if (!gizmo->init()) {
- reset();
- return false;
- }
-
- m_gizmos.insert(GizmosMap::value_type(SlaSupports, gizmo));
-
- return true;
-}
-
-bool GLCanvas3D::Gizmos::is_enabled() const
-{
- return m_enabled;
-}
-
-void GLCanvas3D::Gizmos::set_enabled(bool enable)
-{
- m_enabled = enable;
-}
-
-#if ENABLE_SVG_ICONS
-void GLCanvas3D::Gizmos::set_overlay_icon_size(float size)
-{
- if (m_overlay_icons_size != size)
- {
- m_overlay_icons_size = size;
- m_icons_texture_dirty = true;
- }
-}
-#endif // ENABLE_SVG_ICONS
-
-void GLCanvas3D::Gizmos::set_overlay_scale(float scale)
-{
-#if ENABLE_SVG_ICONS
- if (m_overlay_scale != scale)
- {
- m_overlay_scale = scale;
- m_icons_texture_dirty = true;
- }
-#else
- m_overlay_icons_scale = scale;
- m_overlay_border = 5.0f * scale;
- m_overlay_gap_y = 5.0f * scale;
-#endif // ENABLE_SVG_ICONS
-}
-
-std::string GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection)
-{
- std::string name = "";
-
- if (!m_enabled)
- return name;
-
- float cnv_h = (float)canvas.get_canvas_size().get_height();
- float height = get_total_overlay_height();
-#if ENABLE_SVG_ICONS
- float scaled_icons_size = m_overlay_icons_size * m_overlay_scale;
- float scaled_border = m_overlay_border * m_overlay_scale;
- float scaled_gap_y = m_overlay_gap_y * m_overlay_scale;
- float scaled_stride_y = scaled_icons_size + scaled_gap_y;
- float top_y = 0.5f * (cnv_h - height) + scaled_border;
-#else
- float top_y = 0.5f * (cnv_h - height) + m_overlay_border;
- float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale;
-#endif // ENABLE_SVG_ICONS
-
- for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
- {
- if ((it->second == nullptr) || !it->second->is_selectable())
- continue;
-
-#if ENABLE_SVG_ICONS
- bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size);
-#else
- bool inside = (m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size);
-#endif // ENABLE_SVG_ICONS
- if (inside)
- name = it->second->get_name();
-
- if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On))
- it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off);
-
-#if ENABLE_SVG_ICONS
- top_y += scaled_stride_y;
-#else
- top_y += (scaled_icons_size + m_overlay_gap_y);
-#endif // ENABLE_SVG_ICONS
- }
-
- return name;
-}
-
-void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection)
-{
- if (!m_enabled)
- return;
-
- float cnv_h = (float)canvas.get_canvas_size().get_height();
- float height = get_total_overlay_height();
-
-#if ENABLE_SVG_ICONS
- float scaled_icons_size = m_overlay_icons_size * m_overlay_scale;
- float scaled_border = m_overlay_border * m_overlay_scale;
- float scaled_gap_y = m_overlay_gap_y * m_overlay_scale;
- float scaled_stride_y = scaled_icons_size + scaled_gap_y;
- float top_y = 0.5f * (cnv_h - height) + scaled_border;
-#else
- float top_y = 0.5f * (cnv_h - height) + m_overlay_border;
- float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale;
-#endif // ENABLE_SVG_ICONS
-
- for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
- {
- if ((it->second == nullptr) || !it->second->is_selectable())
- continue;
-
-#if ENABLE_SVG_ICONS
- bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size);
-#else
- bool inside = (m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size);
-#endif // ENABLE_SVG_ICONS
- if (it->second->is_activable(selection) && inside)
- {
- if ((it->second->get_state() == GLGizmoBase::On))
- {
- it->second->set_state(GLGizmoBase::Hover);
- m_current = Undefined;
- }
- else if ((it->second->get_state() == GLGizmoBase::Hover))
- {
- it->second->set_state(GLGizmoBase::On);
- m_current = it->first;
- }
- }
- else
- it->second->set_state(GLGizmoBase::Off);
-
-#if ENABLE_SVG_ICONS
- top_y += scaled_stride_y;
-#else
- top_y += (scaled_icons_size + m_overlay_gap_y);
-#endif // ENABLE_SVG_ICONS
- }
-
- GizmosMap::iterator it = m_gizmos.find(m_current);
- if ((it != m_gizmos.end()) && (it->second != nullptr) && (it->second->get_state() != GLGizmoBase::On))
- it->second->set_state(GLGizmoBase::On);
-}
-
-void GLCanvas3D::Gizmos::update_on_off_state(const Selection& selection)
-{
- GizmosMap::iterator it = m_gizmos.find(m_current);
- if ((it != m_gizmos.end()) && (it->second != nullptr))
- {
- if (!it->second->is_activable(selection))
- {
- it->second->set_state(GLGizmoBase::Off);
- m_current = Undefined;
- }
- }
-}
-
-void GLCanvas3D::Gizmos::reset_all_states()
-{
- if (!m_enabled)
- return;
-
- for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
- {
- if (it->second != nullptr)
- {
- it->second->set_state(GLGizmoBase::Off);
- it->second->set_hover_id(-1);
- }
- }
-
- m_current = Undefined;
-}
-
-void GLCanvas3D::Gizmos::set_hover_id(int id)
-{
- if (!m_enabled)
- return;
-
- for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
- {
- if ((it->second != nullptr) && (it->second->get_state() == GLGizmoBase::On))
- it->second->set_hover_id(id);
- }
-}
-
-void GLCanvas3D::Gizmos::enable_grabber(EType type, unsigned int id, bool enable)
-{
- if (!m_enabled)
- return;
-
- GizmosMap::const_iterator it = m_gizmos.find(type);
- if (it != m_gizmos.end())
- {
- if (enable)
- it->second->enable_grabber(id);
- else
- it->second->disable_grabber(id);
- }
-}
-
-bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const
-{
- if (!m_enabled)
- return false;
-
- float cnv_h = (float)canvas.get_canvas_size().get_height();
- float height = get_total_overlay_height();
-
-#if ENABLE_SVG_ICONS
- float scaled_icons_size = m_overlay_icons_size * m_overlay_scale;
- float scaled_border = m_overlay_border * m_overlay_scale;
- float scaled_gap_y = m_overlay_gap_y * m_overlay_scale;
- float scaled_stride_y = scaled_icons_size + scaled_gap_y;
- float top_y = 0.5f * (cnv_h - height) + scaled_border;
-#else
- float top_y = 0.5f * (cnv_h - height) + m_overlay_border;
- float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale;
-#endif // ENABLE_SVG_ICONS
-
- for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
- {
- if ((it->second == nullptr) || !it->second->is_selectable())
- continue;
-
-#if ENABLE_SVG_ICONS
- if ((scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size))
-#else
- if ((m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size))
-#endif // ENABLE_SVG_ICONS
- return true;
-
-#if ENABLE_SVG_ICONS
- top_y += scaled_stride_y;
-#else
- top_y += (scaled_icons_size + m_overlay_gap_y);
-#endif // ENABLE_SVG_ICONS
- }
-
- return false;
-}
-
-bool GLCanvas3D::Gizmos::grabber_contains_mouse() const
-{
- if (!m_enabled)
- return false;
-
- GLGizmoBase* curr = get_current();
- return (curr != nullptr) ? (curr->get_hover_id() != -1) : false;
-}
-
-void GLCanvas3D::Gizmos::update(const Linef3& mouse_ray, const Selection& selection, bool shift_down, const Point* mouse_pos)
-{
- if (!m_enabled)
- return;
-
- GLGizmoBase* curr = get_current();
- if (curr != nullptr)
- curr->update(GLGizmoBase::UpdateData(mouse_ray, mouse_pos, shift_down), selection);
-}
-
-GLCanvas3D::Gizmos::EType GLCanvas3D::Gizmos::get_current_type() const
-{
- return m_current;
-}
-
-bool GLCanvas3D::Gizmos::is_running() const
-{
- if (!m_enabled)
- return false;
-
- GLGizmoBase* curr = get_current();
- return (curr != nullptr) ? (curr->get_state() == GLGizmoBase::On) : false;
-}
-
-bool GLCanvas3D::Gizmos::handle_shortcut(int key, const Selection& selection)
-{
- if (!m_enabled || selection.is_empty())
- return false;
-
- EType old_current = m_current;
- bool handled = false;
- for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
- {
- if ((it->second == nullptr) || !it->second->is_selectable())
- continue;
-
- int it_key = it->second->get_shortcut_key();
-
- if (it->second->is_activable(selection) && ((it_key == key - 64) || (it_key == key - 96)))
- {
- if ((it->second->get_state() == GLGizmoBase::On))
- {
- it->second->set_state(GLGizmoBase::Off);
- m_current = Undefined;
- handled = true;
- }
- else if ((it->second->get_state() == GLGizmoBase::Off))
- {
- it->second->set_state(GLGizmoBase::On);
- m_current = it->first;
- handled = true;
- }
- }
- }
-
- if (handled && (old_current != Undefined) && (old_current != m_current))
- {
- GizmosMap::const_iterator it = m_gizmos.find(old_current);
- if (it != m_gizmos.end())
- it->second->set_state(GLGizmoBase::Off);
- }
-
- return handled;
-}
-
-bool GLCanvas3D::Gizmos::is_dragging() const
-{
- if (!m_enabled)
- return false;
-
- GLGizmoBase* curr = get_current();
- return (curr != nullptr) ? curr->is_dragging() : false;
-}
-
-void GLCanvas3D::Gizmos::start_dragging(const Selection& selection)
-{
- if (!m_enabled)
- return;
-
- GLGizmoBase* curr = get_current();
- if (curr != nullptr)
- curr->start_dragging(selection);
-}
-
-void GLCanvas3D::Gizmos::stop_dragging()
-{
- if (!m_enabled)
- return;
-
- GLGizmoBase* curr = get_current();
- if (curr != nullptr)
- curr->stop_dragging();
-}
-
-Vec3d GLCanvas3D::Gizmos::get_displacement() const
-{
- if (!m_enabled)
- return Vec3d::Zero();
-
- GizmosMap::const_iterator it = m_gizmos.find(Move);
- return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_displacement() : Vec3d::Zero();
-}
-
-Vec3d GLCanvas3D::Gizmos::get_scale() const
-{
- if (!m_enabled)
- return Vec3d::Ones();
-
- GizmosMap::const_iterator it = m_gizmos.find(Scale);
- return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_scale() : Vec3d::Ones();
-}
-
-void GLCanvas3D::Gizmos::set_scale(const Vec3d& scale)
-{
- if (!m_enabled)
- return;
-
- GizmosMap::const_iterator it = m_gizmos.find(Scale);
- if (it != m_gizmos.end())
- reinterpret_cast(it->second)->set_scale(scale);
-}
-
-Vec3d GLCanvas3D::Gizmos::get_rotation() const
-{
- if (!m_enabled)
- return Vec3d::Zero();
-
- GizmosMap::const_iterator it = m_gizmos.find(Rotate);
- return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_rotation() : Vec3d::Zero();
-}
-
-void GLCanvas3D::Gizmos::set_rotation(const Vec3d& rotation)
-{
- if (!m_enabled)
- return;
-
- GizmosMap::const_iterator it = m_gizmos.find(Rotate);
- if (it != m_gizmos.end())
- reinterpret_cast(it->second)->set_rotation(rotation);
-}
-
-Vec3d GLCanvas3D::Gizmos::get_flattening_normal() const
-{
- if (!m_enabled)
- return Vec3d::Zero();
-
- GizmosMap::const_iterator it = m_gizmos.find(Flatten);
- return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_flattening_normal() : Vec3d::Zero();
-}
-
-void GLCanvas3D::Gizmos::set_flattening_data(const ModelObject* model_object)
-{
- if (!m_enabled)
- return;
-
- GizmosMap::const_iterator it = m_gizmos.find(Flatten);
- if (it != m_gizmos.end())
- reinterpret_cast(it->second)->set_flattening_data(model_object);
-}
-
-void GLCanvas3D::Gizmos::set_sla_support_data(ModelObject* model_object, const Selection& selection)
-{
- if (!m_enabled)
- return;
-
- GizmosMap::const_iterator it = m_gizmos.find(SlaSupports);
- if (it != m_gizmos.end())
- reinterpret_cast(it->second)->set_sla_support_data(model_object, selection);
-}
-
-
-// Returns true if the gizmo used the event to do something, false otherwise.
-bool GLCanvas3D::Gizmos::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down)
-{
- if (!m_enabled)
- return false;
-
- GizmosMap::const_iterator it = m_gizmos.find(SlaSupports);
- if (it != m_gizmos.end())
- return reinterpret_cast(it->second)->gizmo_event(action, mouse_position, shift_down);
-
- return false;
-}
-
-void GLCanvas3D::Gizmos::render_current_gizmo(const Selection& selection) const
-{
- if (!m_enabled)
- return;
-
- do_render_current_gizmo(selection);
-}
-
-void GLCanvas3D::Gizmos::render_current_gizmo_for_picking_pass(const Selection& selection) const
-{
- if (!m_enabled)
- return;
-
- GLGizmoBase* curr = get_current();
- if (curr != nullptr)
- curr->render_for_picking(selection);
-}
-
-void GLCanvas3D::Gizmos::render_overlay(const GLCanvas3D& canvas, const Selection& selection) const
-{
- if (!m_enabled)
- return;
-
-#if ENABLE_SVG_ICONS
- if (m_icons_texture_dirty)
- generate_icons_texture();
-#endif // ENABLE_SVG_ICONS
-
- ::glDisable(GL_DEPTH_TEST);
-
- ::glPushMatrix();
- ::glLoadIdentity();
-
- do_render_overlay(canvas, selection);
-
- ::glPopMatrix();
-}
-
-
-void GLCanvas3D::Gizmos::reset()
-{
- for (GizmosMap::value_type& gizmo : m_gizmos)
- {
- delete gizmo.second;
- gizmo.second = nullptr;
- }
-
- m_gizmos.clear();
-}
-
-void GLCanvas3D::Gizmos::do_render_overlay(const GLCanvas3D& canvas, const Selection& selection) const
-{
- if (m_gizmos.empty())
- return;
-
- float cnv_w = (float)canvas.get_canvas_size().get_width();
- float cnv_h = (float)canvas.get_canvas_size().get_height();
- float zoom = canvas.get_camera_zoom();
- float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
-
- float height = get_total_overlay_height();
- float width = get_total_overlay_width();
-#if ENABLE_SVG_ICONS
- float scaled_border = m_overlay_border * m_overlay_scale * inv_zoom;
-#else
- float scaled_border = m_overlay_border * inv_zoom;
-#endif // ENABLE_SVG_ICONS
-
- float top_x = (-0.5f * cnv_w) * inv_zoom;
- float top_y = (0.5f * height) * inv_zoom;
-
- float left = top_x;
- float top = top_y;
- float right = left + width * inv_zoom;
- float bottom = top - height * inv_zoom;
-
- // renders background
- unsigned int bg_tex_id = m_background_texture.texture.get_id();
- float bg_tex_width = (float)m_background_texture.texture.get_width();
- float bg_tex_height = (float)m_background_texture.texture.get_height();
- if ((bg_tex_id != 0) && (bg_tex_width > 0) && (bg_tex_height > 0))
- {
- float inv_bg_tex_width = (bg_tex_width != 0.0f) ? 1.0f / bg_tex_width : 0.0f;
- float inv_bg_tex_height = (bg_tex_height != 0.0f) ? 1.0f / bg_tex_height : 0.0f;
-
- float bg_uv_left = 0.0f;
- float bg_uv_right = 1.0f;
- float bg_uv_top = 1.0f;
- float bg_uv_bottom = 0.0f;
-
- float bg_left = left;
- float bg_right = right;
- float bg_top = top;
- float bg_bottom = bottom;
- float bg_width = right - left;
- float bg_height = top - bottom;
- float bg_min_size = std::min(bg_width, bg_height);
-
- float bg_uv_i_left = (float)m_background_texture.metadata.left * inv_bg_tex_width;
- float bg_uv_i_right = 1.0f - (float)m_background_texture.metadata.right * inv_bg_tex_width;
- float bg_uv_i_top = 1.0f - (float)m_background_texture.metadata.top * inv_bg_tex_height;
- float bg_uv_i_bottom = (float)m_background_texture.metadata.bottom * inv_bg_tex_height;
-
- float bg_i_left = bg_left + scaled_border;
- float bg_i_right = bg_right - scaled_border;
- float bg_i_top = bg_top - scaled_border;
- float bg_i_bottom = bg_bottom + scaled_border;
-
- bg_uv_left = bg_uv_i_left;
- bg_i_left = bg_left;
-
- if ((m_overlay_border > 0) && (bg_uv_top != bg_uv_i_top))
- {
- if (bg_uv_left != bg_uv_i_left)
- GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_top, bg_top, { { bg_uv_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_top }, { bg_uv_left, bg_uv_top } });
-
- GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_top, bg_top, { { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_top }, { bg_uv_i_left, bg_uv_top } });
-
- if (bg_uv_right != bg_uv_i_right)
- GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_top, bg_top, { { bg_uv_i_right, bg_uv_i_top }, { bg_uv_right, bg_uv_i_top }, { bg_uv_right, bg_uv_top }, { bg_uv_i_right, bg_uv_top } });
- }
-
- if ((m_overlay_border > 0) && (bg_uv_left != bg_uv_i_left))
- GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_bottom, bg_i_top, { { bg_uv_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_left, bg_uv_i_top } });
-
- GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_bottom, bg_i_top, { { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top } });
-
- if ((m_overlay_border > 0) && (bg_uv_right != bg_uv_i_right))
- GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_bottom, bg_i_top, { { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top } });
-
- if ((m_overlay_border > 0) && (bg_uv_bottom != bg_uv_i_bottom))
- {
- if (bg_uv_left != bg_uv_i_left)
- GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_bottom, bg_i_bottom, { { bg_uv_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_left, bg_uv_i_bottom } });
-
- GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_bottom, bg_i_bottom, { { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_right, bg_uv_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom } });
-
- if (bg_uv_right != bg_uv_i_right)
- GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_bottom, bg_i_bottom, { { bg_uv_i_right, bg_uv_bottom }, { bg_uv_right, bg_uv_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom } });
- }
- }
-
-#if ENABLE_SVG_ICONS
- top_x += scaled_border;
- top_y -= scaled_border;
- float scaled_gap_y = m_overlay_gap_y * m_overlay_scale * inv_zoom;
-
- float scaled_icons_size = m_overlay_icons_size * m_overlay_scale * inv_zoom;
- float scaled_stride_y = scaled_icons_size + scaled_gap_y;
- unsigned int icons_texture_id = m_icons_texture.get_id();
- unsigned int tex_width = m_icons_texture.get_width();
- unsigned int tex_height = m_icons_texture.get_height();
- float inv_tex_width = (tex_width != 0) ? 1.0f / (float)tex_width : 0.0f;
- float inv_tex_height = (tex_height != 0) ? 1.0f / (float)tex_height : 0.0f;
-#else
- top_x += m_overlay_border * inv_zoom;
- top_y -= m_overlay_border * inv_zoom;
- float scaled_gap_y = m_overlay_gap_y * inv_zoom;
-
- float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale * inv_zoom;
- unsigned int icons_texture_id = m_icons_texture.texture.get_id();
- unsigned int texture_size = m_icons_texture.texture.get_width();
- float inv_texture_size = (texture_size != 0) ? 1.0f / (float)texture_size : 0.0f;
-#endif // ENABLE_SVG_ICONS
-
-#if ENABLE_SVG_ICONS
- if ((icons_texture_id == 0) || (tex_width <= 0) || (tex_height <= 0))
- return;
-#endif // ENABLE_SVG_ICONS
-
- for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
- {
- if ((it->second == nullptr) || !it->second->is_selectable())
- continue;
-
- unsigned int sprite_id = it->second->get_sprite_id();
- GLGizmoBase::EState state = it->second->get_state();
-
-#if ENABLE_SVG_ICONS
- float u_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_width;
- float v_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_height;
- float v_top = sprite_id * v_icon_size;
- float u_left = state * u_icon_size;
- float v_bottom = v_top + v_icon_size;
- float u_right = u_left + u_icon_size;
-#else
- float uv_icon_size = (float)m_icons_texture.metadata.icon_size * inv_texture_size;
- float v_top = sprite_id * uv_icon_size;
- float u_left = state * uv_icon_size;
- float v_bottom = v_top + uv_icon_size;
- float u_right = u_left + uv_icon_size;
-#endif // ENABLE_SVG_ICONS
-
- GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + scaled_icons_size, top_y - scaled_icons_size, top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } });
- if (it->second->get_state() == GLGizmoBase::On) {
- float toolbar_top = (float)cnv_h - canvas.m_view_toolbar.get_height();
-#if ENABLE_SVG_ICONS
- it->second->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection);
-#else
- it->second->render_input_window(2.0f * m_overlay_border + icon_size * zoom, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection);
-#endif // ENABLE_SVG_ICONS
- }
-#if ENABLE_SVG_ICONS
- top_y -= scaled_stride_y;
-#else
- top_y -= (scaled_icons_size + scaled_gap_y);
-#endif // ENABLE_SVG_ICONS
- }
-}
-
-void GLCanvas3D::Gizmos::do_render_current_gizmo(const Selection& selection) const
-{
- GLGizmoBase* curr = get_current();
- if (curr != nullptr)
- curr->render(selection);
-}
-
-float GLCanvas3D::Gizmos::get_total_overlay_height() const
-{
-#if ENABLE_SVG_ICONS
- float scaled_icons_size = m_overlay_icons_size * m_overlay_scale;
- float scaled_border = m_overlay_border * m_overlay_scale;
- float scaled_gap_y = m_overlay_gap_y * m_overlay_scale;
- float scaled_stride_y = scaled_icons_size + scaled_gap_y;
- float height = 2.0f * scaled_border;
-#else
- float height = 2.0f * m_overlay_border;
-
- float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale;
-#endif // ENABLE_SVG_ICONS
-
- for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
- {
- if ((it->second == nullptr) || !it->second->is_selectable())
- continue;
-
-#if ENABLE_SVG_ICONS
- height += scaled_stride_y;
-#else
- height += (scaled_icons_size + m_overlay_gap_y);
-#endif // ENABLE_SVG_ICONS
- }
-
-#if ENABLE_SVG_ICONS
- return height - scaled_gap_y;
-#else
- return height - m_overlay_gap_y;
-#endif // ENABLE_SVG_ICONS
-}
-
-float GLCanvas3D::Gizmos::get_total_overlay_width() const
-{
-#if ENABLE_SVG_ICONS
- return (2.0f * m_overlay_border + m_overlay_icons_size) * m_overlay_scale;
-#else
- return (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale + 2.0f * m_overlay_border;
-#endif // ENABLE_SVG_ICONS
-}
-
-GLGizmoBase* GLCanvas3D::Gizmos::get_current() const
-{
- GizmosMap::const_iterator it = m_gizmos.find(m_current);
- return (it != m_gizmos.end()) ? it->second : nullptr;
-}
-
-#if ENABLE_SVG_ICONS
-bool GLCanvas3D::Gizmos::generate_icons_texture() const
-{
- std::string path = resources_dir() + "/icons/";
- std::vector filenames;
- for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
- {
- if (it->second != nullptr)
- {
- const std::string& icon_filename = it->second->get_icon_filename();
- if (!icon_filename.empty())
- filenames.push_back(path + icon_filename);
- }
- }
-
- std::vector> states;
- states.push_back(std::make_pair(1, false));
- states.push_back(std::make_pair(0, false));
- states.push_back(std::make_pair(0, true));
-
- bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, (unsigned int)(m_overlay_icons_size * m_overlay_scale));
- if (res)
- m_icons_texture_dirty = false;
-
- return res;
-}
-#endif // ENABLE_SVG_ICONS
-
const unsigned char GLCanvas3D::WarningTexture::Background_Color[3] = { 120, 120, 120 };//{ 9, 91, 134 };
const unsigned char GLCanvas3D::WarningTexture::Opacity = 255;
@@ -1778,14 +838,14 @@ bool GLCanvas3D::WarningTexture::_generate(const std::string& msg_utf8, const GL
}
// sends buffer to gpu
- ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- ::glGenTextures(1, &m_id);
- ::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id);
- ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
- ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
- ::glBindTexture(GL_TEXTURE_2D, 0);
+ glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
+ glsafe(::glGenTextures(1, &m_id));
+ glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id));
+ glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
+ glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+ glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+ glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
+ glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
return true;
}
@@ -1797,12 +857,12 @@ void GLCanvas3D::WarningTexture::render(const GLCanvas3D& canvas) const
if ((m_id > 0) && (m_original_width > 0) && (m_original_height > 0) && (m_width > 0) && (m_height > 0))
{
- ::glDisable(GL_DEPTH_TEST);
- ::glPushMatrix();
- ::glLoadIdentity();
+ glsafe(::glDisable(GL_DEPTH_TEST));
+ glsafe(::glPushMatrix());
+ glsafe(::glLoadIdentity());
const Size& cnv_size = canvas.get_canvas_size();
- float zoom = canvas.get_camera_zoom();
+ float zoom = canvas.get_camera().zoom;
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float left = (-0.5f * (float)m_original_width) * inv_zoom;
float top = (-0.5f * (float)cnv_size.get_height() + (float)m_original_height + 2.0f) * inv_zoom;
@@ -1822,8 +882,8 @@ void GLCanvas3D::WarningTexture::render(const GLCanvas3D& canvas) const
GLTexture::render_sub_texture(m_id, left, right, bottom, top, uvs);
- ::glPopMatrix();
- ::glEnable(GL_DEPTH_TEST);
+ glsafe(::glPopMatrix());
+ glsafe(::glEnable(GL_DEPTH_TEST));
}
}
@@ -2050,14 +1110,14 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c
}
// sends buffer to gpu
- ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- ::glGenTextures(1, &m_id);
- ::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id);
- ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
- ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
- ::glBindTexture(GL_TEXTURE_2D, 0);
+ glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
+ glsafe(::glGenTextures(1, &m_id));
+ glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id));
+ glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
+ glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+ glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+ glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
+ glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
return true;
}
@@ -2066,12 +1126,12 @@ void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const
{
if ((m_id > 0) && (m_original_width > 0) && (m_original_height > 0) && (m_width > 0) && (m_height > 0))
{
- ::glDisable(GL_DEPTH_TEST);
- ::glPushMatrix();
- ::glLoadIdentity();
+ glsafe(::glDisable(GL_DEPTH_TEST));
+ glsafe(::glPushMatrix());
+ glsafe(::glLoadIdentity());
const Size& cnv_size = canvas.get_canvas_size();
- float zoom = canvas.get_camera_zoom();
+ float zoom = canvas.get_camera().zoom;
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float left = (-0.5f * (float)cnv_size.get_width()) * inv_zoom;
float top = (0.5f * (float)cnv_size.get_height()) * inv_zoom;
@@ -2091,8 +1151,8 @@ void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const
GLTexture::render_sub_texture(m_id, left, right, bottom, top, uvs);
- ::glPopMatrix();
- ::glEnable(GL_DEPTH_TEST);
+ glsafe(::glPopMatrix());
+ glsafe(::glEnable(GL_DEPTH_TEST));
}
}
@@ -2182,48 +1242,48 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl)
if ((m_canvas == nullptr) || (m_context == nullptr))
return false;
- ::glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
- ::glClearDepth(1.0f);
+ glsafe(::glClearColor(1.0f, 1.0f, 1.0f, 1.0f));
+ glsafe(::glClearDepth(1.0f));
- ::glDepthFunc(GL_LESS);
+ glsafe(::glDepthFunc(GL_LESS));
- ::glEnable(GL_DEPTH_TEST);
- ::glEnable(GL_CULL_FACE);
- ::glEnable(GL_BLEND);
- ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glsafe(::glEnable(GL_DEPTH_TEST));
+ glsafe(::glEnable(GL_CULL_FACE));
+ glsafe(::glEnable(GL_BLEND));
+ glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
// Set antialiasing / multisampling
- ::glDisable(GL_LINE_SMOOTH);
- ::glDisable(GL_POLYGON_SMOOTH);
+ glsafe(::glDisable(GL_LINE_SMOOTH));
+ glsafe(::glDisable(GL_POLYGON_SMOOTH));
// ambient lighting
GLfloat ambient[4] = { 0.3f, 0.3f, 0.3f, 1.0f };
- ::glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
+ glsafe(::glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient));
- ::glEnable(GL_LIGHT0);
- ::glEnable(GL_LIGHT1);
+ glsafe(::glEnable(GL_LIGHT0));
+ glsafe(::glEnable(GL_LIGHT1));
// light from camera
GLfloat specular_cam[4] = { 0.3f, 0.3f, 0.3f, 1.0f };
- ::glLightfv(GL_LIGHT1, GL_SPECULAR, specular_cam);
+ glsafe(::glLightfv(GL_LIGHT1, GL_SPECULAR, specular_cam));
GLfloat diffuse_cam[4] = { 0.2f, 0.2f, 0.2f, 1.0f };
- ::glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse_cam);
+ glsafe(::glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse_cam));
// light from above
GLfloat specular_top[4] = { 0.2f, 0.2f, 0.2f, 1.0f };
- ::glLightfv(GL_LIGHT0, GL_SPECULAR, specular_top);
+ glsafe(::glLightfv(GL_LIGHT0, GL_SPECULAR, specular_top));
GLfloat diffuse_top[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
- ::glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_top);
+ glsafe(::glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_top));
// Enables Smooth Color Shading; try GL_FLAT for (lack of) fun.
- ::glShadeModel(GL_SMOOTH);
+ glsafe(::glShadeModel(GL_SMOOTH));
// A handy trick -- have surface material mirror the color.
- ::glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
- ::glEnable(GL_COLOR_MATERIAL);
+ glsafe(::glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE));
+ glsafe(::glEnable(GL_COLOR_MATERIAL));
if (m_multisample_allowed)
- ::glEnable(GL_MULTISAMPLE);
+ glsafe(::glEnable(GL_MULTISAMPLE));
if (useVBOs && !m_shader.init("gouraud.vs", "gouraud.fs"))
return false;
@@ -2355,11 +1415,6 @@ void GLCanvas3D::set_color_by(const std::string& value)
m_color_by = value;
}
-float GLCanvas3D::get_camera_zoom() const
-{
- return m_camera.zoom;
-}
-
BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const
{
BoundingBoxf3 bb;
@@ -2473,30 +1528,8 @@ void GLCanvas3D::zoom_to_selection()
void GLCanvas3D::select_view(const std::string& direction)
{
- const float* dir_vec = nullptr;
-
- if (direction == "iso")
- dir_vec = VIEW_DEFAULT;
- else if (direction == "left")
- dir_vec = VIEW_LEFT;
- else if (direction == "right")
- dir_vec = VIEW_RIGHT;
- else if (direction == "top")
- dir_vec = VIEW_TOP;
- else if (direction == "bottom")
- dir_vec = VIEW_BOTTOM;
- else if (direction == "front")
- dir_vec = VIEW_FRONT;
- else if (direction == "rear")
- dir_vec = VIEW_REAR;
-
- if (dir_vec != nullptr)
- {
- m_camera.phi = dir_vec[0];
- m_camera.set_theta(dir_vec[1], false);
- if (m_canvas != nullptr)
- m_canvas->Refresh();
- }
+ if (m_camera.select_view(direction) && (m_canvas != nullptr))
+ m_canvas->Refresh();
}
void GLCanvas3D::update_volumes_colors_by_extruder()
@@ -2540,12 +1573,12 @@ void GLCanvas3D::render()
m_camera.requires_zoom_to_bed = false;
}
- _camera_tranform();
+ m_camera.apply_view_matrix();
GLfloat position_cam[4] = { 1.0f, 0.0f, 1.0f, 0.0f };
- ::glLightfv(GL_LIGHT1, GL_POSITION, position_cam);
+ glsafe(::glLightfv(GL_LIGHT1, GL_POSITION, position_cam));
GLfloat position_top[4] = { -0.5f, -0.5f, 1.0f, 0.0f };
- ::glLightfv(GL_LIGHT0, GL_POSITION, position_top);
+ glsafe(::glLightfv(GL_LIGHT0, GL_POSITION, position_top));
float theta = m_camera.get_theta();
if (theta > 180.f)
@@ -2558,7 +1591,7 @@ void GLCanvas3D::render()
_picking_pass();
// draw scene
- ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
_render_background();
// textured bed needs to be rendered after objects if the texture is transparent
@@ -2953,7 +1986,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
m_selection.volumes_changed(map_glvolume_old_to_new);
}
- _update_gizmos_data();
+ m_gizmos.update_data(*this);
// Update the toolbar
post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT));
@@ -3178,6 +2211,9 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
return;
}
+ if (m_gizmos.on_char(evt, *this))
+ return;
+
//#ifdef __APPLE__
// ctrlMask |= wxMOD_RAW_CONTROL;
//#endif /* __APPLE__ */
@@ -3186,9 +2222,6 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
case 'a':
case 'A':
case WXK_CONTROL_A:
- if (m_gizmos.get_current_type() == Gizmos::SlaSupports && m_gizmos.gizmo_event(SLAGizmoEventType::SelectAll)) // Sla gizmo selects all support points
- m_dirty = true;
- else
post_event(SimpleEvent(EVT_GLCANVAS_SELECT_ALL));
break;
#ifdef __APPLE__
@@ -3204,29 +2237,12 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
} else {
switch (keyCode)
{
- // key ESC
- case WXK_ESCAPE: {
- if (m_gizmos.get_current_type() != Gizmos::SlaSupports || !m_gizmos.gizmo_event(SLAGizmoEventType::DiscardChanges))
- m_gizmos.reset_all_states();
- m_dirty = true;
- break;
- }
-
- case WXK_RETURN: {
- if (m_gizmos.get_current_type() == Gizmos::SlaSupports && m_gizmos.gizmo_event(SLAGizmoEventType::ApplyChanges))
- m_dirty = true;
- break;
- }
-
#ifdef __APPLE__
case WXK_BACK: // the low cost Apple solutions are not equipped with a Delete key, use Backspace instead.
#else /* __APPLE__ */
case WXK_DELETE:
#endif /* __APPLE__ */
- if (m_gizmos.get_current_type() == Gizmos::SlaSupports && m_gizmos.gizmo_event(SLAGizmoEventType::Delete))
- m_dirty = true;
- else
- post_event(SimpleEvent(EVT_GLTOOLBAR_DELETE));
+ post_event(SimpleEvent(EVT_GLTOOLBAR_DELETE));
break;
case '0': { select_view("iso"); break; }
@@ -3240,15 +2256,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
case '-': { post_event(Event(EVT_GLCANVAS_INCREASE_INSTANCES, -1)); break; }
case '?': { post_event(SimpleEvent(EVT_GLCANVAS_QUESTION_MARK)); break; }
case 'A':
- case 'a': {
- if (m_gizmos.get_current_type() == Gizmos::SlaSupports) {
- if (m_gizmos.gizmo_event(SLAGizmoEventType::AutomaticGeneration))
- m_dirty = true;
- }
- else
- post_event(SimpleEvent(EVT_GLCANVAS_ARRANGE));
- break;
- }
+ case 'a': { post_event(SimpleEvent(EVT_GLCANVAS_ARRANGE)); break; }
case 'B':
case 'b': { zoom_to_bed(); break; }
case 'I':
@@ -3257,23 +2265,9 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
case 'o': { set_camera_zoom(-1.0f); break; }
case 'Z':
case 'z': { m_selection.is_empty() ? zoom_to_volumes() : zoom_to_selection(); break; }
- case 'M':
- case 'm': {
- if (m_gizmos.get_current_type() == Gizmos::SlaSupports && m_gizmos.gizmo_event(SLAGizmoEventType::ManualEditing)) {
- m_dirty = true;
- break;
- }
- } // intentional fallthrough
default:
{
- if (m_gizmos.handle_shortcut(keyCode, m_selection))
- {
- _update_gizmos_data();
- m_dirty = true;
- }
- else
- evt.Skip();
-
+ evt.Skip();
break;
}
}
@@ -3287,18 +2281,22 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
auto imgui = wxGetApp().imgui();
if (imgui->update_key_data(evt)) {
render();
- } else
- if (evt.GetEventType() == wxEVT_KEY_UP) {
- if (m_tab_down && keyCode == WXK_TAB && !evt.HasAnyModifiers()) {
- // Enable switching between 3D and Preview with Tab
- // m_canvas->HandleAsNavigationKey(evt); // XXX: Doesn't work in some cases / on Linux
- post_event(SimpleEvent(EVT_GLCANVAS_TAB));
- } else if (m_gizmos.get_current_type() == Gizmos::SlaSupports && keyCode == WXK_SHIFT && m_gizmos.gizmo_event(SLAGizmoEventType::ShiftUp)) {
- // shift has been just released - SLA gizmo might want to close rectangular selection.
- m_dirty = true;
+ }
+ else
+ {
+ if (!m_gizmos.on_key(evt, *this))
+ {
+ if (evt.GetEventType() == wxEVT_KEY_UP) {
+ if (m_tab_down && keyCode == WXK_TAB && !evt.HasAnyModifiers()) {
+ // Enable switching between 3D and Preview with Tab
+ // m_canvas->HandleAsNavigationKey(evt); // XXX: Doesn't work in some cases / on Linux
+ post_event(SimpleEvent(EVT_GLCANVAS_TAB));
+ }
+ }
+ else if (evt.GetEventType() == wxEVT_KEY_DOWN) {
+ m_tab_down = keyCode == WXK_TAB && !evt.HasAnyModifiers();
+ }
}
- } else if (evt.GetEventType() == wxEVT_KEY_DOWN) {
- m_tab_down = keyCode == WXK_TAB && !evt.HasAnyModifiers();
}
if (keyCode != WXK_TAB
@@ -3402,6 +2400,18 @@ std::string format_mouse_event_debug_message(const wxMouseEvent &evt)
void GLCanvas3D::on_mouse(wxMouseEvent& evt)
{
+ auto mouse_up_cleanup = [this](){
+ m_moving = false;
+ m_mouse.drag.move_volume_idx = -1;
+ m_mouse.set_start_position_3D_as_invalid();
+ m_mouse.set_start_position_2D_as_invalid();
+ m_mouse.dragging = false;
+ m_dirty = true;
+
+ if (m_canvas->HasCapture())
+ m_canvas->ReleaseMouse();
+ };
+
#if ENABLE_RETINA_GL
const float scale = m_retina_helper->get_scale_factor();
evt.SetX(evt.GetX() * scale);
@@ -3438,11 +2448,27 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
#endif /* SLIC3R_DEBUG_MOUSE_EVENTS */
}
- bool processed_by_toolbar = m_toolbar.on_mouse(evt, *this);
- processed_by_toolbar |= m_view_toolbar.on_mouse(evt, *this);
-
- if (processed_by_toolbar)
+ if (m_toolbar.on_mouse(evt, *this))
{
+ if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())
+ mouse_up_cleanup();
+ m_mouse.set_start_position_3D_as_invalid();
+ return;
+ }
+
+ if (m_view_toolbar.on_mouse(evt, *this))
+ {
+ if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())
+ mouse_up_cleanup();
+ m_mouse.set_start_position_3D_as_invalid();
+ return;
+ }
+
+ if (m_gizmos.on_mouse(evt, *this))
+ {
+ if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())
+ mouse_up_cleanup();
+
m_mouse.set_start_position_3D_as_invalid();
return;
}
@@ -3453,7 +2479,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
int selected_object_idx = m_selection.get_object_idx();
int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1;
m_layers_editing.select_object(*m_model, layer_editing_object_idx);
- bool gizmos_overlay_contains_mouse = m_gizmos.overlay_contains_mouse(*this, m_mouse.position);
if (m_mouse.drag.move_requires_threshold && m_mouse.is_move_start_threshold_position_2D_defined() && m_mouse.is_move_threshold_met(pos))
{
@@ -3495,10 +2520,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_mouse.position = Vec2d(-1.0, -1.0);
m_dirty = true;
}
- else if (evt.LeftDClick() && (m_gizmos.get_current_type() != Gizmos::Undefined))
- {
- m_mouse.ignore_up_event = true;
- }
else if (evt.LeftDown() || evt.RightDown())
{
// If user pressed left or right button we first check whether this happened
@@ -3523,35 +2544,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_dirty = true;
}
}
- else if (!m_selection.is_empty() && gizmos_overlay_contains_mouse)
- {
- m_gizmos.update_on_off_state(*this, m_mouse.position, m_selection);
- _update_gizmos_data();
- m_dirty = true;
- }
- else if (evt.LeftDown() && m_gizmos.get_current_type() == Gizmos::SlaSupports && m_gizmos.gizmo_event(SLAGizmoEventType::LeftDown, Vec2d(pos(0), pos(1)), evt.ShiftDown()))
- {
- // the gizmo got the event and took some action, there is no need to do anything more
- }
- else if (evt.LeftDown() && !m_selection.is_empty() && m_gizmos.grabber_contains_mouse())
- {
- _update_gizmos_data();
- m_selection.start_dragging();
- m_gizmos.start_dragging(m_selection);
-
- if (m_gizmos.get_current_type() == Gizmos::Flatten) {
- // Rotate the object so the normal points downward:
- m_selection.flattening_rotate(m_gizmos.get_flattening_normal());
- do_flatten();
- wxGetApp().obj_manipul()->update_settings_value(m_selection);
- }
-
- m_dirty = true;
- }
- else if ((selected_object_idx != -1) && evt.RightDown() && m_gizmos.get_current_type() == Gizmos::SlaSupports && m_gizmos.gizmo_event(SLAGizmoEventType::RightDown))
- {
- // event was taken care of by the SlaSupports gizmo
- }
else
{
// Select volume in this 3D canvas.
@@ -3563,15 +2555,15 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (evt.LeftDown() && (m_hover_volume_id != -1))
{
bool already_selected = m_selection.contains_volume(m_hover_volume_id);
- bool shift_down = evt.ShiftDown();
+ bool ctrl_down = evt.CmdDown();
Selection::IndicesList curr_idxs = m_selection.get_volume_idxs();
- if (already_selected && shift_down)
+ if (already_selected && ctrl_down)
m_selection.remove(m_hover_volume_id);
else
{
- bool add_as_single = !already_selected && !shift_down;
+ bool add_as_single = !already_selected && !ctrl_down;
m_selection.add(m_hover_volume_id, add_as_single);
m_mouse.drag.move_requires_threshold = !already_selected;
if (already_selected)
@@ -3582,9 +2574,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (curr_idxs != m_selection.get_volume_idxs())
{
-
- m_gizmos.update_on_off_state(m_selection);
- _update_gizmos_data();
+ m_gizmos.refresh_on_off_state(m_selection);
+ m_gizmos.update_data(*this);
post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT));
m_dirty = true;
}
@@ -3611,8 +2602,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
}
}
- else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown)
- && (m_mouse.drag.move_volume_idx != -1) && m_gizmos.get_current_type() != Gizmos::SlaSupports /* don't allow dragging objects with the Sla gizmo on */)
+ else if (evt.Dragging() && evt.LeftIsDown() && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.move_volume_idx != -1))
{
if (!m_mouse.drag.move_requires_threshold)
{
@@ -3635,11 +2625,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
// vector from the starting position to the found intersection
Vec3d inters_vec = inters - m_mouse.drag.start_position_3D;
- // get the view matrix back from opengl
- GLfloat matrix[16];
- ::glGetFloatv(GL_MODELVIEW_MATRIX, matrix);
- Vec3d camera_right((double)matrix[0], (double)matrix[4], (double)matrix[8]);
- Vec3d camera_up((double)matrix[1], (double)matrix[5], (double)matrix[9]);
+ Vec3d camera_right = m_camera.get_dir_right();
+ Vec3d camera_up = m_camera.get_dir_up();
// finds projection of the vector along the camera axes
double projection_x = inters_vec.dot(camera_right);
@@ -3665,53 +2652,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_dirty = true;
}
}
- else if (evt.Dragging() && m_gizmos.is_dragging())
- {
- if (!m_canvas->HasCapture())
- m_canvas->CaptureMouse();
-
- m_mouse.dragging = true;
- m_gizmos.update(mouse_ray(pos), m_selection, evt.ShiftDown(), &pos);
-
- switch (m_gizmos.get_current_type())
- {
- case Gizmos::Move:
- {
- // Apply new temporary offset
- m_selection.translate(m_gizmos.get_displacement());
- wxGetApp().obj_manipul()->update_settings_value(m_selection);
- break;
- }
- case Gizmos::Scale:
- {
- // Apply new temporary scale factors
- m_selection.scale(m_gizmos.get_scale(), evt.AltDown());
- wxGetApp().obj_manipul()->update_settings_value(m_selection);
- break;
- }
- case Gizmos::Rotate:
- {
- // Apply new temporary rotations
- TransformationType transformation_type(TransformationType::World_Relative_Joint);
- if (evt.AltDown())
- transformation_type.set_independent();
- m_selection.rotate(m_gizmos.get_rotation(), transformation_type);
- wxGetApp().obj_manipul()->update_settings_value(m_selection);
- break;
- }
- default:
- break;
- }
-
- m_dirty = true;
- }
- else if (evt.Dragging() && m_gizmos.get_current_type() == Gizmos::SlaSupports && m_gizmos.gizmo_event(SLAGizmoEventType::Dragging, Vec2d(pos(0), pos(1)), evt.ShiftDown()))
- {
- // the gizmo got the event and took some action, no need to do anything more here
- m_dirty = true;
- }
- // do not process dragging if the mouse is into any of the HUD elements
- else if (evt.Dragging() && !gizmos_overlay_contains_mouse)
+ else if (evt.Dragging())
{
m_mouse.dragging = true;
@@ -3757,13 +2698,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
_stop_timer();
m_layers_editing.accept_changes(*this);
}
- else if (evt.LeftUp() && m_gizmos.get_current_type() == Gizmos::SlaSupports && !m_gizmos.is_dragging()
- && !m_mouse.dragging)
- {
- // in case SLA gizmo is selected, we just pass the LeftUp event and stop processing - neither
- // object moving or selecting is suppressed in that case
- m_gizmos.gizmo_event(SLAGizmoEventType::LeftUp, Vec2d(pos(0), pos(1)), evt.ShiftDown());
- }
else if ((m_mouse.drag.move_volume_idx != -1) && m_mouse.dragging)
{
m_regenerate_volumes = false;
@@ -3773,52 +2707,18 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
// of the scene with the background processing data should be performed.
post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
}
- else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.grabber_contains_mouse() && !m_gizmos.is_dragging()
- && !is_layers_editing_enabled())
+ else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !is_layers_editing_enabled())
{
// deselect and propagate event through callback
- if (!evt.ShiftDown() && m_picking_enabled && !m_mouse.ignore_up_event)
+ if (!evt.ShiftDown() && m_picking_enabled)
{
m_selection.clear();
m_selection.set_mode(Selection::Instance);
wxGetApp().obj_manipul()->update_settings_value(m_selection);
m_gizmos.reset_all_states();
- _update_gizmos_data();
+ m_gizmos.update_data(*this);
post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT));
}
- m_mouse.ignore_up_event = false;
- }
- else if (evt.LeftUp() && m_gizmos.is_dragging())
- {
- switch (m_gizmos.get_current_type())
- {
- case Gizmos::Move:
- {
- m_regenerate_volumes = false;
- do_move();
- break;
- }
- case Gizmos::Scale:
- {
- do_scale();
- break;
- }
- case Gizmos::Rotate:
- {
- do_rotate();
- break;
- }
- default:
- break;
- }
- m_gizmos.stop_dragging();
- _update_gizmos_data();
-
- wxGetApp().obj_manipul()->update_settings_value(m_selection);
- // Let the platter know that the dragging finished, so a delayed refresh
- // of the scene with the background processing data should be performed.
- post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
- m_camera.set_scene_box(scene_bounding_box());
}
else if (evt.RightUp())
{
@@ -3831,13 +2731,13 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
// if right clicking on volume, propagate event through callback (shows context menu)
if (m_volumes.volumes[m_hover_volume_id]->hover
&& !m_volumes.volumes[m_hover_volume_id]->is_wipe_tower // no context menu for the wipe tower
- && m_gizmos.get_current_type() != Gizmos::SlaSupports) // disable context menu when the gizmo is open
+ && m_gizmos.get_current_type() != GLGizmosManager::SlaSupports) // disable context menu when the gizmo is open
{
// forces the selection of the volume
m_selection.add(m_hover_volume_id);
- m_gizmos.update_on_off_state(m_selection);
+ m_gizmos.refresh_on_off_state(m_selection);
post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT));
- _update_gizmos_data();
+ m_gizmos.update_data(*this);
wxGetApp().obj_manipul()->update_settings_value(m_selection);
// forces a frame render to update the view before the context menu is shown
render();
@@ -3852,25 +2752,15 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
}
- m_moving = false;
- m_mouse.drag.move_volume_idx = -1;
- m_mouse.set_start_position_3D_as_invalid();
- m_mouse.set_start_position_2D_as_invalid();
- m_mouse.dragging = false;
- m_dirty = true;
-
- if (m_canvas->HasCapture())
- m_canvas->ReleaseMouse();
+ mouse_up_cleanup();
}
else if (evt.Moving())
{
m_mouse.position = pos.cast();
std::string tooltip = "";
- // updates gizmos overlay
- tooltip = m_gizmos.update_hover_state(*this, m_mouse.position, m_selection);
- if (m_selection.is_empty())
- m_gizmos.reset_all_states();
+ if (tooltip.empty())
+ tooltip = m_gizmos.get_tooltip();
if (tooltip.empty())
tooltip = m_toolbar.get_tooltip();
@@ -3880,6 +2770,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
set_tooltip(tooltip);
+ // updates gizmos overlay
+ if (m_selection.is_empty())
+ m_gizmos.reset_all_states();
+
// Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor hovers over.
if (m_picking_enabled)
m_dirty = true;
@@ -4176,7 +3070,7 @@ void GLCanvas3D::do_mirror()
void GLCanvas3D::set_camera_zoom(float zoom)
{
zoom = std::max(std::min(zoom, 4.0f), -4.0f) / 10.0f;
- zoom = get_camera_zoom() / (1.0f - zoom);
+ zoom = m_camera.zoom / (1.0f - zoom);
// Don't allow to zoom too far outside the scene.
float zoom_min = _get_zoom_to_bounding_box_factor(_max_bounding_box());
@@ -4193,8 +3087,8 @@ void GLCanvas3D::set_camera_zoom(float zoom)
void GLCanvas3D::update_gizmos_on_off_state()
{
set_as_dirty();
- _update_gizmos_data();
- m_gizmos.update_on_off_state(get_selection());
+ m_gizmos.update_data(*this);
+ m_gizmos.refresh_on_off_state(get_selection());
}
void GLCanvas3D::handle_sidebar_focus_event(const std::string& opt_key, bool focus_on)
@@ -4228,6 +3122,13 @@ void GLCanvas3D::update_ui_from_settings()
#endif
}
+Linef3 GLCanvas3D::mouse_ray(const Point& mouse_pos)
+{
+ float z0 = 0.0f;
+ float z1 = 1.0f;
+ return Linef3(_mouse_to_3d(mouse_pos, &z0), _mouse_to_3d(mouse_pos, &z1));
+}
+
bool GLCanvas3D::_is_shown_on_screen() const
{
return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
@@ -4382,7 +3283,7 @@ bool GLCanvas3D::_init_toolbar()
item.sprite_id = 8;
item.is_toggable = true;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); };
- item.visibility_callback = GLToolbarItem::Default_Visibility_Callback;
+ item.visibility_callback = [this]()->bool { return m_process->current_printer_technology() == ptFFF; };
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_layers_editing(); };
if (!m_toolbar.add_item(item))
return false;
@@ -4414,10 +3315,7 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h)
// ensures that this canvas is current
_set_current();
- ::glViewport(0, 0, w, h);
-
- ::glMatrixMode(GL_PROJECTION);
- ::glLoadIdentity();
+ m_camera.apply_viewport(0, 0, w, h);
const BoundingBoxf3& bbox = _max_bounding_box();
@@ -4427,7 +3325,7 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h)
{
float w2 = w;
float h2 = h;
- float two_zoom = 2.0f * get_camera_zoom();
+ float two_zoom = 2.0f * m_camera.zoom;
if (two_zoom != 0.0f)
{
float inv_two_zoom = 1.0f / two_zoom;
@@ -4437,7 +3335,7 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h)
// FIXME: calculate a tighter value for depth will improve z-fighting
float depth = 5.0f * (float)bbox.max_size();
- ::glOrtho(-w2, w2, -h2, h2, -depth, depth);
+ m_camera.apply_ortho_projection(-w2, w2, -h2, h2, -depth, depth);
break;
}
@@ -4470,8 +3368,6 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h)
}
}
- ::glMatrixMode(GL_MODELVIEW);
-
m_dirty = false;
}
@@ -4505,16 +3401,11 @@ float GLCanvas3D::_get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) co
// then calculates the vertices coordinate on this plane along the camera xy axes
// we need the view matrix, we let opengl calculate it (same as done in render())
- _camera_tranform();
+ m_camera.apply_view_matrix();
- // get the view matrix back from opengl
- GLfloat matrix[16];
- ::glGetFloatv(GL_MODELVIEW_MATRIX, matrix);
-
- // camera axes
- Vec3d right((double)matrix[0], (double)matrix[4], (double)matrix[8]);
- Vec3d up((double)matrix[1], (double)matrix[5], (double)matrix[9]);
- Vec3d forward((double)matrix[2], (double)matrix[6], (double)matrix[10]);
+ Vec3d right = m_camera.get_dir_right();
+ Vec3d up = m_camera.get_dir_up();
+ Vec3d forward = m_camera.get_dir_forward();
Vec3d bb_min = bbox.min;
Vec3d bb_max = bbox.max;
@@ -4575,17 +3466,6 @@ void GLCanvas3D::_refresh_if_shown_on_screen()
}
}
-void GLCanvas3D::_camera_tranform() const
-{
- ::glMatrixMode(GL_MODELVIEW);
- ::glLoadIdentity();
-
- ::glRotatef(-m_camera.get_theta(), 1.0f, 0.0f, 0.0f); // pitch
- ::glRotatef(m_camera.phi, 0.0f, 0.0f, 1.0f); // yaw
- Vec3d target = -m_camera.get_target();
- ::glTranslated(target(0), target(1), target(2));
-}
-
void GLCanvas3D::_picking_pass() const
{
const Vec2d& pos = m_mouse.position;
@@ -4597,18 +3477,18 @@ void GLCanvas3D::_picking_pass() const
// Better to use software ray - casting on a bounding - box hierarchy.
if (m_multisample_allowed)
- ::glDisable(GL_MULTISAMPLE);
+ glsafe(::glDisable(GL_MULTISAMPLE));
- ::glDisable(GL_BLEND);
- ::glEnable(GL_DEPTH_TEST);
+ glsafe(::glDisable(GL_BLEND));
+ glsafe(::glEnable(GL_DEPTH_TEST));
- ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
_render_volumes(true);
m_gizmos.render_current_gizmo_for_picking_pass(m_selection);
if (m_multisample_allowed)
- ::glEnable(GL_MULTISAMPLE);
+ glsafe(::glEnable(GL_MULTISAMPLE));
int volume_id = -1;
@@ -4617,7 +3497,7 @@ void GLCanvas3D::_picking_pass() const
bool inside = (0 <= pos(0)) && (pos(0) < cnv_size.get_width()) && (0 <= pos(1)) && (pos(1) < cnv_size.get_height());
if (inside)
{
- ::glReadPixels(pos(0), cnv_size.get_height() - pos(1) - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color);
+ glsafe(::glReadPixels(pos(0), cnv_size.get_height() - pos(1) - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color));
volume_id = color[0] + color[1] * 256 + color[2] * 256 * 256;
}
if ((0 <= volume_id) && (volume_id < (int)m_volumes.volumes.size()))
@@ -4637,14 +3517,14 @@ void GLCanvas3D::_picking_pass() const
void GLCanvas3D::_render_background() const
{
- ::glPushMatrix();
- ::glLoadIdentity();
- ::glMatrixMode(GL_PROJECTION);
- ::glPushMatrix();
- ::glLoadIdentity();
+ glsafe(::glPushMatrix());
+ glsafe(::glLoadIdentity());
+ glsafe(::glMatrixMode(GL_PROJECTION));
+ glsafe(::glPushMatrix());
+ glsafe(::glLoadIdentity());
// Draws a bottom to top gradient over the complete screen.
- ::glDisable(GL_DEPTH_TEST);
+ glsafe(::glDisable(GL_DEPTH_TEST));
::glBegin(GL_QUADS);
if (m_dynamic_background_enabled && _is_any_volume_outside())
@@ -4662,13 +3542,13 @@ void GLCanvas3D::_render_background() const
::glVertex2f(1.0f, 1.0f);
::glVertex2f(-1.0f, 1.0f);
- ::glEnd();
+ glsafe(::glEnd());
- ::glEnable(GL_DEPTH_TEST);
+ glsafe(::glEnable(GL_DEPTH_TEST));
- ::glPopMatrix();
- ::glMatrixMode(GL_MODELVIEW);
- ::glPopMatrix();
+ glsafe(::glPopMatrix());
+ glsafe(::glMatrixMode(GL_MODELVIEW));
+ glsafe(::glPopMatrix());
}
void GLCanvas3D::_render_bed(float theta) const
@@ -4690,8 +3570,8 @@ void GLCanvas3D::_render_objects() const
if (m_volumes.empty())
return;
- ::glEnable(GL_LIGHTING);
- ::glEnable(GL_DEPTH_TEST);
+ glsafe(::glEnable(GL_LIGHTING));
+ glsafe(::glEnable(GL_DEPTH_TEST));
if (m_use_VBOs)
{
@@ -4716,45 +3596,45 @@ void GLCanvas3D::_render_objects() const
m_shader.start_using();
if (m_picking_enabled && m_layers_editing.is_enabled() && m_layers_editing.last_object_id != -1) {
int object_id = m_layers_editing.last_object_id;
- m_volumes.render_VBOs(GLVolumeCollection::Opaque, false, [object_id](const GLVolume &volume) {
+ m_volumes.render_VBOs(GLVolumeCollection::Opaque, false, m_camera.get_view_matrix(), [object_id](const GLVolume &volume) {
// Which volume to paint without the layer height profile shader?
- return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id);
+ return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id);
});
// Let LayersEditing handle rendering of the active object using the layer height profile shader.
m_layers_editing.render_volumes(*this, this->m_volumes);
} else {
// do not cull backfaces to show broken geometry, if any
- m_volumes.render_VBOs(GLVolumeCollection::Opaque, m_picking_enabled, [this](const GLVolume& volume) {
+ m_volumes.render_VBOs(GLVolumeCollection::Opaque, m_picking_enabled, m_camera.get_view_matrix(), [this](const GLVolume& volume) {
return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
});
}
- m_volumes.render_VBOs(GLVolumeCollection::Transparent, false);
+ m_volumes.render_VBOs(GLVolumeCollection::Transparent, false, m_camera.get_view_matrix());
m_shader.stop_using();
}
else
{
if (m_use_clipping_planes)
{
- ::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_clipping_planes[0].get_data());
- ::glEnable(GL_CLIP_PLANE0);
- ::glClipPlane(GL_CLIP_PLANE1, (GLdouble*)m_clipping_planes[1].get_data());
- ::glEnable(GL_CLIP_PLANE1);
+ glsafe(::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_clipping_planes[0].get_data()));
+ glsafe(::glEnable(GL_CLIP_PLANE0));
+ glsafe(::glClipPlane(GL_CLIP_PLANE1, (GLdouble*)m_clipping_planes[1].get_data()));
+ glsafe(::glEnable(GL_CLIP_PLANE1));
}
// do not cull backfaces to show broken geometry, if any
- m_volumes.render_legacy(GLVolumeCollection::Opaque, m_picking_enabled, [this](const GLVolume& volume) {
- return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
- });
- m_volumes.render_legacy(GLVolumeCollection::Transparent, false);
+ m_volumes.render_legacy(GLVolumeCollection::Opaque, m_picking_enabled, m_camera.get_view_matrix(), [this](const GLVolume& volume) {
+ return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
+ });
+ m_volumes.render_legacy(GLVolumeCollection::Transparent, false, m_camera.get_view_matrix());
if (m_use_clipping_planes)
{
- ::glDisable(GL_CLIP_PLANE0);
- ::glDisable(GL_CLIP_PLANE1);
+ glsafe(::glDisable(GL_CLIP_PLANE0));
+ glsafe(::glDisable(GL_CLIP_PLANE1));
}
}
- ::glDisable(GL_LIGHTING);
+ glsafe(::glDisable(GL_LIGHTING));
}
void GLCanvas3D::_render_selection() const
@@ -4794,16 +3674,16 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const
static const GLfloat INV_255 = 1.0f / 255.0f;
if (!fake_colors)
- ::glEnable(GL_LIGHTING);
+ glsafe(::glEnable(GL_LIGHTING));
// do not cull backfaces to show broken geometry, if any
- ::glDisable(GL_CULL_FACE);
+ glsafe(::glDisable(GL_CULL_FACE));
- ::glEnable(GL_BLEND);
- ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glsafe(::glEnable(GL_BLEND));
+ glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
- ::glEnableClientState(GL_VERTEX_ARRAY);
- ::glEnableClientState(GL_NORMAL_ARRAY);
+ glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
+ glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
unsigned int volume_id = 0;
for (GLVolume* vol : m_volumes.volumes)
@@ -4814,12 +3694,12 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const
unsigned int r = (volume_id & 0x000000FF) >> 0;
unsigned int g = (volume_id & 0x0000FF00) >> 8;
unsigned int b = (volume_id & 0x00FF0000) >> 16;
- ::glColor3f((GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255);
+ glsafe(::glColor3f((GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255));
}
else
{
vol->set_render_color();
- ::glColor4fv(vol->render_color);
+ glsafe(::glColor4fv(vol->render_color));
}
if ((!fake_colors || !vol->disabled) && (vol->composite_id.volume_id >= 0 || m_render_sla_auxiliaries))
@@ -4828,14 +3708,14 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const
++volume_id;
}
- ::glDisableClientState(GL_NORMAL_ARRAY);
- ::glDisableClientState(GL_VERTEX_ARRAY);
- ::glDisable(GL_BLEND);
+ glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
+ glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
+ glsafe(::glDisable(GL_BLEND));
- ::glEnable(GL_CULL_FACE);
+ glsafe(::glEnable(GL_CULL_FACE));
if (!fake_colors)
- ::glDisable(GL_LIGHTING);
+ glsafe(::glDisable(GL_LIGHTING));
}
void GLCanvas3D::_render_current_gizmo() const
@@ -4864,7 +3744,7 @@ void GLCanvas3D::_render_toolbar() const
#endif // ENABLE_RETINA_GL
Size cnv_size = get_canvas_size();
- float zoom = get_camera_zoom();
+ float zoom = m_camera.zoom;
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
GLToolbar::Layout::EOrientation orientation = m_toolbar.get_layout_orientation();
@@ -4927,7 +3807,7 @@ void GLCanvas3D::_render_view_toolbar() const
#endif // ENABLE_RETINA_GL
Size cnv_size = get_canvas_size();
- float zoom = get_camera_zoom();
+ float zoom = m_camera.zoom;
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
// places the toolbar on the bottom-left corner of the 3d scene
@@ -4949,9 +3829,9 @@ void GLCanvas3D::_render_camera_target() const
{
double half_length = 5.0;
- ::glDisable(GL_DEPTH_TEST);
+ glsafe(::glDisable(GL_DEPTH_TEST));
- ::glLineWidth(2.0f);
+ glsafe(::glLineWidth(2.0f));
::glBegin(GL_LINES);
const Vec3d& target = m_camera.get_target();
// draw line for x axis
@@ -4966,7 +3846,7 @@ void GLCanvas3D::_render_camera_target() const
::glColor3f(0.0f, 0.0f, 1.0f);
::glVertex3d(target(0), target(1), target(2) - half_length);
::glVertex3d(target(0), target(1), target(2) + half_length);
- ::glEnd();
+ glsafe(::glEnd());
}
#endif // ENABLE_SHOW_CAMERA_TARGET
@@ -5059,33 +3939,33 @@ void GLCanvas3D::_render_sla_slices() const
{
for (const SLAPrintObject::Instance& inst : obj->instances())
{
- ::glPushMatrix();
- ::glTranslated(unscale(inst.shift.x()), unscale(inst.shift.y()), 0);
- ::glRotatef(Geometry::rad2deg(inst.rotation), 0.0, 0.0, 1.0);
+ glsafe(::glPushMatrix());
+ glsafe(::glTranslated(unscale(inst.shift.x()), unscale(inst.shift.y()), 0));
+ glsafe(::glRotatef(Geometry::rad2deg(inst.rotation), 0.0, 0.0, 1.0));
if (obj->is_left_handed())
// The polygons are mirrored by X.
- ::glScalef(-1.0, 1.0, 1.0);
- ::glEnableClientState(GL_VERTEX_ARRAY);
- ::glColor3f(1.0f, 0.37f, 0.0f);
+ glsafe(::glScalef(-1.0, 1.0, 1.0));
+ glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
+ glsafe(::glColor3f(1.0f, 0.37f, 0.0f));
if (!bottom_obj_triangles.empty()) {
- ::glVertexPointer(3, GL_DOUBLE, 0, (GLdouble*)bottom_obj_triangles.front().data());
- ::glDrawArrays(GL_TRIANGLES, 0, bottom_obj_triangles.size());
+ glsafe(::glVertexPointer(3, GL_DOUBLE, 0, (GLdouble*)bottom_obj_triangles.front().data()));
+ glsafe(::glDrawArrays(GL_TRIANGLES, 0, bottom_obj_triangles.size()));
}
if (! top_obj_triangles.empty()) {
- ::glVertexPointer(3, GL_DOUBLE, 0, (GLdouble*)top_obj_triangles.front().data());
- ::glDrawArrays(GL_TRIANGLES, 0, top_obj_triangles.size());
+ glsafe(::glVertexPointer(3, GL_DOUBLE, 0, (GLdouble*)top_obj_triangles.front().data()));
+ glsafe(::glDrawArrays(GL_TRIANGLES, 0, top_obj_triangles.size()));
}
- ::glColor3f(1.0f, 0.0f, 0.37f);
+ glsafe(::glColor3f(1.0f, 0.0f, 0.37f));
if (! bottom_sup_triangles.empty()) {
- ::glVertexPointer(3, GL_DOUBLE, 0, (GLdouble*)bottom_sup_triangles.front().data());
- ::glDrawArrays(GL_TRIANGLES, 0, bottom_sup_triangles.size());
+ glsafe(::glVertexPointer(3, GL_DOUBLE, 0, (GLdouble*)bottom_sup_triangles.front().data()));
+ glsafe(::glDrawArrays(GL_TRIANGLES, 0, bottom_sup_triangles.size()));
}
if (! top_sup_triangles.empty()) {
- ::glVertexPointer(3, GL_DOUBLE, 0, (GLdouble*)top_sup_triangles.front().data());
- ::glDrawArrays(GL_TRIANGLES, 0, top_sup_triangles.size());
+ glsafe(::glVertexPointer(3, GL_DOUBLE, 0, (GLdouble*)top_sup_triangles.front().data()));
+ glsafe(::glDrawArrays(GL_TRIANGLES, 0, top_sup_triangles.size()));
}
- ::glDisableClientState(GL_VERTEX_ARRAY);
- ::glPopMatrix();
+ glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
+ glsafe(::glPopMatrix());
}
}
}
@@ -5137,46 +4017,6 @@ void GLCanvas3D::_update_volumes_hover_state() const
}
}
-void GLCanvas3D::_update_gizmos_data()
-{
- if (!m_gizmos.is_enabled())
- return;
-
- bool enable_move_z = !m_selection.is_wipe_tower();
- m_gizmos.enable_grabber(Gizmos::Move, 2, enable_move_z);
- bool enable_scale_xyz = m_selection.is_single_full_instance() || m_selection.is_single_volume() || m_selection.is_single_modifier();
- for (int i = 0; i < 6; ++i)
- {
- m_gizmos.enable_grabber(Gizmos::Scale, i, enable_scale_xyz);
- }
-
- if (m_selection.is_single_full_instance())
- {
- // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first
- const GLVolume* volume = m_volumes.volumes[*m_selection.get_volume_idxs().begin()];
- m_gizmos.set_scale(volume->get_instance_scaling_factor());
- m_gizmos.set_rotation(Vec3d::Zero());
- ModelObject* model_object = m_model->objects[m_selection.get_object_idx()];
- m_gizmos.set_flattening_data(model_object);
- m_gizmos.set_sla_support_data(model_object, m_selection);
- }
- else if (m_selection.is_single_volume() || m_selection.is_single_modifier())
- {
- const GLVolume* volume = m_volumes.volumes[*m_selection.get_volume_idxs().begin()];
- m_gizmos.set_scale(volume->get_volume_scaling_factor());
- m_gizmos.set_rotation(Vec3d::Zero());
- m_gizmos.set_flattening_data(nullptr);
- m_gizmos.set_sla_support_data(nullptr, m_selection);
- }
- else
- {
- m_gizmos.set_scale(Vec3d::Ones());
- m_gizmos.set_rotation(Vec3d::Zero());
- m_gizmos.set_flattening_data(m_selection.is_from_single_object() ? m_model->objects[m_selection.get_object_idx()] : nullptr);
- m_gizmos.set_sla_support_data(nullptr, m_selection);
- }
-}
-
void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt)
{
int object_idx_selected = m_layers_editing.last_object_id;
@@ -5206,24 +4046,20 @@ Vec3d GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z)
if (m_canvas == nullptr)
return Vec3d(DBL_MAX, DBL_MAX, DBL_MAX);
- _camera_tranform();
- GLint viewport[4];
- ::glGetIntegerv(GL_VIEWPORT, viewport);
- GLdouble modelview_matrix[16];
- ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix);
- GLdouble projection_matrix[16];
- ::glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix);
+ const std::array& viewport = m_camera.get_viewport();
+ const Transform3d& modelview_matrix = m_camera.get_view_matrix();
+ const Transform3d& projection_matrix = m_camera.get_projection_matrix();
GLint y = viewport[3] - (GLint)mouse_pos(1);
GLfloat mouse_z;
if (z == nullptr)
- ::glReadPixels((GLint)mouse_pos(0), y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, (void*)&mouse_z);
+ glsafe(::glReadPixels((GLint)mouse_pos(0), y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, (void*)&mouse_z));
else
mouse_z = *z;
GLdouble out_x, out_y, out_z;
- ::gluUnProject((GLdouble)mouse_pos(0), (GLdouble)y, (GLdouble)mouse_z, modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z);
+ ::gluUnProject((GLdouble)mouse_pos(0), (GLdouble)y, (GLdouble)mouse_z, (GLdouble*)modelview_matrix.data(), (GLdouble*)projection_matrix.data(), (GLint*)viewport.data(), &out_x, &out_y, &out_z);
return Vec3d((double)out_x, (double)out_y, (double)out_z);
}
@@ -5232,13 +4068,6 @@ Vec3d GLCanvas3D::_mouse_to_bed_3d(const Point& mouse_pos)
return mouse_ray(mouse_pos).intersect_plane(0.0);
}
-Linef3 GLCanvas3D::mouse_ray(const Point& mouse_pos)
-{
- float z0 = 0.0f;
- float z1 = 1.0f;
- return Linef3(_mouse_to_3d(mouse_pos, &z0), _mouse_to_3d(mouse_pos, &z1));
-}
-
void GLCanvas3D::_start_timer()
{
m_timer.Start(100, wxTIMER_CONTINUOUS);
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 7e414d52a8..53551a4728 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -10,6 +10,7 @@
#include "3DBed.hpp"
#include "Camera.hpp"
#include "Selection.hpp"
+#include "Gizmos/GLGizmosManager.hpp"
#include
@@ -64,33 +65,6 @@ public:
void set_scale_factor(int height);
};
-class Rect
-{
- float m_left;
- float m_top;
- float m_right;
- float m_bottom;
-
-public:
- Rect();
- Rect(float left, float top, float right, float bottom);
-
- float get_left() const;
- void set_left(float left);
-
- float get_top() const;
- void set_top(float top);
-
- float get_right() const;
- void set_right(float right);
-
- float get_bottom() const;
- void set_bottom(float bottom);
-
- float get_width() const { return m_right - m_left; }
- float get_height() const { return m_top - m_bottom; }
-};
-
wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
using Vec2dEvent = Event;
@@ -118,22 +92,6 @@ wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent);
-// this describes events being passed from GLCanvas3D to SlaSupport gizmo
-enum class SLAGizmoEventType {
- LeftDown = 1,
- LeftUp,
- RightDown,
- Dragging,
- Delete,
- SelectAll,
- ShiftUp,
- ApplyChanges,
- DiscardChanges,
- AutomaticGeneration,
- ManualEditing
-};
-
-
class GLCanvas3D
{
struct GCodePreviewVolumeIndex
@@ -314,7 +272,6 @@ class GLCanvas3D
Vec2d position;
Vec3d scene_position;
Drag drag;
- bool ignore_up_event;
Mouse();
@@ -358,118 +315,6 @@ public:
};
private:
- class Gizmos
- {
- public:
-#if ENABLE_SVG_ICONS
- static const float Default_Icons_Size;
-#endif // ENABLE_SVG_ICONS
-
- enum EType : unsigned char
- {
- Undefined,
- Move,
- Scale,
- Rotate,
- Flatten,
- Cut,
- SlaSupports,
- Num_Types
- };
-
- private:
- bool m_enabled;
- typedef std::map GizmosMap;
- GizmosMap m_gizmos;
-#if ENABLE_SVG_ICONS
- mutable GLTexture m_icons_texture;
- mutable bool m_icons_texture_dirty;
-#else
- ItemsIconsTexture m_icons_texture;
-#endif // ENABLE_SVG_ICONS
- BackgroundTexture m_background_texture;
- EType m_current;
-
-#if ENABLE_SVG_ICONS
- float m_overlay_icons_size;
- float m_overlay_scale;
-#else
- float m_overlay_icons_scale;
-#endif // ENABLE_SVG_ICONS
- float m_overlay_border;
- float m_overlay_gap_y;
-
- public:
- Gizmos();
- ~Gizmos();
-
- bool init(GLCanvas3D& parent);
-
- bool is_enabled() const;
- void set_enabled(bool enable);
-
-#if ENABLE_SVG_ICONS
- void set_overlay_icon_size(float size);
-#endif // ENABLE_SVG_ICONS
- void set_overlay_scale(float scale);
-
- std::string update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection);
- void update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection);
- void update_on_off_state(const Selection& selection);
- void reset_all_states();
-
- void set_hover_id(int id);
- void enable_grabber(EType type, unsigned int id, bool enable);
-
- bool overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const;
- bool grabber_contains_mouse() const;
- void update(const Linef3& mouse_ray, const Selection& selection, bool shift_down, const Point* mouse_pos = nullptr);
- Rect get_reset_rect_viewport(const GLCanvas3D& canvas) const;
- EType get_current_type() const;
-
- bool is_running() const;
- bool handle_shortcut(int key, const Selection& selection);
-
- bool is_dragging() const;
- void start_dragging(const Selection& selection);
- void stop_dragging();
-
- Vec3d get_displacement() const;
-
- Vec3d get_scale() const;
- void set_scale(const Vec3d& scale);
-
- Vec3d get_rotation() const;
- void set_rotation(const Vec3d& rotation);
-
- Vec3d get_flattening_normal() const;
-
- void set_flattening_data(const ModelObject* model_object);
-
- void set_sla_support_data(ModelObject* model_object, const Selection& selection);
- bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false);
-
- void render_current_gizmo(const Selection& selection) const;
- void render_current_gizmo_for_picking_pass(const Selection& selection) const;
-
- void render_overlay(const GLCanvas3D& canvas, const Selection& selection) const;
-
- private:
- void reset();
-
- void do_render_overlay(const GLCanvas3D& canvas, const Selection& selection) const;
- void do_render_current_gizmo(const Selection& selection) const;
-
- float get_total_overlay_height() const;
- float get_total_overlay_width() const;
-
- GLGizmoBase* get_current() const;
-
-#if ENABLE_SVG_ICONS
- bool generate_icons_texture() const;
-#endif // ENABLE_SVG_ICONS
- };
-
struct SlaCap
{
struct Triangles
@@ -557,7 +402,7 @@ private:
LayersEditing m_layers_editing;
Shader m_shader;
Mouse m_mouse;
- mutable Gizmos m_gizmos;
+ mutable GLGizmosManager m_gizmos;
mutable GLToolbar m_toolbar;
ClippingPlane m_clipping_planes[2];
bool m_use_clipping_planes;
@@ -639,7 +484,7 @@ public:
void set_color_by(const std::string& value);
- float get_camera_zoom() const;
+ const Camera& get_camera() const { return m_camera; }
BoundingBoxf3 volumes_bounding_box() const;
BoundingBoxf3 scene_bounding_box() const;
@@ -721,6 +566,19 @@ public:
void update_ui_from_settings();
+ float get_view_toolbar_height() const { return m_view_toolbar.get_height(); }
+
+ int get_move_volume_id() const { return m_mouse.drag.move_volume_idx; }
+ int get_hover_volume_id() const { return m_hover_volume_id; }
+
+ // Returns the view ray line, in world coordinate, at the given mouse position.
+ Linef3 mouse_ray(const Point& mouse_pos);
+
+ void set_mouse_as_dragging() { m_mouse.dragging = true; }
+ void disable_regenerate_volumes() { m_regenerate_volumes = false; }
+ void refresh_camera_scene_box() { m_camera.set_scene_box(scene_bounding_box()); }
+ bool is_mouse_dragging() const { return m_mouse.dragging; }
+
private:
bool _is_shown_on_screen() const;
@@ -736,7 +594,6 @@ private:
void _refresh_if_shown_on_screen();
- void _camera_tranform() const;
void _picking_pass() const;
void _render_background() const;
void _render_bed(float theta) const;
@@ -760,7 +617,6 @@ private:
void _render_selection_sidebar_hints() const;
void _update_volumes_hover_state() const;
- void _update_gizmos_data();
void _perform_layer_editing_action(wxMouseEvent* evt = nullptr);
@@ -771,9 +627,6 @@ private:
// Convert the screen space coordinate to world coordinate on the bed.
Vec3d _mouse_to_bed_3d(const Point& mouse_pos);
- // Returns the view ray line, in world coordinate, at the given mouse position.
- Linef3 mouse_ray(const Point& mouse_pos);
-
void _start_timer();
void _stop_timer();
diff --git a/src/slic3r/GUI/GLShader.cpp b/src/slic3r/GUI/GLShader.cpp
index f401f54662..7f3bc0893a 100644
--- a/src/slic3r/GUI/GLShader.cpp
+++ b/src/slic3r/GUI/GLShader.cpp
@@ -3,6 +3,7 @@
#include "GLShader.hpp"
#include "libslic3r/Utils.hpp"
+#include "3DScene.hpp"
#include
#include
@@ -52,21 +53,22 @@ bool GLShader::load_from_text(const char *fragment_shader, const char *vertex_sh
}
if (fragment_shader != nullptr) {
- this->fragment_program_id = glCreateShader(GL_FRAGMENT_SHADER);
+ this->fragment_program_id = ::glCreateShader(GL_FRAGMENT_SHADER);
+ glcheck();
if (this->fragment_program_id == 0) {
last_error = "glCreateShader(GL_FRAGMENT_SHADER) failed.";
return false;
}
GLint len = (GLint)strlen(fragment_shader);
- glShaderSource(this->fragment_program_id, 1, &fragment_shader, &len);
- glCompileShader(this->fragment_program_id);
+ glsafe(::glShaderSource(this->fragment_program_id, 1, &fragment_shader, &len));
+ glsafe(::glCompileShader(this->fragment_program_id));
GLint params;
- glGetShaderiv(this->fragment_program_id, GL_COMPILE_STATUS, ¶ms);
+ glsafe(::glGetShaderiv(this->fragment_program_id, GL_COMPILE_STATUS, ¶ms));
if (params == GL_FALSE) {
// Compilation failed. Get the log.
- glGetShaderiv(this->fragment_program_id, GL_INFO_LOG_LENGTH, ¶ms);
+ glsafe(::glGetShaderiv(this->fragment_program_id, GL_INFO_LOG_LENGTH, ¶ms));
std::vector msg(params);
- glGetShaderInfoLog(this->fragment_program_id, params, ¶ms, msg.data());
+ glsafe(::glGetShaderInfoLog(this->fragment_program_id, params, ¶ms, msg.data()));
this->last_error = std::string("Fragment shader compilation failed:\n") + msg.data();
this->release();
return false;
@@ -74,22 +76,23 @@ bool GLShader::load_from_text(const char *fragment_shader, const char *vertex_sh
}
if (vertex_shader != nullptr) {
- this->vertex_program_id = glCreateShader(GL_VERTEX_SHADER);
+ this->vertex_program_id = ::glCreateShader(GL_VERTEX_SHADER);
+ glcheck();
if (this->vertex_program_id == 0) {
last_error = "glCreateShader(GL_VERTEX_SHADER) failed.";
this->release();
return false;
}
GLint len = (GLint)strlen(vertex_shader);
- glShaderSource(this->vertex_program_id, 1, &vertex_shader, &len);
- glCompileShader(this->vertex_program_id);
+ glsafe(::glShaderSource(this->vertex_program_id, 1, &vertex_shader, &len));
+ glsafe(::glCompileShader(this->vertex_program_id));
GLint params;
- glGetShaderiv(this->vertex_program_id, GL_COMPILE_STATUS, ¶ms);
+ glsafe(::glGetShaderiv(this->vertex_program_id, GL_COMPILE_STATUS, ¶ms));
if (params == GL_FALSE) {
// Compilation failed. Get the log.
- glGetShaderiv(this->vertex_program_id, GL_INFO_LOG_LENGTH, ¶ms);
+ glsafe(::glGetShaderiv(this->vertex_program_id, GL_INFO_LOG_LENGTH, ¶ms));
std::vector msg(params);
- glGetShaderInfoLog(this->vertex_program_id, params, ¶ms, msg.data());
+ glsafe(::glGetShaderInfoLog(this->vertex_program_id, params, ¶ms, msg.data()));
this->last_error = std::string("Vertex shader compilation failed:\n") + msg.data();
this->release();
return false;
@@ -97,7 +100,8 @@ bool GLShader::load_from_text(const char *fragment_shader, const char *vertex_sh
}
// Link shaders
- this->shader_program_id = glCreateProgram();
+ this->shader_program_id = ::glCreateProgram();
+ glcheck();
if (this->shader_program_id == 0) {
last_error = "glCreateProgram() failed.";
this->release();
@@ -105,18 +109,18 @@ bool GLShader::load_from_text(const char *fragment_shader, const char *vertex_sh
}
if (this->fragment_program_id)
- glAttachShader(this->shader_program_id, this->fragment_program_id);
+ glsafe(::glAttachShader(this->shader_program_id, this->fragment_program_id));
if (this->vertex_program_id)
- glAttachShader(this->shader_program_id, this->vertex_program_id);
- glLinkProgram(this->shader_program_id);
+ glsafe(::glAttachShader(this->shader_program_id, this->vertex_program_id));
+ glsafe(::glLinkProgram(this->shader_program_id));
GLint params;
- glGetProgramiv(this->shader_program_id, GL_LINK_STATUS, ¶ms);
+ glsafe(::glGetProgramiv(this->shader_program_id, GL_LINK_STATUS, ¶ms));
if (params == GL_FALSE) {
// Linking failed. Get the log.
- glGetProgramiv(this->vertex_program_id, GL_INFO_LOG_LENGTH, ¶ms);
+ glsafe(::glGetProgramiv(this->vertex_program_id, GL_INFO_LOG_LENGTH, ¶ms));
std::vector msg(params);
- glGetProgramInfoLog(this->vertex_program_id, params, ¶ms, msg.data());
+ glsafe(::glGetProgramInfoLog(this->vertex_program_id, params, ¶ms, msg.data()));
this->last_error = std::string("Shader linking failed:\n") + msg.data();
this->release();
return false;
@@ -165,31 +169,31 @@ void GLShader::release()
{
if (this->shader_program_id) {
if (this->vertex_program_id)
- glDetachShader(this->shader_program_id, this->vertex_program_id);
+ glsafe(::glDetachShader(this->shader_program_id, this->vertex_program_id));
if (this->fragment_program_id)
- glDetachShader(this->shader_program_id, this->fragment_program_id);
- glDeleteProgram(this->shader_program_id);
+ glsafe(::glDetachShader(this->shader_program_id, this->fragment_program_id));
+ glsafe(::glDeleteProgram(this->shader_program_id));
this->shader_program_id = 0;
}
if (this->vertex_program_id) {
- glDeleteShader(this->vertex_program_id);
+ glsafe(::glDeleteShader(this->vertex_program_id));
this->vertex_program_id = 0;
}
if (this->fragment_program_id) {
- glDeleteShader(this->fragment_program_id);
+ glsafe(::glDeleteShader(this->fragment_program_id));
this->fragment_program_id = 0;
}
}
void GLShader::enable() const
{
- glUseProgram(this->shader_program_id);
+ glsafe(::glUseProgram(this->shader_program_id));
}
void GLShader::disable() const
{
- glUseProgram(0);
+ glsafe(::glUseProgram(0));
}
// Return shader vertex attribute ID
@@ -208,7 +212,7 @@ bool GLShader::set_uniform(const char *name, float value) const
{
int id = this->get_uniform_location(name);
if (id >= 0) {
- glUniform1fARB(id, value);
+ glsafe(::glUniform1fARB(id, value));
return true;
}
return false;
@@ -219,7 +223,7 @@ bool GLShader::set_uniform(const char* name, const float* matrix) const
int id = get_uniform_location(name);
if (id >= 0)
{
- ::glUniformMatrix4fv(id, 1, GL_FALSE, (const GLfloat*)matrix);
+ glsafe(::glUniformMatrix4fv(id, 1, GL_FALSE, (const GLfloat*)matrix));
return true;
}
return false;
@@ -230,7 +234,7 @@ bool GLShader::set_uniform(const char* name, int value) const
int id = get_uniform_location(name);
if (id >= 0)
{
- ::glUniform1i(id, value);
+ glsafe(::glUniform1i(id, value));
return true;
}
return false;
diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp
index b48ca20448..2fff0869ad 100644
--- a/src/slic3r/GUI/GLTexture.cpp
+++ b/src/slic3r/GUI/GLTexture.cpp
@@ -1,6 +1,8 @@
#include "libslic3r/libslic3r.h"
#include "GLTexture.hpp"
+#include "3DScene.hpp"
+
#include
#include
@@ -11,15 +13,11 @@
#include
#include
-#define NANOSVG_IMPLEMENTATION
#include "nanosvg/nanosvg.h"
-#define NANOSVGRAST_IMPLEMENTATION
#include "nanosvg/nanosvgrast.h"
#include "libslic3r/Utils.hpp"
-#include "libslic3r/Utils.hpp"
-
namespace Slic3r {
namespace GUI {
@@ -177,15 +175,15 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vectoris_separator())
{
// mouse is inside an icon
do_action((unsigned int)item_id, parent);
- processed = true;
}
}
else if (evt.MiddleDown())
@@ -612,7 +612,7 @@ std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLC
{
// NB: mouse_pos is already scaled appropriately
- float zoom = parent.get_camera_zoom();
+ float zoom = parent.get_camera().zoom;
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = m_layout.scale * inv_zoom;
@@ -717,7 +717,7 @@ std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCan
{
// NB: mouse_pos is already scaled appropriately
- float zoom = parent.get_camera_zoom();
+ float zoom = parent.get_camera().zoom;
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = m_layout.scale * inv_zoom;
@@ -834,7 +834,7 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3
{
// NB: mouse_pos is already scaled appropriately
- float zoom = parent.get_camera_zoom();
+ float zoom = parent.get_camera().zoom;
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = m_layout.scale * inv_zoom;
@@ -917,7 +917,7 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D&
{
// NB: mouse_pos is already scaled appropriately
- float zoom = parent.get_camera_zoom();
+ float zoom = parent.get_camera().zoom;
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = m_layout.scale * inv_zoom;
@@ -1013,7 +1013,7 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const
return;
#endif // !ENABLE_SVG_ICONS
- float zoom = parent.get_camera_zoom();
+ float zoom = parent.get_camera().zoom;
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = inv_zoom * m_layout.scale;
@@ -1168,7 +1168,7 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent) const
return;
#endif // !ENABLE_SVG_ICONS
- float zoom = parent.get_camera_zoom();
+ float zoom = parent.get_camera().zoom;
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = inv_zoom * m_layout.scale;
diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp
index 5fac1f5b2e..0f8b17e04f 100644
--- a/src/slic3r/GUI/GLToolbar.hpp
+++ b/src/slic3r/GUI/GLToolbar.hpp
@@ -7,6 +7,7 @@
#include "GLTexture.hpp"
#include "Event.hpp"
+#include "libslic3r/Point.hpp"
class wxEvtHandler;
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index f1a7d92632..8c34337dcb 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -76,6 +76,24 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension)
static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); }
+static void register_dpi_event()
+{
+#ifdef WIN32
+ enum { WM_DPICHANGED_ = 0x02e0 };
+
+ wxWindow::MSWRegisterMessageHandler(WM_DPICHANGED_, [](wxWindow *win, WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) {
+ const int dpi = wParam & 0xffff;
+ const auto rect = reinterpret_cast(lParam);
+ const wxRect wxrect(wxPoint(rect->top, rect->left), wxPoint(rect->bottom, rect->right));
+
+ DpiChangedEvent evt(EVT_DPI_CHANGED, dpi, wxrect);
+ win->GetEventHandler()->AddPendingEvent(evt);
+
+ return true;
+ });
+#endif
+}
+
IMPLEMENT_APP(GUI_App)
GUI_App::GUI_App()
@@ -149,6 +167,8 @@ bool GUI_App::OnInit()
show_error(nullptr, ex.what());
}
+ register_dpi_event();
+
// Let the libslic3r know the callback, which will translate messages on demand.
Slic3r::I18N::set_translate_callback(libslic3r_translate_callback);
diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index c0dcc659d9..37d30bbe2b 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -62,18 +62,18 @@ ObjectList::ObjectList(wxWindow* parent) :
// Fill CATEGORY_ICON
{
// ptFFF
- CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap("layers.png"); // wxBitmap(from_u8(var("layers.png")), wxBITMAP_TYPE_PNG);
- CATEGORY_ICON[L("Infill")] = create_scaled_bitmap("infill.png"); // wxBitmap(from_u8(var("infill.png")), wxBITMAP_TYPE_PNG);
- CATEGORY_ICON[L("Support material")] = create_scaled_bitmap("building.png"); // wxBitmap(from_u8(var("building.png")), wxBITMAP_TYPE_PNG);
- CATEGORY_ICON[L("Speed")] = create_scaled_bitmap("time.png"); // wxBitmap(from_u8(var("time.png")), wxBITMAP_TYPE_PNG);
- CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap("funnel.png"); // wxBitmap(from_u8(var("funnel.png")), wxBITMAP_TYPE_PNG);
- CATEGORY_ICON[L("Extrusion Width")] = create_scaled_bitmap("funnel.png"); // wxBitmap(from_u8(var("funnel.png")), wxBITMAP_TYPE_PNG);
-// CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap("box.png"); // wxBitmap(from_u8(var("box.png")), wxBITMAP_TYPE_PNG);
-// CATEGORY_ICON[L("Speed > Acceleration")] = create_scaled_bitmap("time.png"); // wxBitmap(from_u8(var("time.png")), wxBITMAP_TYPE_PNG);
- CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap("wand.png"); // wxBitmap(from_u8(var("wand.png")), wxBITMAP_TYPE_PNG);
+ CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap("layers");
+ CATEGORY_ICON[L("Infill")] = create_scaled_bitmap("infill");
+ CATEGORY_ICON[L("Support material")] = create_scaled_bitmap("support");
+ CATEGORY_ICON[L("Speed")] = create_scaled_bitmap("time");
+ CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap("funnel");
+ CATEGORY_ICON[L("Extrusion Width")] = create_scaled_bitmap("funnel");
+// CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap("skirt+brim");
+// CATEGORY_ICON[L("Speed > Acceleration")] = create_scaled_bitmap("time");
+ CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap("wrench");
// ptSLA
- CATEGORY_ICON[L("Supports")] = create_scaled_bitmap("building.png"); // wxBitmap(from_u8(var("building.png")), wxBITMAP_TYPE_PNG);
- CATEGORY_ICON[L("Pad")] = create_scaled_bitmap("brick.png"); // wxBitmap(from_u8(var("brick.png")), wxBITMAP_TYPE_PNG);
+ CATEGORY_ICON[L("Supports")] = create_scaled_bitmap("sla_supports");
+ CATEGORY_ICON[L("Pad")] = create_scaled_bitmap("brick.png");
}
// create control
@@ -82,13 +82,35 @@ ObjectList::ObjectList(wxWindow* parent) :
init_icons();
// describe control behavior
- Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxEvent& event) {
+ Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxDataViewEvent& event) {
#ifndef __APPLE__
// On Windows and Linux, forces a kill focus emulation on the object manipulator fields because this event handler is called
// before the kill focus event handler on the object manipulator when changing selection in the list, invalidating the object
// manipulator cache with the following call to selection_changed()
wxGetApp().obj_manipul()->emulate_kill_focus();
+
+ // To avoid selection update from SetSelection() and UnselectAll() under osx
+ if (m_prevent_list_events)
+ return;
#endif // __APPLE__
+
+ /* For multiple selection with pressed SHIFT,
+ * event.GetItem() returns value of a first item in selection list
+ * instead of real last clicked item.
+ * So, let check last selected item in such strange way
+ */
+ if (wxGetKeyState(WXK_SHIFT))
+ {
+ wxDataViewItemArray sels;
+ GetSelections(sels);
+ if (sels.front() == m_last_selected_item)
+ m_last_selected_item = sels.back();
+ else
+ m_last_selected_item = event.GetItem();
+ }
+ else
+ m_last_selected_item = event.GetItem();
+
selection_changed();
#ifndef __WXMSW__
set_tooltip_for_item(get_mouse_position_in_control());
@@ -368,13 +390,6 @@ void ObjectList::update_name_in_model(const wxDataViewItem& item) const
void ObjectList::init_icons()
{
-// m_bmp_modifiermesh = wxBitmap(from_u8(var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG);
-// m_bmp_solidmesh = wxBitmap(from_u8(var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG);
-
-// m_bmp_support_enforcer = wxBitmap(from_u8(var("support_enforcer_.png")), wxBITMAP_TYPE_PNG);
-// m_bmp_support_blocker = wxBitmap(from_u8(var("support_blocker_.png")), wxBITMAP_TYPE_PNG);
-
-
m_bmp_modifiermesh = create_scaled_bitmap("lambda.png");
m_bmp_solidmesh = create_scaled_bitmap("object.png");
m_bmp_support_enforcer = create_scaled_bitmap("support_enforcer_.png");
@@ -389,16 +404,13 @@ void ObjectList::init_icons()
m_objects_model->SetVolumeBitmaps(m_bmp_vector);
// init icon for manifold warning
-// m_bmp_manifold_warning = wxBitmap(from_u8(var("exclamation_mark_.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG);
m_bmp_manifold_warning = create_scaled_bitmap("exclamation_mark_.png");
// init bitmap for "Split to sub-objects" context menu
-// m_bmp_split = wxBitmap(from_u8(var("split.png")), wxBITMAP_TYPE_PNG);
- m_bmp_split = create_scaled_bitmap("split.png");
+ m_bmp_split = create_scaled_bitmap("split_parts");
// init bitmap for "Add Settings" context menu
-// m_bmp_cog = wxBitmap(from_u8(var("cog.png")), wxBITMAP_TYPE_PNG);
- m_bmp_cog = create_scaled_bitmap("cog.png");
+ m_bmp_cog = create_scaled_bitmap("cog");
}
@@ -509,7 +521,7 @@ void ObjectList::key_event(wxKeyEvent& event)
) {
remove();
}
- else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_SHIFT))
+ else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL/*WXK_SHIFT*/))
select_item_all_children();
else
event.Skip();
@@ -1005,8 +1017,7 @@ wxMenuItem* ObjectList::append_menu_item_instance_to_object(wxMenu* menu)
void ObjectList::append_menu_items_osx(wxMenu* menu)
{
- append_menu_item(menu, wxID_ANY, _(L("Delete item")), "",
- [this](wxCommandEvent&) { remove(); }, "", menu);
+ append_menu_item_delete(menu);
append_menu_item(menu, wxID_ANY, _(L("Rename")), "",
[this](wxCommandEvent&) { rename_item(); }, "", menu);
@@ -1025,7 +1036,7 @@ void ObjectList::append_menu_item_fix_through_netfabb(wxMenu* menu)
void ObjectList::append_menu_item_export_stl(wxMenu* menu) const
{
- append_menu_item(menu, wxID_ANY, _(L("Export object as STL")) + dots, "",
+ append_menu_item(menu, wxID_ANY, _(L("Export as STL")) + dots, "",
[](wxCommandEvent&) { wxGetApp().plater()->export_stl(true); }, "", menu);
menu->AppendSeparator();
}
@@ -1061,6 +1072,12 @@ void ObjectList::append_menu_item_change_extruder(wxMenu* menu) const
}
}
+void ObjectList::append_menu_item_delete(wxMenu* menu)
+{
+ append_menu_item(menu, wxID_ANY, _(L("Delete")), "",
+ [this](wxCommandEvent&) { remove(); }, "", menu);
+}
+
void ObjectList::create_object_popupmenu(wxMenu *menu)
{
#ifdef __WXOSX__
@@ -1101,6 +1118,7 @@ void ObjectList::create_part_popupmenu(wxMenu *menu)
#endif // __WXOSX__
append_menu_item_fix_through_netfabb(menu);
+ append_menu_item_export_stl(menu);
m_menu_item_split_part = append_menu_item_split(menu);
@@ -1117,10 +1135,21 @@ void ObjectList::create_part_popupmenu(wxMenu *menu)
void ObjectList::create_instance_popupmenu(wxMenu*menu)
{
+#ifdef __WXOSX__
+ append_menu_item_delete(menu);
+#endif // __WXOSX__
m_menu_item_split_instances = append_menu_item_instance_to_object(menu);
+ /* New behavior logic:
+ * 1. Split Object to several separated object, if ALL instances are selected
+ * 2. Separate selected instances from the initial object to the separated object,
+ * if some (not all) instances are selected
+ */
wxGetApp().plater()->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) {
- evt.Enable(can_split_instances()); }, m_menu_item_split_instances->GetId());
+// evt.Enable(can_split_instances()); }, m_menu_item_split_instances->GetId());
+ evt.SetText(wxGetApp().plater()->canvas3D()->get_selection().is_single_full_object() ?
+ _(L("Set as a Separated Objects")) : _(L("Set as a Separated Object")));
+ }, m_menu_item_split_instances->GetId());
}
wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu)
@@ -1551,7 +1580,7 @@ void ObjectList::split()
auto opt_keys = model_object->volumes[id]->config.keys();
if ( !(opt_keys.size() == 1 && opt_keys[0] == "extruder") ) {
select_item(m_objects_model->AddSettingsChild(vol_item));
- Collapse(vol_item);
+ /*Collapse*/Expand(vol_item);
}
}
@@ -1638,16 +1667,20 @@ void ObjectList::part_selection_changed()
bool update_and_show_manipulations = false;
bool update_and_show_settings = false;
- if (multiple_selection()) {
+ const auto item = GetSelection();
+
+ if ( multiple_selection() || item && m_objects_model->GetItemType(item) == itInstanceRoot )
+ {
og_name = _(L("Group manipulation"));
- update_and_show_manipulations = true;
+
+ const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
+ // don't show manipulation panel for case of all Object's parts selection
+ update_and_show_manipulations = !selection.is_single_full_instance();
}
else
{
- const auto item = GetSelection();
if (item)
{
- bool is_part = false;
if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
obj_idx = m_objects_model->GetIdByItem(item);
og_name = _(L("Object manipulation"));
@@ -1665,7 +1698,6 @@ void ObjectList::part_selection_changed()
}
else {
og_name = _(L("Part Settings to modify"));
- is_part = true;
auto main_parent = m_objects_model->GetParent(parent);
obj_idx = m_objects_model->GetIdByItem(main_parent);
const auto volume_id = m_objects_model->GetVolumeIdByItem(parent);
@@ -1675,7 +1707,6 @@ void ObjectList::part_selection_changed()
}
else if (m_objects_model->GetItemType(item) == itVolume) {
og_name = _(L("Part manipulation"));
- is_part = true;
const auto volume_id = m_objects_model->GetVolumeIdByItem(item);
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
update_and_show_manipulations = true;
@@ -1743,7 +1774,7 @@ void ObjectList::add_object_to_list(size_t obj_idx)
auto opt_keys = model_object->volumes[id]->config.keys();
if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) {
select_item(m_objects_model->AddSettingsChild(vol_item));
- Collapse(vol_item);
+ /*Collapse*/Expand(vol_item);
}
}
Expand(item);
@@ -1757,7 +1788,7 @@ void ObjectList::add_object_to_list(size_t obj_idx)
auto opt_keys = model_object->config.keys();
if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) {
select_item(m_objects_model->AddSettingsChild(item));
- Collapse(item);
+ /*Collapse*/Expand(item);
}
#ifndef __WXOSX__
@@ -1923,11 +1954,22 @@ bool ObjectList::multiple_selection() const
return GetSelectedItemsCount() > 1;
}
+bool ObjectList::is_selected(const ItemType type) const
+{
+ const wxDataViewItem& item = GetSelection();
+ if (item)
+ return m_objects_model->GetItemType(item) == type;
+
+ return false;
+}
+
void ObjectList::update_selections()
{
const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
wxDataViewItemArray sels;
+ m_selection_mode = smInstance;
+
// We doesn't update selection if SettingsItem for the current object/part is selected
if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) == itSettings )
{
@@ -1941,32 +1983,56 @@ void ObjectList::update_selections()
return;
}
}
-
- if (selection.is_single_full_object())
+ else if (selection.is_single_full_object() || selection.is_multiple_full_object())
{
- sels.Add(m_objects_model->GetItemById(selection.get_object_idx()));
+ const Selection::ObjectIdxsToInstanceIdxsMap& objects_content = selection.get_content();
+ for (const auto& object : objects_content) {
+ if (object.second.size() == 1) // object with 1 instance
+ sels.Add(m_objects_model->GetItemById(object.first));
+ else if (object.second.size() > 1) // object with several instances
+ {
+ wxDataViewItemArray current_sels;
+ GetSelections(current_sels);
+ const wxDataViewItem frst_inst_item = m_objects_model->GetItemByInstanceId(object.first, 0);
+
+ bool root_is_selected = false;
+ for (const auto& item:current_sels)
+ if (item == m_objects_model->GetParent(frst_inst_item) ||
+ item == m_objects_model->GetTopParent(frst_inst_item)) {
+ root_is_selected = true;
+ sels.Add(item);
+ break;
+ }
+ if (root_is_selected)
+ continue;
+
+ const Selection::InstanceIdxsList& instances = object.second;
+ for (const auto& inst : instances)
+ sels.Add(m_objects_model->GetItemByInstanceId(object.first, inst));
+ }
+ }
}
- else if (selection.is_single_volume() || selection.is_modifier() ||
- selection.is_multiple_volume() || selection.is_multiple_full_object()) {
+ else if (selection.is_single_volume() || selection.is_modifier() || selection.is_multiple_volume())
+ {
for (auto idx : selection.get_volume_idxs()) {
const auto gl_vol = selection.get_volume(idx);
- if (selection.is_multiple_full_object())
- sels.Add(m_objects_model->GetItemById(gl_vol->object_idx()));
- else if (gl_vol->volume_idx() >= 0)
+ if (gl_vol->volume_idx() >= 0)
// Only add GLVolumes with non-negative volume_ids. GLVolumes with negative volume ids
// are not associated with ModelVolumes, but they are temporarily generated by the backend
// (for example, SLA supports or SLA pad).
sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx()));
}
+ m_selection_mode = smVolume;
}
- else if (selection.is_single_full_instance() || selection.is_multiple_full_instance()) {
+ else if (selection.is_single_full_instance() || selection.is_multiple_full_instance())
+ {
for (auto idx : selection.get_instance_idxs()) {
sels.Add(m_objects_model->GetItemByInstanceId(selection.get_object_idx(), idx));
}
}
else if (selection.is_mixed())
{
- auto& objects_content_list = selection.get_content();
+ const Selection::ObjectIdxsToInstanceIdxsMap& objects_content_list = selection.get_content();
for (auto idx : selection.get_volume_idxs()) {
const auto gl_vol = selection.get_volume(idx);
@@ -1999,6 +2065,9 @@ void ObjectList::update_selections()
sels.Add(m_objects_model->GetItemByVolumeId(glv_obj_idx, glv_vol_idx));
}
}
+
+ if (sels.size() == 0)
+ m_selection_mode = smUndef;
select_items(sels);
@@ -2034,29 +2103,34 @@ void ObjectList::update_selections_on_canvas()
auto add_to_selection = [this](const wxDataViewItem& item, Selection& selection, int instance_idx, bool as_single_selection)
{
- if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
- selection.add_object(m_objects_model->GetIdByItem(item), as_single_selection);
+ const ItemType& type = m_objects_model->GetItemType(item);
+ if ( type == itInstanceRoot || m_objects_model->GetParent(item) == wxDataViewItem(0) ) {
+ wxDataViewItem obj_item = type == itInstanceRoot ? m_objects_model->GetParent(item) : item;
+ selection.add_object(m_objects_model->GetIdByItem(obj_item), as_single_selection);
return;
}
- if (m_objects_model->GetItemType(item) == itVolume) {
+ if (type == itVolume) {
const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetParent(item));
const int vol_idx = m_objects_model->GetVolumeIdByItem(item);
selection.add_volume(obj_idx, vol_idx, std::max(instance_idx, 0), as_single_selection);
}
- else if (m_objects_model->GetItemType(item) == itInstance) {
+ else if (type == itInstance) {
const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item));
const int inst_idx = m_objects_model->GetInstanceIdByItem(item);
selection.add_instance(obj_idx, inst_idx, as_single_selection);
}
};
+ // stores current instance idx before to clear the selection
+ int instance_idx = selection.get_instance_idx();
+
if (sel_cnt == 1) {
wxDataViewItem item = GetSelection();
if (m_objects_model->GetItemType(item) & (itSettings|itInstanceRoot))
- add_to_selection(m_objects_model->GetParent(item), selection, -1, true);
+ add_to_selection(m_objects_model->GetParent(item), selection, instance_idx, true);
else
- add_to_selection(item, selection, -1, true);
+ add_to_selection(item, selection, instance_idx, true);
wxGetApp().plater()->canvas3D()->update_gizmos_on_off_state();
return;
@@ -2065,8 +2139,6 @@ void ObjectList::update_selections_on_canvas()
wxDataViewItemArray sels;
GetSelections(sels);
- // stores current instance idx before to clear the selection
- int instance_idx = selection.get_instance_idx();
selection.clear();
for (auto item: sels)
add_to_selection(item, selection, instance_idx, false);
@@ -2112,22 +2184,101 @@ void ObjectList::select_item_all_children()
if (!GetSelection() || m_objects_model->GetItemType(GetSelection()) == itObject) {
for (int i = 0; i < m_objects->size(); i++)
sels.Add(m_objects_model->GetItemById(i));
+ m_selection_mode = smInstance;
}
else {
const auto item = GetSelection();
// Some volume(instance) is selected => select all volumes(instances) inside the current object
- if (m_objects_model->GetItemType(item) & (itVolume | itInstance)) {
+ if (m_objects_model->GetItemType(item) & (itVolume | itInstance))
m_objects_model->GetChildren(m_objects_model->GetParent(item), sels);
- }
+
+ m_selection_mode = m_objects_model->GetItemType(item)&itVolume ? smVolume : smInstance;
}
SetSelections(sels);
selection_changed();
}
+// update selection mode for non-multiple selection
+void ObjectList::update_selection_mode()
+{
+ // All items are unselected
+ if (!GetSelection())
+ {
+ m_last_selected_item = wxDataViewItem(0);
+ m_selection_mode = smUndef;
+ return;
+ }
+
+ const ItemType type = m_objects_model->GetItemType(GetSelection());
+ m_selection_mode = type&itSettings ? smUndef :
+ type&itVolume ? smVolume : smInstance;
+}
+
+// check last selected item. If is it possible to select it
+bool ObjectList::check_last_selection(wxString& msg_str)
+{
+ if (!m_last_selected_item)
+ return true;
+
+ const bool is_shift_pressed = wxGetKeyState(WXK_SHIFT);
+
+ /* We can't mix Parts and Objects/Instances.
+ * So, show information about it
+ */
+ const ItemType type = m_objects_model->GetItemType(m_last_selected_item);
+
+ // check a case of a selection of the Parts from different Objects
+ bool impossible_multipart_selection = false;
+ if (type & itVolume && m_selection_mode == smVolume)
+ {
+ wxDataViewItemArray sels;
+ GetSelections(sels);
+ for (const auto& sel: sels)
+ if (sel != m_last_selected_item &&
+ m_objects_model->GetParent(sel) != m_objects_model->GetParent(m_last_selected_item))
+ {
+ impossible_multipart_selection = true;
+ break;
+ }
+ }
+
+ if (impossible_multipart_selection ||
+ type & itSettings ||
+ type & itVolume && m_selection_mode == smInstance ||
+ !(type & itVolume) && m_selection_mode == smVolume)
+ {
+ // Inform user why selection isn't complited
+ const wxString item_type = m_selection_mode == smInstance ? _(L("Object or Instance")) : _(L("Part"));
+
+ msg_str = wxString::Format( _(L("Unsupported selection")) + "\n\n" +
+ _(L("You started your selection with %s Item.")) + "\n" +
+ _(L("In this mode you can select only other %s Items%s")),
+ item_type, item_type,
+ m_selection_mode == smInstance ? "." :
+ " " + _(L("of a current Object")));
+
+ // Unselect last selected item, if selection is without SHIFT
+ if (!is_shift_pressed) {
+ Unselect(m_last_selected_item);
+ show_info(this, msg_str, _(L("Info")));
+ }
+
+ return is_shift_pressed;
+ }
+
+ return true;
+}
+
void ObjectList::fix_multiselection_conflicts()
{
- if (GetSelectedItemsCount() <= 1)
+ if (GetSelectedItemsCount() <= 1) {
+ update_selection_mode();
+ return;
+ }
+
+ wxString msg_string;
+ if (!check_last_selection(msg_string))
return;
m_prevent_list_events = true;
@@ -2135,12 +2286,58 @@ void ObjectList::fix_multiselection_conflicts()
wxDataViewItemArray sels;
GetSelections(sels);
- for (auto item : sels) {
- if (m_objects_model->GetItemType(item) & (itSettings|itInstanceRoot))
- Unselect(item);
- else if (m_objects_model->GetParent(item) != wxDataViewItem(0))
- Unselect(m_objects_model->GetParent(item));
+ if (m_selection_mode == smVolume)
+ {
+ // identify correct parent of the initial selected item
+ const wxDataViewItem& parent = m_objects_model->GetParent(m_last_selected_item == sels.front() ? sels.back() : sels.front());
+
+ sels.clear();
+ wxDataViewItemArray children; // selected volumes from current parent
+ m_objects_model->GetChildren(parent, children);
+
+ for (const auto child : children)
+ if (IsSelected(child) && m_objects_model->GetItemType(child)&itVolume)
+ sels.Add(child);
+
+ // If some part is selected, unselect all items except of selected parts of the current object
+ UnselectAll();
+ SetSelections(sels);
}
+ else
+ {
+ for (const auto item : sels)
+ {
+ if (!IsSelected(item)) // if this item is unselected now (from previous actions)
+ continue;
+
+ if (m_objects_model->GetItemType(item) & itSettings) {
+ Unselect(item);
+ continue;
+ }
+
+ const wxDataViewItem& parent = m_objects_model->GetParent(item);
+ if (parent != wxDataViewItem(0) && IsSelected(parent))
+ Unselect(parent);
+ else
+ {
+ wxDataViewItemArray unsels;
+ m_objects_model->GetAllChildren(item, unsels);
+ for (const auto unsel_item : unsels)
+ Unselect(unsel_item);
+ }
+
+ if (m_objects_model->GetItemType(item) & itVolume)
+ Unselect(item);
+
+ m_selection_mode = smInstance;
+ }
+ }
+
+ if (!msg_string.IsEmpty())
+ show_info(this, msg_string, _(L("Info")));
+
+ if (!IsSelected(m_last_selected_item))
+ m_last_selected_item = wxDataViewItem(0);
m_prevent_list_events = false;
}
@@ -2272,6 +2469,12 @@ void ObjectList::update_object_menu()
void ObjectList::instances_to_separated_object(const int obj_idx, const std::set& inst_idxs)
{
+ if ((*m_objects)[obj_idx]->instances.size() == inst_idxs.size())
+ {
+ instances_to_separated_objects(obj_idx);
+ return;
+ }
+
// create new object from selected instance
ModelObject* model_object = (*m_objects)[obj_idx]->get_model()->add_object(*(*m_objects)[obj_idx]);
for (int inst_idx = model_object->instances.size() - 1; inst_idx >= 0; inst_idx--)
@@ -2292,6 +2495,31 @@ void ObjectList::instances_to_separated_object(const int obj_idx, const std::set
}
}
+void ObjectList::instances_to_separated_objects(const int obj_idx)
+{
+ const int inst_cnt = (*m_objects)[obj_idx]->instances.size();
+
+ for (int i = inst_cnt-1; i > 0 ; i--)
+ {
+ // create new object from initial
+ ModelObject* object = (*m_objects)[obj_idx]->get_model()->add_object(*(*m_objects)[obj_idx]);
+ for (int inst_idx = object->instances.size() - 1; inst_idx >= 0; inst_idx--)
+ {
+ if (inst_idx == i)
+ continue;
+ // delete unnecessary instances
+ object->delete_instance(inst_idx);
+ }
+
+ // Add new object to the object_list
+ add_object_to_list(m_objects->size() - 1);
+
+ // delete current instance from the initial object
+ del_subobject_from_object(obj_idx, i, itInstance);
+ delete_instance_from_list(obj_idx, i);
+ }
+}
+
void ObjectList::split_instances()
{
const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
@@ -2299,6 +2527,12 @@ void ObjectList::split_instances()
if (obj_idx == -1)
return;
+ if (selection.is_single_full_object())
+ {
+ instances_to_separated_objects(obj_idx);
+ return;
+ }
+
const int inst_idx = selection.get_instance_idx();
const std::set inst_idxs = inst_idx < 0 ?
selection.get_instance_idxs() :
@@ -2424,8 +2658,7 @@ void ObjectList::show_multi_selection_menu()
wxMenu* menu = new wxMenu();
#ifdef __WXOSX__
- append_menu_item(menu, wxID_ANY, _(L("Delete items")), "",
- [this](wxCommandEvent&) { remove(); }, "", menu);
+ append_menu_item_delete(menu);
#endif //__WXOSX__
if (extruders_count() > 1)
diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp
index 4ddc6ea71f..2f465a4ab5 100644
--- a/src/slic3r/GUI/GUI_ObjectList.hpp
+++ b/src/slic3r/GUI/GUI_ObjectList.hpp
@@ -60,6 +60,12 @@ struct ItemForDelete
class ObjectList : public wxDataViewCtrl
{
+ enum SELECTION_MODE
+ {
+ smUndef,
+ smVolume,
+ smInstance
+ } m_selection_mode {smUndef};
struct dragged_item_data
{
@@ -135,6 +141,7 @@ class ObjectList : public wxDataViewCtrl
bool m_part_settings_changed = false;
int m_selected_row = 0;
+ wxDataViewItem m_last_selected_item {nullptr};
#if 0
FreqSettingsBundle m_freq_settings_fff;
@@ -188,6 +195,7 @@ public:
void append_menu_item_fix_through_netfabb(wxMenu* menu);
void append_menu_item_export_stl(wxMenu* menu) const ;
void append_menu_item_change_extruder(wxMenu* menu) const;
+ void append_menu_item_delete(wxMenu* menu);
void create_object_popupmenu(wxMenu *menu);
void create_sla_object_popupmenu(wxMenu*menu);
void create_part_popupmenu(wxMenu*menu);
@@ -251,12 +259,15 @@ public:
void init_objects();
bool multiple_selection() const ;
+ bool is_selected(const ItemType type) const;
void update_selections();
void update_selections_on_canvas();
void select_item(const wxDataViewItem& item);
void select_items(const wxDataViewItemArray& sels);
void select_all();
void select_item_all_children();
+ void update_selection_mode();
+ bool check_last_selection(wxString& msg_str);
// correct current selections to avoid of the possible conflicts
void fix_multiselection_conflicts();
@@ -269,6 +280,7 @@ public:
void update_object_menu();
void instances_to_separated_object(const int obj_idx, const std::set& inst_idx);
+ void instances_to_separated_objects(const int obj_idx);
void split_instances();
void rename_item();
void fix_through_netfabb() const;
diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp
index 61e98d5425..7e7d8a1d5a 100644
--- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp
+++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp
@@ -92,7 +92,6 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
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());
create_scaled_bitmap("one_layer_lock_on.png").GetSize());
};
}
@@ -161,6 +160,8 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
m_new_move_label_string = L("Position");
m_new_rotate_label_string = L("Rotation");
m_new_scale_label_string = L("Scale factors");
+
+ ObjectList* obj_list = wxGetApp().obj_list();
if (selection.is_single_full_instance())
{
// all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one
@@ -187,7 +188,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
m_new_enabled = true;
}
- else if (selection.is_single_full_object())
+ else if (selection.is_single_full_object() && obj_list->is_selected(itObject))
{
m_cache.instance.reset();
@@ -212,7 +213,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
m_new_size = (volume->get_volume_transformation().get_matrix(true, true) * volume->bounding_box.size()).cwiseAbs();
m_new_enabled = true;
}
- else if (wxGetApp().obj_list()->multiple_selection())
+ else if (obj_list->multiple_selection() || obj_list->is_selected(itInstanceRoot))
{
reset_settings_value();
m_new_move_label_string = L("Translate");
diff --git a/src/slic3r/GUI/GUI_ObjectSettings.cpp b/src/slic3r/GUI/GUI_ObjectSettings.cpp
index cd995bc094..b1f33eea11 100644
--- a/src/slic3r/GUI/GUI_ObjectSettings.cpp
+++ b/src/slic3r/GUI/GUI_ObjectSettings.cpp
@@ -77,7 +77,6 @@ void ObjectSettings::update_settings_list()
{
auto opt_key = (line.get_options())[0].opt_id; //we assume that we have one option per line
-// auto btn = new wxBitmapButton(parent, wxID_ANY, wxBitmap(from_u8(var("colorchange_delete_on.png")), wxBITMAP_TYPE_PNG),
auto btn = new wxBitmapButton(parent, wxID_ANY, create_scaled_bitmap("colorchange_delete_on.png"),
wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
#ifdef __WXMSW__
diff --git a/src/slic3r/GUI/GUI_Utils.cpp b/src/slic3r/GUI/GUI_Utils.cpp
index 56d6eaeb58..6dc5c5b4c7 100644
--- a/src/slic3r/GUI/GUI_Utils.cpp
+++ b/src/slic3r/GUI/GUI_Utils.cpp
@@ -4,9 +4,14 @@
#include
#include
+#ifdef _WIN32
+#include
+#endif
+
#include
#include
#include
+#include
#include "libslic3r/Config.hpp"
@@ -48,6 +53,64 @@ void on_window_geometry(wxTopLevelWindow *tlw, std::function callback)
#endif
}
+wxDEFINE_EVENT(EVT_DPI_CHANGED, DpiChangedEvent);
+
+#ifdef _WIN32
+template typename F::FN winapi_get_function(const wchar_t *dll, const char *fn_name) {
+ static HINSTANCE dll_handle = LoadLibraryExW(dll, nullptr, 0);
+
+ if (dll_handle == nullptr) { return nullptr; }
+ return (F::FN)GetProcAddress(dll_handle, fn_name);
+}
+#endif
+
+int get_dpi_for_window(wxWindow *window)
+{
+#ifdef _WIN32
+ enum MONITOR_DPI_TYPE_ {
+ // This enum is inlined here to avoid build-time dependency
+ MDT_EFFECTIVE_DPI_ = 0,
+ MDT_ANGULAR_DPI_ = 1,
+ MDT_RAW_DPI_ = 2,
+ MDT_DEFAULT_ = MDT_EFFECTIVE_DPI_,
+ };
+
+ // Need strong types for winapi_get_function() to work
+ struct GetDpiForWindow_t { typedef HRESULT (WINAPI *FN)(HWND hwnd); };
+ struct GetDpiForMonitor_t { typedef HRESULT (WINAPI *FN)(HMONITOR hmonitor, MONITOR_DPI_TYPE_ dpiType, UINT *dpiX, UINT *dpiY); };
+
+ static auto GetDpiForWindow_fn = winapi_get_function(L"User32.dll", "GetDpiForWindow");
+ static auto GetDpiForMonitor_fn = winapi_get_function(L"Shcore.dll", "GetDpiForMonitor");
+
+ const HWND hwnd = window->GetHandle();
+
+ if (GetDpiForWindow_fn != nullptr) {
+ // We're on Windows 10, we have per-screen DPI settings
+ return GetDpiForWindow_fn(hwnd);
+ } else if (GetDpiForMonitor_fn != nullptr) {
+ // We're on Windows 8.1, we have per-system DPI
+ // Note: MonitorFromWindow() is available on all Windows.
+
+ const HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
+ UINT dpiX;
+ UINT dpiY;
+ return GetDpiForMonitor_fn(monitor, MDT_EFFECTIVE_DPI_, &dpiX, &dpiY) == S_OK ? dpiX : DPI_DEFAULT;
+ } else {
+ // We're on Windows earlier than 8.1, use DC
+
+ const HDC hdc = GetDC(hwnd);
+ if (hdc == NULL) { return DPI_DEFAULT; }
+ return GetDeviceCaps(hdc, LOGPIXELSX);
+ }
+#elif defined __linux__
+ // TODO
+ return DPI_DEFAULT;
+#elif defined __APPLE__
+ // TODO
+ return DPI_DEFAULT;
+#endif
+}
+
CheckboxFileDialog::ExtraPanel::ExtraPanel(wxWindow *parent)
: wxPanel(parent, wxID_ANY)
diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp
index 8d942dcf84..e121536259 100644
--- a/src/slic3r/GUI/GUI_Utils.hpp
+++ b/src/slic3r/GUI/GUI_Utils.hpp
@@ -8,10 +8,13 @@
#include
+#include
+#include
#include
#include
#include
#include
+#include
#include
class wxCheckBox;
@@ -27,6 +30,68 @@ wxTopLevelWindow* find_toplevel_parent(wxWindow *window);
void on_window_geometry(wxTopLevelWindow *tlw, std::function callback);
+enum { DPI_DEFAULT = 96 };
+
+int get_dpi_for_window(wxWindow *window);
+
+struct DpiChangedEvent : public wxEvent {
+ int dpi;
+ wxRect rect;
+
+ DpiChangedEvent(wxEventType eventType, int dpi, wxRect rect)
+ : wxEvent(0, eventType), dpi(dpi), rect(rect)
+ {}
+
+ virtual wxEvent *Clone() const
+ {
+ return new DpiChangedEvent(*this);
+ }
+};
+
+wxDECLARE_EVENT(EVT_DPI_CHANGED, DpiChangedEvent);
+
+template class DPIAware : public P
+{
+public:
+ DPIAware(wxWindow *parent, wxWindowID id, const wxString &title, const wxPoint &pos=wxDefaultPosition,
+ const wxSize &size=wxDefaultSize, long style=wxDEFAULT_FRAME_STYLE, const wxString &name=wxFrameNameStr)
+ : P(parent, id, title, pos, size, style, name)
+ {
+ m_scale_factor = (float)get_dpi_for_window(this) / (float)DPI_DEFAULT;
+ recalc_font();
+
+ this->Bind(EVT_DPI_CHANGED, [this](const DpiChangedEvent &evt) {
+ m_scale_factor = (float)evt.dpi / (float)DPI_DEFAULT;
+ on_dpi_changed(evt.rect);
+ });
+ }
+
+ virtual ~DPIAware() {}
+
+ float scale_factor() const { return m_scale_factor; }
+ int em_unit() const { return m_em_unit; }
+ int font_size() const { return m_font_size; }
+
+protected:
+ virtual void on_dpi_changed(const wxRect &suggested_rect) = 0;
+
+private:
+ int m_scale_factor;
+ int m_em_unit;
+ int m_font_size;
+
+ void recalc_font()
+ {
+ wxClientDC dc(this);
+ const auto metrics = dc.GetFontMetrics();
+ m_font_size = metrics.height;
+ m_em_unit = metrics.averageWidth;
+ }
+};
+
+typedef DPIAware DPIFrame;
+typedef DPIAware DPIDialog;
+
class EventGuard
{
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
index a1e95cfc53..6bf43955fb 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
@@ -1,4 +1,5 @@
#include "GLGizmoBase.hpp"
+#include "slic3r/GUI/GLCanvas3D.hpp"
#include
@@ -60,62 +61,62 @@ void GLGizmoBase::Grabber::render(float size, const float* render_color, bool us
float half_size = dragging ? get_dragging_half_size(size) : get_half_size(size);
if (use_lighting)
- ::glEnable(GL_LIGHTING);
+ glsafe(::glEnable(GL_LIGHTING));
- ::glColor3fv(render_color);
+ glsafe(::glColor3fv(render_color));
- ::glPushMatrix();
- ::glTranslated(center(0), center(1), center(2));
+ glsafe(::glPushMatrix());
+ glsafe(::glTranslated(center(0), center(1), center(2)));
- ::glRotated(Geometry::rad2deg(angles(2)), 0.0, 0.0, 1.0);
- ::glRotated(Geometry::rad2deg(angles(1)), 0.0, 1.0, 0.0);
- ::glRotated(Geometry::rad2deg(angles(0)), 1.0, 0.0, 0.0);
+ glsafe(::glRotated(Geometry::rad2deg(angles(2)), 0.0, 0.0, 1.0));
+ glsafe(::glRotated(Geometry::rad2deg(angles(1)), 0.0, 1.0, 0.0));
+ glsafe(::glRotated(Geometry::rad2deg(angles(0)), 1.0, 0.0, 0.0));
// face min x
- ::glPushMatrix();
- ::glTranslatef(-(GLfloat)half_size, 0.0f, 0.0f);
- ::glRotatef(-90.0f, 0.0f, 1.0f, 0.0f);
+ glsafe(::glPushMatrix());
+ glsafe(::glTranslatef(-(GLfloat)half_size, 0.0f, 0.0f));
+ glsafe(::glRotatef(-90.0f, 0.0f, 1.0f, 0.0f));
render_face(half_size);
- ::glPopMatrix();
+ glsafe(::glPopMatrix());
// face max x
- ::glPushMatrix();
- ::glTranslatef((GLfloat)half_size, 0.0f, 0.0f);
- ::glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
+ glsafe(::glPushMatrix());
+ glsafe(::glTranslatef((GLfloat)half_size, 0.0f, 0.0f));
+ glsafe(::glRotatef(90.0f, 0.0f, 1.0f, 0.0f));
render_face(half_size);
- ::glPopMatrix();
+ glsafe(::glPopMatrix());
// face min y
- ::glPushMatrix();
- ::glTranslatef(0.0f, -(GLfloat)half_size, 0.0f);
- ::glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
+ glsafe(::glPushMatrix());
+ glsafe(::glTranslatef(0.0f, -(GLfloat)half_size, 0.0f));
+ glsafe(::glRotatef(90.0f, 1.0f, 0.0f, 0.0f));
render_face(half_size);
- ::glPopMatrix();
+ glsafe(::glPopMatrix());
// face max y
- ::glPushMatrix();
- ::glTranslatef(0.0f, (GLfloat)half_size, 0.0f);
- ::glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
+ glsafe(::glPushMatrix());
+ glsafe(::glTranslatef(0.0f, (GLfloat)half_size, 0.0f));
+ glsafe(::glRotatef(-90.0f, 1.0f, 0.0f, 0.0f));
render_face(half_size);
- ::glPopMatrix();
+ glsafe(::glPopMatrix());
// face min z
- ::glPushMatrix();
- ::glTranslatef(0.0f, 0.0f, -(GLfloat)half_size);
- ::glRotatef(180.0f, 1.0f, 0.0f, 0.0f);
+ glsafe(::glPushMatrix());
+ glsafe(::glTranslatef(0.0f, 0.0f, -(GLfloat)half_size));
+ glsafe(::glRotatef(180.0f, 1.0f, 0.0f, 0.0f));
render_face(half_size);
- ::glPopMatrix();
+ glsafe(::glPopMatrix());
// face max z
- ::glPushMatrix();
- ::glTranslatef(0.0f, 0.0f, (GLfloat)half_size);
+ glsafe(::glPushMatrix());
+ glsafe(::glTranslatef(0.0f, 0.0f, (GLfloat)half_size));
render_face(half_size);
- ::glPopMatrix();
+ glsafe(::glPopMatrix());
- ::glPopMatrix();
+ glsafe(::glPopMatrix());
if (use_lighting)
- ::glDisable(GL_LIGHTING);
+ glsafe(::glDisable(GL_LIGHTING));
}
void GLGizmoBase::Grabber::render_face(float half_size) const
@@ -128,7 +129,7 @@ void GLGizmoBase::Grabber::render_face(float half_size) const
::glVertex3f((GLfloat)half_size, (GLfloat)half_size, 0.0f);
::glVertex3f(-(GLfloat)half_size, (GLfloat)half_size, 0.0f);
::glVertex3f(-(GLfloat)half_size, -(GLfloat)half_size, 0.0f);
- ::glEnd();
+ glsafe(::glEnd());
}
#if ENABLE_SVG_ICONS
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
index b70c1f8df1..461f655702 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
@@ -3,7 +3,6 @@
#include "libslic3r/Point.hpp"
-#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/Selection.hpp"
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
index 02d663e93b..cabede8cd6 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
@@ -1,5 +1,6 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoCut.hpp"
+#include "slic3r/GUI/GLCanvas3D.hpp"
#include
@@ -15,12 +16,6 @@ namespace Slic3r {
namespace GUI {
-
-
-
-
-// GLGizmoCut
-
class GLGizmoCutPanel : public wxPanel
{
public:
@@ -141,10 +136,10 @@ void GLGizmoCut::on_render(const Selection& selection) const
const float max_x = box.max(0) + Margin;
const float min_y = box.min(1) - Margin;
const float max_y = box.max(1) + Margin;
- ::glEnable(GL_DEPTH_TEST);
- ::glDisable(GL_CULL_FACE);
- ::glEnable(GL_BLEND);
- ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glsafe(::glEnable(GL_DEPTH_TEST));
+ glsafe(::glDisable(GL_CULL_FACE));
+ glsafe(::glEnable(GL_BLEND));
+ glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
// Draw the cutting plane
::glBegin(GL_QUADS);
@@ -153,10 +148,10 @@ void GLGizmoCut::on_render(const Selection& selection) const
::glVertex3f(max_x, min_y, plane_center(2));
::glVertex3f(max_x, max_y, plane_center(2));
::glVertex3f(min_x, max_y, plane_center(2));
- ::glEnd();
+ glsafe(::glEnd());
- ::glEnable(GL_CULL_FACE);
- ::glDisable(GL_BLEND);
+ glsafe(::glEnable(GL_CULL_FACE));
+ glsafe(::glDisable(GL_BLEND));
// TODO: draw cut part contour?
@@ -164,13 +159,13 @@ void GLGizmoCut::on_render(const Selection& selection) const
m_grabbers[0].center = plane_center;
m_grabbers[0].center(2) = plane_center(2) + Offset;
- ::glDisable(GL_DEPTH_TEST);
- ::glLineWidth(m_hover_id != -1 ? 2.0f : 1.5f);
- ::glColor3f(1.0, 1.0, 0.0);
+ glsafe(::glDisable(GL_DEPTH_TEST));
+ glsafe(::glLineWidth(m_hover_id != -1 ? 2.0f : 1.5f));
+ glsafe(::glColor3f(1.0, 1.0, 0.0));
::glBegin(GL_LINES);
::glVertex3dv(plane_center.data());
::glVertex3dv(m_grabbers[0].center.data());
- ::glEnd();
+ glsafe(::glEnd());
std::copy(std::begin(GrabberColor), std::end(GrabberColor), m_grabbers[0].color);
m_grabbers[0].render(m_hover_id == 0, (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0));
@@ -178,7 +173,7 @@ void GLGizmoCut::on_render(const Selection& selection) const
void GLGizmoCut::on_render_for_picking(const Selection& selection) const
{
- ::glDisable(GL_DEPTH_TEST);
+ glsafe(::glDisable(GL_DEPTH_TEST));
render_grabbers_for_picking(selection.get_bounding_box());
}
@@ -192,7 +187,7 @@ void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit, co
m_imgui->set_next_window_bg_alpha(0.5f);
m_imgui->begin(_(L("Cut")), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
- ImGui::PushItemWidth(100.0f);
+ ImGui::PushItemWidth(m_imgui->scaled(5.0f));
bool _value_changed = ImGui::InputDouble("Z", &m_cut_z, 0.0f, 0.0f, "%.2f");
m_imgui->checkbox(_(L("Keep upper part")), m_keep_upper);
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
index 2ed0486772..56dbf4f54f 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
@@ -49,67 +49,67 @@ void GLGizmoFlatten::on_start_dragging(const Selection& selection)
void GLGizmoFlatten::on_render(const Selection& selection) const
{
- ::glClear(GL_DEPTH_BUFFER_BIT);
+ glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
- ::glEnable(GL_DEPTH_TEST);
- ::glEnable(GL_BLEND);
+ glsafe(::glEnable(GL_DEPTH_TEST));
+ glsafe(::glEnable(GL_BLEND));
if (selection.is_single_full_instance())
{
const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix();
- ::glPushMatrix();
- ::glTranslatef(0.f, 0.f, selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z());
- ::glMultMatrixd(m.data());
+ glsafe(::glPushMatrix());
+ glsafe(::glTranslatef(0.f, 0.f, selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z()));
+ glsafe(::glMultMatrixd(m.data()));
if (this->is_plane_update_necessary())
const_cast(this)->update_planes();
for (int i = 0; i < (int)m_planes.size(); ++i)
{
if (i == m_hover_id)
- ::glColor4f(0.9f, 0.9f, 0.9f, 0.75f);
+ glsafe(::glColor4f(0.9f, 0.9f, 0.9f, 0.75f));
else
- ::glColor4f(0.9f, 0.9f, 0.9f, 0.5f);
+ glsafe(::glColor4f(0.9f, 0.9f, 0.9f, 0.5f));
::glBegin(GL_POLYGON);
for (const Vec3d& vertex : m_planes[i].vertices)
{
::glVertex3dv(vertex.data());
}
- ::glEnd();
+ glsafe(::glEnd());
}
- ::glPopMatrix();
+ glsafe(::glPopMatrix());
}
- ::glEnable(GL_CULL_FACE);
- ::glDisable(GL_BLEND);
+ glsafe(::glEnable(GL_CULL_FACE));
+ glsafe(::glDisable(GL_BLEND));
}
void GLGizmoFlatten::on_render_for_picking(const Selection& selection) const
{
- ::glDisable(GL_DEPTH_TEST);
- ::glDisable(GL_BLEND);
+ glsafe(::glDisable(GL_DEPTH_TEST));
+ glsafe(::glDisable(GL_BLEND));
if (selection.is_single_full_instance())
{
const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix();
- ::glPushMatrix();
- ::glTranslatef(0.f, 0.f, selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z());
- ::glMultMatrixd(m.data());
+ glsafe(::glPushMatrix());
+ glsafe(::glTranslatef(0.f, 0.f, selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z()));
+ glsafe(::glMultMatrixd(m.data()));
if (this->is_plane_update_necessary())
const_cast(this)->update_planes();
for (int i = 0; i < (int)m_planes.size(); ++i)
{
- ::glColor3fv(picking_color_component(i).data());
+ glsafe(::glColor3fv(picking_color_component(i).data()));
::glBegin(GL_POLYGON);
for (const Vec3d& vertex : m_planes[i].vertices)
{
::glVertex3dv(vertex.data());
}
- ::glEnd();
+ glsafe(::glEnd());
}
- ::glPopMatrix();
+ glsafe(::glPopMatrix());
}
- ::glEnable(GL_CULL_FACE);
+ glsafe(::glEnable(GL_CULL_FACE));
}
void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object)
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
index 61f219dbff..7cc5b6485b 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
@@ -97,8 +97,8 @@ void GLGizmoMove3D::on_render(const Selection& selection) const
else if (!m_grabbers[2].dragging && (m_hover_id == 2))
set_tooltip("Z");
- ::glClear(GL_DEPTH_BUFFER_BIT);
- ::glEnable(GL_DEPTH_TEST);
+ glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
+ glsafe(::glEnable(GL_DEPTH_TEST));
const BoundingBoxf3& box = selection.get_bounding_box();
const Vec3d& center = box.center();
@@ -115,7 +115,7 @@ void GLGizmoMove3D::on_render(const Selection& selection) const
m_grabbers[2].center = Vec3d(center(0), center(1), box.max(2) + Offset);
::memcpy((void*)m_grabbers[2].color, (const void*)&AXES_COLOR[2], 3 * sizeof(float));
- ::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f);
+ glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f));
if (m_hover_id == -1)
{
@@ -124,11 +124,11 @@ void GLGizmoMove3D::on_render(const Selection& selection) const
{
if (m_grabbers[i].enabled)
{
- ::glColor3fv(AXES_COLOR[i]);
+ glsafe(::glColor3fv(AXES_COLOR[i]));
::glBegin(GL_LINES);
::glVertex3dv(center.data());
::glVertex3dv(m_grabbers[i].center.data());
- ::glEnd();
+ glsafe(::glEnd());
}
}
@@ -143,11 +143,11 @@ void GLGizmoMove3D::on_render(const Selection& selection) const
else
{
// draw axis
- ::glColor3fv(AXES_COLOR[m_hover_id]);
+ glsafe(::glColor3fv(AXES_COLOR[m_hover_id]));
::glBegin(GL_LINES);
::glVertex3dv(center.data());
::glVertex3dv(m_grabbers[m_hover_id].center.data());
- ::glEnd();
+ glsafe(::glEnd());
// draw grabber
m_grabbers[m_hover_id].render(true, box.max_size());
@@ -157,7 +157,7 @@ void GLGizmoMove3D::on_render(const Selection& selection) const
void GLGizmoMove3D::on_render_for_picking(const Selection& selection) const
{
- ::glDisable(GL_DEPTH_TEST);
+ glsafe(::glDisable(GL_DEPTH_TEST));
const BoundingBoxf3& box = selection.get_bounding_box();
render_grabbers_for_picking(box);
@@ -229,25 +229,25 @@ void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box
}
if (!picking)
- ::glEnable(GL_LIGHTING);
+ glsafe(::glEnable(GL_LIGHTING));
- ::glColor3fv(color);
- ::glPushMatrix();
- ::glTranslated(m_grabbers[axis].center(0), m_grabbers[axis].center(1), m_grabbers[axis].center(2));
+ glsafe(::glColor3fv(color));
+ glsafe(::glPushMatrix());
+ glsafe(::glTranslated(m_grabbers[axis].center(0), m_grabbers[axis].center(1), m_grabbers[axis].center(2)));
if (axis == X)
- ::glRotated(90.0, 0.0, 1.0, 0.0);
+ glsafe(::glRotated(90.0, 0.0, 1.0, 0.0));
else if (axis == Y)
- ::glRotated(-90.0, 1.0, 0.0, 0.0);
+ glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0));
- ::glTranslated(0.0, 0.0, 2.0 * size);
+ glsafe(::glTranslated(0.0, 0.0, 2.0 * size));
::gluQuadricOrientation(m_quadric, GLU_OUTSIDE);
::gluCylinder(m_quadric, 0.75 * size, 0.0, 3.0 * size, 36, 1);
::gluQuadricOrientation(m_quadric, GLU_INSIDE);
::gluDisk(m_quadric, 0.0, 0.75 * size, 36, 1);
- ::glPopMatrix();
+ glsafe(::glPopMatrix());
if (!picking)
- ::glDisable(GL_LIGHTING);
+ glsafe(::glDisable(GL_LIGHTING));
}
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp
index e05ad00fe0..ff9cf380e2 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp
@@ -155,13 +155,13 @@ void GLGizmoRotate::on_render(const Selection& selection) const
m_snap_fine_out_radius = m_radius * (1.0f + ScaleLongTooth);
}
- ::glEnable(GL_DEPTH_TEST);
+ glsafe(::glEnable(GL_DEPTH_TEST));
- ::glPushMatrix();
+ glsafe(::glPushMatrix());
transform_to_local(selection);
- ::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f);
- ::glColor3fv((m_hover_id != -1) ? m_drag_color : m_highlight_color);
+ glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f));
+ glsafe(::glColor3fv((m_hover_id != -1) ? m_drag_color : m_highlight_color));
render_circle();
@@ -172,7 +172,7 @@ void GLGizmoRotate::on_render(const Selection& selection) const
render_reference_radius();
}
- ::glColor3fv(m_highlight_color);
+ glsafe(::glColor3fv(m_highlight_color));
if (m_hover_id != -1)
render_angle();
@@ -180,14 +180,14 @@ void GLGizmoRotate::on_render(const Selection& selection) const
render_grabber(box);
render_grabber_extension(box, false);
- ::glPopMatrix();
+ glsafe(::glPopMatrix());
}
void GLGizmoRotate::on_render_for_picking(const Selection& selection) const
{
- ::glDisable(GL_DEPTH_TEST);
+ glsafe(::glDisable(GL_DEPTH_TEST));
- ::glPushMatrix();
+ glsafe(::glPushMatrix());
transform_to_local(selection);
@@ -195,7 +195,7 @@ void GLGizmoRotate::on_render_for_picking(const Selection& selection) const
render_grabbers_for_picking(box);
render_grabber_extension(box, true);
- ::glPopMatrix();
+ glsafe(::glPopMatrix());
}
void GLGizmoRotate::render_circle() const
@@ -209,7 +209,7 @@ void GLGizmoRotate::render_circle() const
float z = 0.0f;
::glVertex3f((GLfloat)x, (GLfloat)y, (GLfloat)z);
}
- ::glEnd();
+ glsafe(::glEnd());
}
void GLGizmoRotate::render_scale() const
@@ -232,7 +232,7 @@ void GLGizmoRotate::render_scale() const
::glVertex3f((GLfloat)in_x, (GLfloat)in_y, (GLfloat)in_z);
::glVertex3f((GLfloat)out_x, (GLfloat)out_y, (GLfloat)out_z);
}
- ::glEnd();
+ glsafe(::glEnd());
}
void GLGizmoRotate::render_snap_radii() const
@@ -257,7 +257,7 @@ void GLGizmoRotate::render_snap_radii() const
::glVertex3f((GLfloat)in_x, (GLfloat)in_y, (GLfloat)in_z);
::glVertex3f((GLfloat)out_x, (GLfloat)out_y, (GLfloat)out_z);
}
- ::glEnd();
+ glsafe(::glEnd());
}
void GLGizmoRotate::render_reference_radius() const
@@ -265,7 +265,7 @@ void GLGizmoRotate::render_reference_radius() const
::glBegin(GL_LINES);
::glVertex3f(0.0f, 0.0f, 0.0f);
::glVertex3f((GLfloat)(m_radius * (1.0f + GrabberOffset)), 0.0f, 0.0f);
- ::glEnd();
+ glsafe(::glEnd());
}
void GLGizmoRotate::render_angle() const
@@ -282,7 +282,7 @@ void GLGizmoRotate::render_angle() const
float z = 0.0f;
::glVertex3f((GLfloat)x, (GLfloat)y, (GLfloat)z);
}
- ::glEnd();
+ glsafe(::glEnd());
}
void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const
@@ -291,12 +291,12 @@ void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const
m_grabbers[0].center = Vec3d(::cos(m_angle) * grabber_radius, ::sin(m_angle) * grabber_radius, 0.0);
m_grabbers[0].angles(2) = m_angle;
- ::glColor3fv((m_hover_id != -1) ? m_drag_color : m_highlight_color);
+ glsafe(::glColor3fv((m_hover_id != -1) ? m_drag_color : m_highlight_color));
::glBegin(GL_LINES);
::glVertex3f(0.0f, 0.0f, 0.0f);
::glVertex3dv(m_grabbers[0].center.data());
- ::glEnd();
+ glsafe(::glEnd());
::memcpy((void*)m_grabbers[0].color, (const void*)m_highlight_color, 3 * sizeof(float));
render_grabbers(box);
@@ -320,56 +320,56 @@ void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool pick
}
if (!picking)
- ::glEnable(GL_LIGHTING);
+ glsafe(::glEnable(GL_LIGHTING));
- ::glColor3fv(color);
- ::glPushMatrix();
- ::glTranslated(m_grabbers[0].center(0), m_grabbers[0].center(1), m_grabbers[0].center(2));
- ::glRotated(Geometry::rad2deg(m_angle), 0.0, 0.0, 1.0);
- ::glRotated(90.0, 1.0, 0.0, 0.0);
- ::glTranslated(0.0, 0.0, 2.0 * size);
+ glsafe(::glColor3fv(color));
+ glsafe(::glPushMatrix());
+ glsafe(::glTranslated(m_grabbers[0].center(0), m_grabbers[0].center(1), m_grabbers[0].center(2)));
+ glsafe(::glRotated(Geometry::rad2deg(m_angle), 0.0, 0.0, 1.0));
+ glsafe(::glRotated(90.0, 1.0, 0.0, 0.0));
+ glsafe(::glTranslated(0.0, 0.0, 2.0 * size));
::gluQuadricOrientation(m_quadric, GLU_OUTSIDE);
::gluCylinder(m_quadric, 0.75 * size, 0.0, 3.0 * size, 36, 1);
::gluQuadricOrientation(m_quadric, GLU_INSIDE);
::gluDisk(m_quadric, 0.0, 0.75 * size, 36, 1);
- ::glPopMatrix();
- ::glPushMatrix();
- ::glTranslated(m_grabbers[0].center(0), m_grabbers[0].center(1), m_grabbers[0].center(2));
- ::glRotated(Geometry::rad2deg(m_angle), 0.0, 0.0, 1.0);
- ::glRotated(-90.0, 1.0, 0.0, 0.0);
- ::glTranslated(0.0, 0.0, 2.0 * size);
+ glsafe(::glPopMatrix());
+ glsafe(::glPushMatrix());
+ glsafe(::glTranslated(m_grabbers[0].center(0), m_grabbers[0].center(1), m_grabbers[0].center(2)));
+ glsafe(::glRotated(Geometry::rad2deg(m_angle), 0.0, 0.0, 1.0));
+ glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0));
+ glsafe(::glTranslated(0.0, 0.0, 2.0 * size));
::gluQuadricOrientation(m_quadric, GLU_OUTSIDE);
::gluCylinder(m_quadric, 0.75 * size, 0.0, 3.0 * size, 36, 1);
::gluQuadricOrientation(m_quadric, GLU_INSIDE);
::gluDisk(m_quadric, 0.0, 0.75 * size, 36, 1);
- ::glPopMatrix();
+ glsafe(::glPopMatrix());
if (!picking)
- ::glDisable(GL_LIGHTING);
+ glsafe(::glDisable(GL_LIGHTING));
}
void GLGizmoRotate::transform_to_local(const Selection& selection) const
{
- ::glTranslated(m_center(0), m_center(1), m_center(2));
+ glsafe(::glTranslated(m_center(0), m_center(1), m_center(2)));
if (selection.is_single_volume() || selection.is_single_modifier() || selection.requires_local_axes())
{
Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true);
- ::glMultMatrixd(orient_matrix.data());
+ glsafe(::glMultMatrixd(orient_matrix.data()));
}
switch (m_axis)
{
case X:
{
- ::glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
- ::glRotatef(-90.0f, 0.0f, 0.0f, 1.0f);
+ glsafe(::glRotatef(90.0f, 0.0f, 1.0f, 0.0f));
+ glsafe(::glRotatef(-90.0f, 0.0f, 0.0f, 1.0f));
break;
}
case Y:
{
- ::glRotatef(-90.0f, 0.0f, 0.0f, 1.0f);
- ::glRotatef(-90.0f, 0.0f, 1.0f, 0.0f);
+ glsafe(::glRotatef(-90.0f, 0.0f, 0.0f, 1.0f));
+ glsafe(::glRotatef(-90.0f, 0.0f, 1.0f, 0.0f));
break;
}
default:
@@ -472,7 +472,7 @@ void GLGizmoRotate3D::on_stop_dragging()
void GLGizmoRotate3D::on_render(const Selection& selection) const
{
- ::glClear(GL_DEPTH_BUFFER_BIT);
+ glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
if ((m_hover_id == -1) || (m_hover_id == 0))
m_gizmos[X].render(selection);
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp
index e9f7e2ae2d..a4f3969341 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp
@@ -108,8 +108,8 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
((m_hover_id == 6) || (m_hover_id == 7) || (m_hover_id == 8) || (m_hover_id == 9)))
set_tooltip("X/Y/Z");
- ::glClear(GL_DEPTH_BUFFER_BIT);
- ::glEnable(GL_DEPTH_TEST);
+ glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
+ glsafe(::glEnable(GL_DEPTH_TEST));
BoundingBoxf3 box;
Transform3d transform = Transform3d::Identity();
@@ -187,7 +187,7 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
m_grabbers[i].angles = angles;
}
- ::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f);
+ glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f));
const BoundingBoxf3& selection_box = selection.get_bounding_box();
@@ -198,20 +198,20 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
// draw connections
if (m_grabbers[0].enabled && m_grabbers[1].enabled)
{
- ::glColor3fv(m_grabbers[0].color);
+ glsafe(::glColor3fv(m_grabbers[0].color));
render_grabbers_connection(0, 1);
}
if (m_grabbers[2].enabled && m_grabbers[3].enabled)
{
- ::glColor3fv(m_grabbers[2].color);
+ glsafe(::glColor3fv(m_grabbers[2].color));
render_grabbers_connection(2, 3);
}
if (m_grabbers[4].enabled && m_grabbers[5].enabled)
{
- ::glColor3fv(m_grabbers[4].color);
+ glsafe(::glColor3fv(m_grabbers[4].color));
render_grabbers_connection(4, 5);
}
- ::glColor3fv(m_base_color);
+ glsafe(::glColor3fv(m_base_color));
render_grabbers_connection(6, 7);
render_grabbers_connection(7, 8);
render_grabbers_connection(8, 9);
@@ -222,7 +222,7 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
else if ((m_hover_id == 0) || (m_hover_id == 1))
{
// draw connection
- ::glColor3fv(m_grabbers[0].color);
+ glsafe(::glColor3fv(m_grabbers[0].color));
render_grabbers_connection(0, 1);
// draw grabbers
m_grabbers[0].render(true, grabber_mean_size);
@@ -231,7 +231,7 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
else if ((m_hover_id == 2) || (m_hover_id == 3))
{
// draw connection
- ::glColor3fv(m_grabbers[2].color);
+ glsafe(::glColor3fv(m_grabbers[2].color));
render_grabbers_connection(2, 3);
// draw grabbers
m_grabbers[2].render(true, grabber_mean_size);
@@ -240,7 +240,7 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
else if ((m_hover_id == 4) || (m_hover_id == 5))
{
// draw connection
- ::glColor3fv(m_grabbers[4].color);
+ glsafe(::glColor3fv(m_grabbers[4].color));
render_grabbers_connection(4, 5);
// draw grabbers
m_grabbers[4].render(true, grabber_mean_size);
@@ -249,7 +249,7 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
else if (m_hover_id >= 6)
{
// draw connection
- ::glColor3fv(m_drag_color);
+ glsafe(::glColor3fv(m_drag_color));
render_grabbers_connection(6, 7);
render_grabbers_connection(7, 8);
render_grabbers_connection(8, 9);
@@ -264,7 +264,7 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
void GLGizmoScale3D::on_render_for_picking(const Selection& selection) const
{
- ::glDisable(GL_DEPTH_TEST);
+ glsafe(::glDisable(GL_DEPTH_TEST));
render_grabbers_for_picking(selection.get_bounding_box());
}
@@ -291,7 +291,7 @@ void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int
::glBegin(GL_LINES);
::glVertex3dv(m_grabbers[id_1].center.data());
::glVertex3dv(m_grabbers[id_2].center.data());
- ::glEnd();
+ glsafe(::glEnd());
}
}
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
index 2264c541e8..63bff63056 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
@@ -1,5 +1,6 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoSlaSupports.hpp"
+#include "slic3r/GUI/GLCanvas3D.hpp"
#include
@@ -84,13 +85,13 @@ void GLGizmoSlaSupports::on_render(const Selection& selection) const
return;
}
- ::glEnable(GL_BLEND);
- ::glEnable(GL_DEPTH_TEST);
+ glsafe(::glEnable(GL_BLEND));
+ glsafe(::glEnable(GL_DEPTH_TEST));
render_points(selection, false);
render_selection_rectangle();
- ::glDisable(GL_BLEND);
+ glsafe(::glDisable(GL_BLEND));
}
void GLGizmoSlaSupports::render_selection_rectangle() const
@@ -98,44 +99,44 @@ void GLGizmoSlaSupports::render_selection_rectangle() const
if (!m_selection_rectangle_active)
return;
- ::glLineWidth(1.5f);
+ glsafe(::glLineWidth(1.5f));
float render_color[3] = {1.f, 0.f, 0.f};
- ::glColor3fv(render_color);
+ glsafe(::glColor3fv(render_color));
- ::glPushAttrib(GL_TRANSFORM_BIT); // remember current MatrixMode
+ glsafe(::glPushAttrib(GL_TRANSFORM_BIT)); // remember current MatrixMode
- ::glMatrixMode(GL_MODELVIEW); // cache modelview matrix and set to identity
- ::glPushMatrix();
- ::glLoadIdentity();
+ glsafe(::glMatrixMode(GL_MODELVIEW)); // cache modelview matrix and set to identity
+ glsafe(::glPushMatrix());
+ glsafe(::glLoadIdentity());
- ::glMatrixMode(GL_PROJECTION); // cache projection matrix and set to identity
- ::glPushMatrix();
- ::glLoadIdentity();
+ glsafe(::glMatrixMode(GL_PROJECTION)); // cache projection matrix and set to identity
+ glsafe(::glPushMatrix());
+ glsafe(::glLoadIdentity());
- ::glOrtho(0.f, m_canvas_width, m_canvas_height, 0.f, -1.f, 1.f); // set projection matrix so that world coords = window coords
+ glsafe(::glOrtho(0.f, m_canvas_width, m_canvas_height, 0.f, -1.f, 1.f)); // set projection matrix so that world coords = window coords
// render the selection rectangle (window coordinates):
- ::glPushAttrib(GL_ENABLE_BIT);
- ::glLineStipple(4, 0xAAAA);
- ::glEnable(GL_LINE_STIPPLE);
+ glsafe(::glPushAttrib(GL_ENABLE_BIT));
+ glsafe(::glLineStipple(4, 0xAAAA));
+ glsafe(::glEnable(GL_LINE_STIPPLE));
::glBegin(GL_LINE_LOOP);
::glVertex3f((GLfloat)m_selection_rectangle_start_corner(0), (GLfloat)m_selection_rectangle_start_corner(1), (GLfloat)0.5f);
::glVertex3f((GLfloat)m_selection_rectangle_end_corner(0), (GLfloat)m_selection_rectangle_start_corner(1), (GLfloat)0.5f);
::glVertex3f((GLfloat)m_selection_rectangle_end_corner(0), (GLfloat)m_selection_rectangle_end_corner(1), (GLfloat)0.5f);
::glVertex3f((GLfloat)m_selection_rectangle_start_corner(0), (GLfloat)m_selection_rectangle_end_corner(1), (GLfloat)0.5f);
- ::glEnd();
- ::glPopAttrib();
+ glsafe(::glEnd());
+ glsafe(::glPopAttrib());
- ::glPopMatrix(); // restore former projection matrix
- ::glMatrixMode(GL_MODELVIEW);
- ::glPopMatrix(); // restore former modelview matrix
- ::glPopAttrib(); // restore former MatrixMode
+ glsafe(::glPopMatrix()); // restore former projection matrix
+ glsafe(::glMatrixMode(GL_MODELVIEW));
+ glsafe(::glPopMatrix()); // restore former modelview matrix
+ glsafe(::glPopAttrib()); // restore former MatrixMode
}
void GLGizmoSlaSupports::on_render_for_picking(const Selection& selection) const
{
- ::glEnable(GL_DEPTH_TEST);
+ glsafe(::glEnable(GL_DEPTH_TEST));
render_points(selection, true);
}
@@ -146,16 +147,16 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
return;
if (!picking)
- ::glEnable(GL_LIGHTING);
+ glsafe(::glEnable(GL_LIGHTING));
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
double z_shift = vol->get_sla_shift_z();
const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse();
const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix();
- ::glPushMatrix();
- ::glTranslated(0.0, 0.0, z_shift);
- ::glMultMatrixd(instance_matrix.data());
+ glsafe(::glPushMatrix());
+ glsafe(::glTranslated(0.0, 0.0, z_shift));
+ glsafe(::glMultMatrixd(instance_matrix.data()));
float render_color[3];
for (int i = 0; i < (int)m_editing_mode_cache.size(); ++i)
@@ -187,14 +188,14 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
for (unsigned char i=0; i<3; ++i) render_color[i] = 0.5f;
}
}
- ::glColor3fv(render_color);
+ glsafe(::glColor3fv(render_color));
float render_color_emissive[4] = { 0.5f * render_color[0], 0.5f * render_color[1], 0.5f * render_color[2], 1.f};
- ::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive);
+ glsafe(::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive));
// Inverse matrix of the instance scaling is applied so that the mark does not scale with the object.
- ::glPushMatrix();
- ::glTranslated(support_point.pos(0), support_point.pos(1), support_point.pos(2));
- ::glMultMatrixd(instance_scaling_matrix_inverse.data());
+ glsafe(::glPushMatrix());
+ glsafe(::glTranslated(support_point.pos(0), support_point.pos(1), support_point.pos(2)));
+ glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data()));
// Matrices set, we can render the point mark now.
// If in editing mode, we'll also render a cone pointing to the sphere.
@@ -205,31 +206,31 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
Eigen::Quaterniond q;
q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * m_editing_mode_cache[i].normal.cast());
Eigen::AngleAxisd aa(q);
- ::glRotated(aa.angle() * (180./M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2));
+ glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2)));
const float cone_radius = 0.25f; // mm
const float cone_height = 0.75f;
- ::glPushMatrix();
- ::glTranslatef(0.f, 0.f, m_editing_mode_cache[i].support_point.head_front_radius * RenderPointScale);
+ glsafe(::glPushMatrix());
+ glsafe(::glTranslatef(0.f, 0.f, m_editing_mode_cache[i].support_point.head_front_radius * RenderPointScale));
::gluCylinder(m_quadric, 0.f, cone_radius, cone_height, 24, 1);
- ::glTranslatef(0.f, 0.f, cone_height);
+ glsafe(::glTranslatef(0.f, 0.f, cone_height));
::gluDisk(m_quadric, 0.0, cone_radius, 24, 1);
- ::glPopMatrix();
+ glsafe(::glPopMatrix());
}
::gluSphere(m_quadric, m_editing_mode_cache[i].support_point.head_front_radius * RenderPointScale, 24, 12);
- ::glPopMatrix();
+ glsafe(::glPopMatrix());
}
{
// Reset emissive component to zero (the default value)
float render_color_emissive[4] = { 0.f, 0.f, 0.f, 1.f };
- ::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive);
+ glsafe(::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive));
}
if (!picking)
- ::glDisable(GL_LIGHTING);
+ glsafe(::glDisable(GL_LIGHTING));
- ::glPopMatrix();
+ glsafe(::glPopMatrix());
}
bool GLGizmoSlaSupports::is_mesh_update_necessary() const
@@ -272,17 +273,15 @@ std::pair GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse
if (m_V.size() == 0)
update_mesh();
- Eigen::Matrix viewport;
- ::glGetIntegerv(GL_VIEWPORT, viewport.data());
- Eigen::Matrix modelview_matrix;
- ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data());
- Eigen::Matrix projection_matrix;
- ::glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix.data());
+ const Camera& camera = m_parent.get_camera();
+ const std::array& viewport = camera.get_viewport();
+ const Transform3d& modelview_matrix = camera.get_view_matrix();
+ const Transform3d& projection_matrix = camera.get_projection_matrix();
Vec3d point1;
Vec3d point2;
- ::gluUnProject(mouse_pos(0), viewport(3)-mouse_pos(1), 0.f, modelview_matrix.data(), projection_matrix.data(), viewport.data(), &point1(0), &point1(1), &point1(2));
- ::gluUnProject(mouse_pos(0), viewport(3)-mouse_pos(1), 1.f, modelview_matrix.data(), projection_matrix.data(), viewport.data(), &point2(0), &point2(1), &point2(2));
+ ::gluUnProject(mouse_pos(0), viewport[3] - mouse_pos(1), 0.f, modelview_matrix.data(), projection_matrix.data(), viewport.data(), &point1(0), &point1(1), &point1(2));
+ ::gluUnProject(mouse_pos(0), viewport[3] - mouse_pos(1), 1.f, modelview_matrix.data(), projection_matrix.data(), viewport.data(), &point2(0), &point2(1), &point2(2));
igl::Hit hit;
@@ -368,12 +367,10 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
// left up with selection rectangle - select points inside the rectangle:
if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp) && m_selection_rectangle_active) {
const Transform3d& instance_matrix = m_model_object->instances[m_active_instance]->get_transformation().get_matrix();
- GLint viewport[4];
- ::glGetIntegerv(GL_VIEWPORT, viewport);
- GLdouble modelview_matrix[16];
- ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix);
- GLdouble projection_matrix[16];
- ::glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix);
+ const Camera& camera = m_parent.get_camera();
+ const std::array& viewport = camera.get_viewport();
+ const Transform3d& modelview_matrix = camera.get_view_matrix();
+ const Transform3d& projection_matrix = camera.get_projection_matrix();
const Selection& selection = m_parent.get_selection();
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
@@ -384,7 +381,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
const Transform3d& instance_matrix_no_translation = volume->get_instance_transformation().get_matrix(true);
// we'll recover current look direction from the modelview matrix (in world coords)...
- Vec3f direction_to_camera(modelview_matrix[2], modelview_matrix[6], modelview_matrix[10]);
+ Vec3f direction_to_camera = camera.get_dir_forward().cast();
// ...and transform it to model coords.
direction_to_camera = (instance_matrix_no_translation.inverse().cast() * direction_to_camera).normalized().eval();
@@ -394,8 +391,8 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
Vec3f pos = instance_matrix.cast() * support_point.pos;
pos(2) += z_offset;
GLdouble out_x, out_y, out_z;
- ::gluProject((GLdouble)pos(0), (GLdouble)pos(1), (GLdouble)pos(2), modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z);
- out_y = m_canvas_height - out_y;
+ ::gluProject((GLdouble)pos(0), (GLdouble)pos(1), (GLdouble)pos(2), (GLdouble*)modelview_matrix.data(), (GLdouble*)projection_matrix.data(), (GLint*)viewport.data(), &out_x, &out_y, &out_z);
+ out_y = m_canvas_height - out_y;
if (rectangle.contains(Point(out_x, out_y))) {
bool is_obscured = false;
@@ -729,40 +726,45 @@ std::string GLGizmoSlaSupports::on_get_name() const
void GLGizmoSlaSupports::on_set_state()
{
- if (m_state == On && m_old_state != On) { // the gizmo was just turned on
+ // Following is called through CallAfter, because otherwise there was a problem
+ // on OSX with the wxMessageDialog being shown several times when clicked into.
- if (is_mesh_update_necessary())
- update_mesh();
+ wxGetApp().CallAfter([this]() {
+ if (m_state == On && m_old_state != On) { // the gizmo was just turned on
- // we'll now reload support points:
- if (m_model_object)
- editing_mode_reload_cache();
+ if (is_mesh_update_necessary())
+ update_mesh();
- m_parent.toggle_model_objects_visibility(false);
- if (m_model_object)
- m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance);
+ // we'll now reload support points:
+ if (m_model_object)
+ editing_mode_reload_cache();
- // Set default head diameter from config.
- const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
- m_new_point_head_diameter = static_cast(cfg.option("support_head_front_diameter"))->value;
- }
- if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
- if (m_model_object) {
- if (m_unsaved_changes) {
- wxMessageDialog dlg(GUI::wxGetApp().plater(), _(L("Do you want to save your manually edited support points ?\n")),
- _(L("Save changes?")), wxICON_QUESTION | wxYES | wxNO);
- if (dlg.ShowModal() == wxID_YES)
- editing_mode_apply_changes();
- else
- editing_mode_discard_changes();
- }
+ m_parent.toggle_model_objects_visibility(false);
+ if (m_model_object)
+ m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance);
+
+ // Set default head diameter from config.
+ const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
+ m_new_point_head_diameter = static_cast(cfg.option("support_head_front_diameter"))->value;
}
+ if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
+ if (m_model_object) {
+ if (m_unsaved_changes) {
+ wxMessageDialog dlg(GUI::wxGetApp().mainframe, _(L("Do you want to save your manually edited support points ?\n")),
+ _(L("Save changes?")), wxICON_QUESTION | wxYES | wxNO);
+ if (dlg.ShowModal() == wxID_YES)
+ editing_mode_apply_changes();
+ else
+ editing_mode_discard_changes();
+ }
+ }
- m_parent.toggle_model_objects_visibility(true);
- m_editing_mode = false; // so it is not active next time the gizmo opens
- m_editing_mode_cache.clear();
- }
- m_old_state = m_state;
+ m_parent.toggle_model_objects_visibility(true);
+ m_editing_mode = false; // so it is not active next time the gizmo opens
+ m_editing_mode_cache.clear();
+ }
+ m_old_state = m_state;
+ });
}
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
index bb3cf06ce4..126aeab6e4 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
@@ -2,6 +2,7 @@
#define slic3r_GLGizmoSlaSupports_hpp_
#include "GLGizmoBase.hpp"
+#include "GLGizmos.hpp"
// There is an L function in igl that would be overridden by our localization macro - let's undefine it...
#undef L
diff --git a/src/slic3r/GUI/Gizmos/GLGizmos.hpp b/src/slic3r/GUI/Gizmos/GLGizmos.hpp
index 8c5e25669d..c45b7648db 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmos.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmos.hpp
@@ -1,6 +1,21 @@
#ifndef slic3r_GLGizmos_hpp_
#define slic3r_GLGizmos_hpp_
+// this describes events being passed from GLCanvas3D to SlaSupport gizmo
+enum class SLAGizmoEventType {
+ LeftDown = 1,
+ LeftUp,
+ RightDown,
+ Dragging,
+ Delete,
+ SelectAll,
+ ShiftUp,
+ ApplyChanges,
+ DiscardChanges,
+ AutomaticGeneration,
+ ManualEditing
+};
+
#include "slic3r/GUI/Gizmos/GLGizmoMove.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoScale.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoRotate.hpp"
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
new file mode 100644
index 0000000000..3935e240a7
--- /dev/null
+++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
@@ -0,0 +1,1215 @@
+#include "libslic3r/libslic3r.h"
+#include "GLGizmosManager.hpp"
+#include "slic3r/GUI/GLCanvas3D.hpp"
+#include "slic3r/GUI/3DScene.hpp"
+#include "slic3r/GUI/GUI_App.hpp"
+#include "slic3r/GUI/GUI_ObjectManipulation.hpp"
+
+#include
+#include
+
+namespace Slic3r {
+namespace GUI {
+
+#if ENABLE_SVG_ICONS
+ const float GLGizmosManager::Default_Icons_Size = 64;
+#endif // ENABLE_SVG_ICONS
+
+GLGizmosManager::GLGizmosManager()
+ : m_enabled(false)
+#if ENABLE_SVG_ICONS
+ , m_icons_texture_dirty(true)
+#endif // ENABLE_SVG_ICONS
+ , m_current(Undefined)
+#if ENABLE_SVG_ICONS
+ , m_overlay_icons_size(Default_Icons_Size)
+ , m_overlay_scale(1.0f)
+ , m_overlay_border(5.0f)
+ , m_overlay_gap_y(5.0f)
+ , m_tooltip("")
+{
+}
+#else
+{
+ set_overlay_scale(1.0);
+}
+#endif // ENABLE_SVG_ICONS
+
+GLGizmosManager::~GLGizmosManager()
+{
+ reset();
+}
+
+bool GLGizmosManager::init(GLCanvas3D& parent)
+{
+#if !ENABLE_SVG_ICONS
+ m_icons_texture.metadata.filename = "gizmos.png";
+ m_icons_texture.metadata.icon_size = 64;
+
+ if (!m_icons_texture.metadata.filename.empty())
+ {
+ if (!m_icons_texture.texture.load_from_file(resources_dir() + "/icons/" + m_icons_texture.metadata.filename, false))
+ {
+ reset();
+ return false;
+ }
+ }
+#endif // !ENABLE_SVG_ICONS
+
+ m_background_texture.metadata.filename = "toolbar_background.png";
+ m_background_texture.metadata.left = 16;
+ m_background_texture.metadata.top = 16;
+ m_background_texture.metadata.right = 16;
+ m_background_texture.metadata.bottom = 16;
+
+ if (!m_background_texture.metadata.filename.empty())
+ {
+ if (!m_background_texture.texture.load_from_file(resources_dir() + "/icons/" + m_background_texture.metadata.filename, false))
+ {
+ reset();
+ return false;
+ }
+ }
+
+#if ENABLE_SVG_ICONS
+ GLGizmoBase* gizmo = new GLGizmoMove3D(parent, "move.svg", 0);
+#else
+ GLGizmoBase* gizmo = new GLGizmoMove3D(parent, 0);
+#endif // ENABLE_SVG_ICONS
+ if (gizmo == nullptr)
+ return false;
+
+ if (!gizmo->init())
+ return false;
+
+ m_gizmos.insert(GizmosMap::value_type(Move, gizmo));
+
+#if ENABLE_SVG_ICONS
+ gizmo = new GLGizmoScale3D(parent, "scale.svg", 1);
+#else
+ gizmo = new GLGizmoScale3D(parent, 1);
+#endif // ENABLE_SVG_ICONS
+ if (gizmo == nullptr)
+ return false;
+
+ if (!gizmo->init())
+ return false;
+
+ m_gizmos.insert(GizmosMap::value_type(Scale, gizmo));
+
+#if ENABLE_SVG_ICONS
+ gizmo = new GLGizmoRotate3D(parent, "rotate.svg", 2);
+#else
+ gizmo = new GLGizmoRotate3D(parent, 2);
+#endif // ENABLE_SVG_ICONS
+ if (gizmo == nullptr)
+ {
+ reset();
+ return false;
+ }
+
+ if (!gizmo->init())
+ {
+ reset();
+ return false;
+ }
+
+ m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo));
+
+#if ENABLE_SVG_ICONS
+ gizmo = new GLGizmoFlatten(parent, "place.svg", 3);
+#else
+ gizmo = new GLGizmoFlatten(parent, 3);
+#endif // ENABLE_SVG_ICONS
+ if (gizmo == nullptr)
+ return false;
+
+ if (!gizmo->init()) {
+ reset();
+ return false;
+ }
+
+ m_gizmos.insert(GizmosMap::value_type(Flatten, gizmo));
+
+#if ENABLE_SVG_ICONS
+ gizmo = new GLGizmoCut(parent, "cut.svg", 4);
+#else
+ gizmo = new GLGizmoCut(parent, 4);
+#endif // ENABLE_SVG_ICONS
+ if (gizmo == nullptr)
+ return false;
+
+ if (!gizmo->init()) {
+ reset();
+ return false;
+ }
+
+ m_gizmos.insert(GizmosMap::value_type(Cut, gizmo));
+
+#if ENABLE_SVG_ICONS
+ gizmo = new GLGizmoSlaSupports(parent, "sla_supports.svg", 5);
+#else
+ gizmo = new GLGizmoSlaSupports(parent, 5);
+#endif // ENABLE_SVG_ICONS
+ if (gizmo == nullptr)
+ return false;
+
+ if (!gizmo->init()) {
+ reset();
+ return false;
+ }
+
+ m_gizmos.insert(GizmosMap::value_type(SlaSupports, gizmo));
+
+ return true;
+}
+
+#if ENABLE_SVG_ICONS
+void GLGizmosManager::set_overlay_icon_size(float size)
+{
+ if (m_overlay_icons_size != size)
+ {
+ m_overlay_icons_size = size;
+ m_icons_texture_dirty = true;
+ }
+}
+#endif // ENABLE_SVG_ICONS
+
+void GLGizmosManager::set_overlay_scale(float scale)
+{
+#if ENABLE_SVG_ICONS
+ if (m_overlay_scale != scale)
+ {
+ m_overlay_scale = scale;
+ m_icons_texture_dirty = true;
+ }
+#else
+ m_overlay_icons_scale = scale;
+ m_overlay_border = 5.0f * scale;
+ m_overlay_gap_y = 5.0f * scale;
+#endif // ENABLE_SVG_ICONS
+}
+
+void GLGizmosManager::refresh_on_off_state(const Selection& selection)
+{
+ GizmosMap::iterator it = m_gizmos.find(m_current);
+ if ((it != m_gizmos.end()) && (it->second != nullptr))
+ {
+ if (!it->second->is_activable(selection))
+ {
+ it->second->set_state(GLGizmoBase::Off);
+ m_current = Undefined;
+ }
+ }
+}
+
+void GLGizmosManager::reset_all_states()
+{
+ if (!m_enabled)
+ return;
+
+ for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
+ {
+ if (it->second != nullptr)
+ {
+ it->second->set_state(GLGizmoBase::Off);
+ it->second->set_hover_id(-1);
+ }
+ }
+
+ m_current = Undefined;
+}
+
+void GLGizmosManager::set_hover_id(int id)
+{
+ if (!m_enabled)
+ return;
+
+ for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
+ {
+ if ((it->second != nullptr) && (it->second->get_state() == GLGizmoBase::On))
+ it->second->set_hover_id(id);
+ }
+}
+
+void GLGizmosManager::enable_grabber(EType type, unsigned int id, bool enable)
+{
+ if (!m_enabled)
+ return;
+
+ GizmosMap::const_iterator it = m_gizmos.find(type);
+ if (it != m_gizmos.end())
+ {
+ if (enable)
+ it->second->enable_grabber(id);
+ else
+ it->second->disable_grabber(id);
+ }
+}
+
+void GLGizmosManager::update(const Linef3& mouse_ray, const Selection& selection, bool shift_down, const Point* mouse_pos)
+{
+ if (!m_enabled)
+ return;
+
+ GLGizmoBase* curr = get_current();
+ if (curr != nullptr)
+ curr->update(GLGizmoBase::UpdateData(mouse_ray, mouse_pos, shift_down), selection);
+}
+
+void GLGizmosManager::update_data(GLCanvas3D& canvas)
+{
+ if (!m_enabled)
+ return;
+
+ const Selection& selection = canvas.get_selection();
+
+ bool enable_move_z = !selection.is_wipe_tower();
+ enable_grabber(Move, 2, enable_move_z);
+ bool enable_scale_xyz = selection.is_single_full_instance() || selection.is_single_volume() || selection.is_single_modifier();
+ for (int i = 0; i < 6; ++i)
+ {
+ enable_grabber(Scale, i, enable_scale_xyz);
+ }
+
+ if (selection.is_single_full_instance())
+ {
+ // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first
+ const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
+ set_scale(volume->get_instance_scaling_factor());
+ set_rotation(Vec3d::Zero());
+ ModelObject* model_object = selection.get_model()->objects[selection.get_object_idx()];
+ set_flattening_data(model_object);
+ set_sla_support_data(model_object, selection);
+ }
+ else if (selection.is_single_volume() || selection.is_single_modifier())
+ {
+ const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
+ set_scale(volume->get_volume_scaling_factor());
+ set_rotation(Vec3d::Zero());
+ set_flattening_data(nullptr);
+ set_sla_support_data(nullptr, selection);
+ }
+ else
+ {
+ set_scale(Vec3d::Ones());
+ set_rotation(Vec3d::Zero());
+ set_flattening_data(selection.is_from_single_object() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr);
+ set_sla_support_data(nullptr, selection);
+ }
+}
+
+bool GLGizmosManager::is_running() const
+{
+ if (!m_enabled)
+ return false;
+
+ GLGizmoBase* curr = get_current();
+ return (curr != nullptr) ? (curr->get_state() == GLGizmoBase::On) : false;
+}
+
+bool GLGizmosManager::handle_shortcut(int key, const Selection& selection)
+{
+ if (!m_enabled || selection.is_empty())
+ return false;
+
+ EType old_current = m_current;
+ bool handled = false;
+ for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
+ {
+ if ((it->second == nullptr) || !it->second->is_selectable())
+ continue;
+
+ int it_key = it->second->get_shortcut_key();
+
+ if (it->second->is_activable(selection) && ((it_key == key - 64) || (it_key == key - 96)))
+ {
+ if ((it->second->get_state() == GLGizmoBase::On))
+ {
+ it->second->set_state(GLGizmoBase::Off);
+ m_current = Undefined;
+ handled = true;
+ }
+ else if ((it->second->get_state() == GLGizmoBase::Off))
+ {
+ it->second->set_state(GLGizmoBase::On);
+ m_current = it->first;
+ handled = true;
+ }
+ }
+ }
+
+ if (handled && (old_current != Undefined) && (old_current != m_current))
+ {
+ GizmosMap::const_iterator it = m_gizmos.find(old_current);
+ if (it != m_gizmos.end())
+ it->second->set_state(GLGizmoBase::Off);
+ }
+
+ return handled;
+}
+
+bool GLGizmosManager::is_dragging() const
+{
+ if (!m_enabled)
+ return false;
+
+ GLGizmoBase* curr = get_current();
+ return (curr != nullptr) ? curr->is_dragging() : false;
+}
+
+void GLGizmosManager::start_dragging(const Selection& selection)
+{
+ if (!m_enabled)
+ return;
+
+ GLGizmoBase* curr = get_current();
+ if (curr != nullptr)
+ curr->start_dragging(selection);
+}
+
+void GLGizmosManager::stop_dragging()
+{
+ if (!m_enabled)
+ return;
+
+ GLGizmoBase* curr = get_current();
+ if (curr != nullptr)
+ curr->stop_dragging();
+}
+
+Vec3d GLGizmosManager::get_displacement() const
+{
+ if (!m_enabled)
+ return Vec3d::Zero();
+
+ GizmosMap::const_iterator it = m_gizmos.find(Move);
+ return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_displacement() : Vec3d::Zero();
+}
+
+Vec3d GLGizmosManager::get_scale() const
+{
+ if (!m_enabled)
+ return Vec3d::Ones();
+
+ GizmosMap::const_iterator it = m_gizmos.find(Scale);
+ return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_scale() : Vec3d::Ones();
+}
+
+void GLGizmosManager::set_scale(const Vec3d& scale)
+{
+ if (!m_enabled)
+ return;
+
+ GizmosMap::const_iterator it = m_gizmos.find(Scale);
+ if (it != m_gizmos.end())
+ reinterpret_cast(it->second)->set_scale(scale);
+}
+
+Vec3d GLGizmosManager::get_rotation() const
+{
+ if (!m_enabled)
+ return Vec3d::Zero();
+
+ GizmosMap::const_iterator it = m_gizmos.find(Rotate);
+ return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_rotation() : Vec3d::Zero();
+}
+
+void GLGizmosManager::set_rotation(const Vec3d& rotation)
+{
+ if (!m_enabled)
+ return;
+
+ GizmosMap::const_iterator it = m_gizmos.find(Rotate);
+ if (it != m_gizmos.end())
+ reinterpret_cast(it->second)->set_rotation(rotation);
+}
+
+Vec3d GLGizmosManager::get_flattening_normal() const
+{
+ if (!m_enabled)
+ return Vec3d::Zero();
+
+ GizmosMap::const_iterator it = m_gizmos.find(Flatten);
+ return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_flattening_normal() : Vec3d::Zero();
+}
+
+void GLGizmosManager::set_flattening_data(const ModelObject* model_object)
+{
+ if (!m_enabled)
+ return;
+
+ GizmosMap::const_iterator it = m_gizmos.find(Flatten);
+ if (it != m_gizmos.end())
+ reinterpret_cast(it->second)->set_flattening_data(model_object);
+}
+
+void GLGizmosManager::set_sla_support_data(ModelObject* model_object, const Selection& selection)
+{
+ if (!m_enabled)
+ return;
+
+ GizmosMap::const_iterator it = m_gizmos.find(SlaSupports);
+ if (it != m_gizmos.end())
+ reinterpret_cast(it->second)->set_sla_support_data(model_object, selection);
+}
+
+// Returns true if the gizmo used the event to do something, false otherwise.
+bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down)
+{
+ if (!m_enabled)
+ return false;
+
+ GizmosMap::const_iterator it = m_gizmos.find(SlaSupports);
+ if (it != m_gizmos.end())
+ return reinterpret_cast(it->second)->gizmo_event(action, mouse_position, shift_down);
+
+ return false;
+}
+
+void GLGizmosManager::render_current_gizmo(const Selection& selection) const
+{
+ if (!m_enabled)
+ return;
+
+ GLGizmoBase* curr = get_current();
+ if (curr != nullptr)
+ curr->render(selection);
+}
+
+void GLGizmosManager::render_current_gizmo_for_picking_pass(const Selection& selection) const
+{
+ if (!m_enabled)
+ return;
+
+ GLGizmoBase* curr = get_current();
+ if (curr != nullptr)
+ curr->render_for_picking(selection);
+}
+
+void GLGizmosManager::render_overlay(const GLCanvas3D& canvas, const Selection& selection) const
+{
+ if (!m_enabled)
+ return;
+
+#if ENABLE_SVG_ICONS
+ if (m_icons_texture_dirty)
+ generate_icons_texture();
+#endif // ENABLE_SVG_ICONS
+
+ glsafe(::glDisable(GL_DEPTH_TEST));
+
+ glsafe(::glPushMatrix());
+ glsafe(::glLoadIdentity());
+
+ do_render_overlay(canvas, selection);
+
+ glsafe(::glPopMatrix());
+}
+
+bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas)
+{
+ Point pos(evt.GetX(), evt.GetY());
+ Vec2d mouse_pos((double)evt.GetX(), (double)evt.GetY());
+
+ Selection& selection = canvas.get_selection();
+ int selected_object_idx = selection.get_object_idx();
+ bool processed = false;
+
+ // mouse anywhere
+ if (!evt.Dragging() && !evt.Leaving() && !evt.Entering() && (m_mouse_capture.parent != nullptr))
+ {
+ if (m_mouse_capture.any() && (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()))
+ // prevents loosing selection into the scene if mouse down was done inside the toolbar and mouse up was down outside it
+ processed = true;
+
+ m_mouse_capture.reset();
+ }
+
+ // mouse anywhere
+ if (evt.Moving())
+ m_tooltip = update_hover_state(canvas, mouse_pos);
+ else if (evt.LeftUp())
+ m_mouse_capture.left = false;
+ else if (evt.MiddleUp())
+ m_mouse_capture.middle = false;
+ else if (evt.RightUp())
+ m_mouse_capture.right = false;
+ else if (evt.Dragging() && m_mouse_capture.any())
+ // if the button down was done on this toolbar, prevent from dragging into the scene
+ processed = true;
+
+ if (!overlay_contains_mouse(canvas, mouse_pos))
+ {
+ // mouse is outside the toolbar
+ m_tooltip = "";
+
+ if (evt.LeftDown())
+ {
+ if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown()))
+ // the gizmo got the event and took some action, there is no need to do anything more
+ processed = true;
+ else if (!selection.is_empty() && grabber_contains_mouse())
+ {
+ update_data(canvas);
+ selection.start_dragging();
+ start_dragging(selection);
+
+ if (m_current == Flatten)
+ {
+ // Rotate the object so the normal points downward:
+ selection.flattening_rotate(get_flattening_normal());
+ canvas.do_flatten();
+ wxGetApp().obj_manipul()->update_settings_value(selection);
+ }
+
+ canvas.set_as_dirty();
+ processed = true;
+ }
+ }
+ else if (evt.RightDown() && (selected_object_idx != -1) && (m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::RightDown))
+ // event was taken care of by the SlaSupports gizmo
+ processed = true;
+ else if (evt.Dragging() && (canvas.get_move_volume_id() != -1) && (m_current == SlaSupports))
+ // don't allow dragging objects with the Sla gizmo on
+ processed = true;
+ else if (evt.Dragging() && (m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown()))
+ {
+ // the gizmo got the event and took some action, no need to do anything more here
+ canvas.set_as_dirty();
+ processed = true;
+ }
+ else if (evt.Dragging() && is_dragging())
+ {
+ if (!canvas.get_wxglcanvas()->HasCapture())
+ canvas.get_wxglcanvas()->CaptureMouse();
+
+ canvas.set_mouse_as_dragging();
+ update(canvas.mouse_ray(pos), selection, evt.ShiftDown(), &pos);
+
+ switch (m_current)
+ {
+ case Move:
+ {
+ // Apply new temporary offset
+ selection.translate(get_displacement());
+ wxGetApp().obj_manipul()->update_settings_value(selection);
+ break;
+ }
+ case Scale:
+ {
+ // Apply new temporary scale factors
+ selection.scale(get_scale(), evt.AltDown());
+ wxGetApp().obj_manipul()->update_settings_value(selection);
+ break;
+ }
+ case Rotate:
+ {
+ // Apply new temporary rotations
+ TransformationType transformation_type(TransformationType::World_Relative_Joint);
+ if (evt.AltDown())
+ transformation_type.set_independent();
+ selection.rotate(get_rotation(), transformation_type);
+ wxGetApp().obj_manipul()->update_settings_value(selection);
+ break;
+ }
+ default:
+ break;
+ }
+
+ canvas.set_as_dirty();
+ processed = true;
+ }
+ else if (evt.LeftUp() && is_dragging())
+ {
+ switch (m_current)
+ {
+ case Move:
+ {
+ canvas.disable_regenerate_volumes();
+ canvas.do_move();
+ break;
+ }
+ case Scale:
+ {
+ canvas.do_scale();
+ break;
+ }
+ case Rotate:
+ {
+ canvas.do_rotate();
+ break;
+ }
+ default:
+ break;
+ }
+
+ stop_dragging();
+ update_data(canvas);
+
+ wxGetApp().obj_manipul()->update_settings_value(selection);
+ // Let the platter know that the dragging finished, so a delayed refresh
+ // of the scene with the background processing data should be performed.
+ canvas.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
+ // updates camera target constraints
+ canvas.refresh_camera_scene_box();
+
+ processed = true;
+ }
+ else if (evt.LeftUp() && (m_current == SlaSupports) && !canvas.is_mouse_dragging())
+ {
+ // in case SLA gizmo is selected, we just pass the LeftUp event and stop processing - neither
+ // object moving or selecting is suppressed in that case
+ gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown());
+ processed = true;
+ }
+ else if (evt.LeftUp() && (m_current == Flatten) && ((canvas.get_hover_volume_id() != -1) || grabber_contains_mouse()))
+ {
+ // to avoid to loose the selection when user clicks an object while the Flatten gizmo is active
+ processed = true;
+ }
+ }
+ else
+ {
+ // mouse inside toolbar
+ if (evt.LeftDown() || evt.LeftDClick())
+ {
+ m_mouse_capture.left = true;
+ m_mouse_capture.parent = &canvas;
+ processed = true;
+ if (!selection.is_empty())
+ {
+ update_on_off_state(canvas, mouse_pos, selection);
+ update_data(canvas);
+ canvas.set_as_dirty();
+ }
+ }
+ else if (evt.MiddleDown())
+ {
+ m_mouse_capture.middle = true;
+ m_mouse_capture.parent = &canvas;
+ }
+ else if (evt.RightDown())
+ {
+ m_mouse_capture.right = true;
+ m_mouse_capture.parent = &canvas;
+ }
+ else if (evt.LeftUp())
+ processed = true;
+ }
+
+ return processed;
+}
+
+bool GLGizmosManager::on_char(wxKeyEvent& evt, GLCanvas3D& canvas)
+{
+ // see include/wx/defs.h enum wxKeyCode
+ int keyCode = evt.GetKeyCode();
+ int ctrlMask = wxMOD_CONTROL;
+
+ bool processed = false;
+
+ if ((evt.GetModifiers() & ctrlMask) != 0)
+ {
+ switch (keyCode)
+ {
+ case WXK_CONTROL_A:
+ {
+ // Sla gizmo selects all support points
+ if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::SelectAll))
+ processed = true;
+
+ break;
+ }
+ }
+ }
+ else if (!evt.HasModifiers())
+ {
+ switch (keyCode)
+ {
+ // key ESC
+ case WXK_ESCAPE:
+ {
+ if ((m_current != SlaSupports) || !gizmo_event(SLAGizmoEventType::DiscardChanges))
+ reset_all_states();
+
+ processed = true;
+ break;
+ }
+ case WXK_RETURN:
+ {
+ if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::ApplyChanges))
+ processed = true;
+
+ break;
+ }
+#ifdef __APPLE__
+ case WXK_BACK: // the low cost Apple solutions are not equipped with a Delete key, use Backspace instead.
+#else /* __APPLE__ */
+ case WXK_DELETE:
+#endif /* __APPLE__ */
+ {
+ if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::Delete))
+ processed = true;
+
+ break;
+ }
+ case 'A':
+ case 'a':
+ {
+ if (m_current == SlaSupports)
+ {
+ gizmo_event(SLAGizmoEventType::AutomaticGeneration);
+ // set as processed no matter what's returned by gizmo_event() to avoid the calling canvas to process 'A' as arrange
+ processed = true;
+ }
+ break;
+ }
+ case 'M':
+ case 'm':
+ {
+ if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::ManualEditing))
+ processed = true;
+
+ break;
+ }
+ }
+ }
+
+ if (!processed)
+ {
+ if (handle_shortcut(keyCode, canvas.get_selection()))
+ {
+ update_data(canvas);
+ processed = true;
+ }
+ }
+
+ if (processed)
+ canvas.set_as_dirty();
+
+ return processed;
+}
+
+bool GLGizmosManager::on_key(wxKeyEvent& evt, GLCanvas3D& canvas)
+{
+ const int keyCode = evt.GetKeyCode();
+ bool processed = false;
+
+ if (evt.GetEventType() == wxEVT_KEY_UP)
+ {
+ if ((m_current == SlaSupports) && (keyCode == WXK_SHIFT) && gizmo_event(SLAGizmoEventType::ShiftUp))
+ // shift has been just released - SLA gizmo might want to close rectangular selection.
+ processed = true;
+ }
+
+ if (processed)
+ canvas.set_as_dirty();
+
+ return processed;
+}
+
+void GLGizmosManager::reset()
+{
+ for (GizmosMap::value_type& gizmo : m_gizmos)
+ {
+ delete gizmo.second;
+ gizmo.second = nullptr;
+ }
+
+ m_gizmos.clear();
+}
+
+void GLGizmosManager::do_render_overlay(const GLCanvas3D& canvas, const Selection& selection) const
+{
+ if (m_gizmos.empty())
+ return;
+
+ float cnv_w = (float)canvas.get_canvas_size().get_width();
+ float cnv_h = (float)canvas.get_canvas_size().get_height();
+ float zoom = canvas.get_camera().zoom;
+ float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
+
+ float height = get_total_overlay_height();
+ float width = get_total_overlay_width();
+#if ENABLE_SVG_ICONS
+ float scaled_border = m_overlay_border * m_overlay_scale * inv_zoom;
+#else
+ float scaled_border = m_overlay_border * inv_zoom;
+#endif // ENABLE_SVG_ICONS
+
+ float top_x = (-0.5f * cnv_w) * inv_zoom;
+ float top_y = (0.5f * height) * inv_zoom;
+
+ float left = top_x;
+ float top = top_y;
+ float right = left + width * inv_zoom;
+ float bottom = top - height * inv_zoom;
+
+ // renders background
+ unsigned int bg_tex_id = m_background_texture.texture.get_id();
+ float bg_tex_width = (float)m_background_texture.texture.get_width();
+ float bg_tex_height = (float)m_background_texture.texture.get_height();
+ if ((bg_tex_id != 0) && (bg_tex_width > 0) && (bg_tex_height > 0))
+ {
+ float inv_bg_tex_width = (bg_tex_width != 0.0f) ? 1.0f / bg_tex_width : 0.0f;
+ float inv_bg_tex_height = (bg_tex_height != 0.0f) ? 1.0f / bg_tex_height : 0.0f;
+
+ float bg_uv_left = 0.0f;
+ float bg_uv_right = 1.0f;
+ float bg_uv_top = 1.0f;
+ float bg_uv_bottom = 0.0f;
+
+ float bg_left = left;
+ float bg_right = right;
+ float bg_top = top;
+ float bg_bottom = bottom;
+ float bg_width = right - left;
+ float bg_height = top - bottom;
+ float bg_min_size = std::min(bg_width, bg_height);
+
+ float bg_uv_i_left = (float)m_background_texture.metadata.left * inv_bg_tex_width;
+ float bg_uv_i_right = 1.0f - (float)m_background_texture.metadata.right * inv_bg_tex_width;
+ float bg_uv_i_top = 1.0f - (float)m_background_texture.metadata.top * inv_bg_tex_height;
+ float bg_uv_i_bottom = (float)m_background_texture.metadata.bottom * inv_bg_tex_height;
+
+ float bg_i_left = bg_left + scaled_border;
+ float bg_i_right = bg_right - scaled_border;
+ float bg_i_top = bg_top - scaled_border;
+ float bg_i_bottom = bg_bottom + scaled_border;
+
+ bg_uv_left = bg_uv_i_left;
+ bg_i_left = bg_left;
+
+ if ((m_overlay_border > 0) && (bg_uv_top != bg_uv_i_top))
+ {
+ if (bg_uv_left != bg_uv_i_left)
+ GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_top, bg_top, { { bg_uv_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_top }, { bg_uv_left, bg_uv_top } });
+
+ GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_top, bg_top, { { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_top }, { bg_uv_i_left, bg_uv_top } });
+
+ if (bg_uv_right != bg_uv_i_right)
+ GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_top, bg_top, { { bg_uv_i_right, bg_uv_i_top }, { bg_uv_right, bg_uv_i_top }, { bg_uv_right, bg_uv_top }, { bg_uv_i_right, bg_uv_top } });
+ }
+
+ if ((m_overlay_border > 0) && (bg_uv_left != bg_uv_i_left))
+ GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_bottom, bg_i_top, { { bg_uv_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_left, bg_uv_i_top } });
+
+ GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_bottom, bg_i_top, { { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top } });
+
+ if ((m_overlay_border > 0) && (bg_uv_right != bg_uv_i_right))
+ GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_bottom, bg_i_top, { { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top } });
+
+ if ((m_overlay_border > 0) && (bg_uv_bottom != bg_uv_i_bottom))
+ {
+ if (bg_uv_left != bg_uv_i_left)
+ GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_bottom, bg_i_bottom, { { bg_uv_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_left, bg_uv_i_bottom } });
+
+ GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_bottom, bg_i_bottom, { { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_right, bg_uv_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom } });
+
+ if (bg_uv_right != bg_uv_i_right)
+ GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_bottom, bg_i_bottom, { { bg_uv_i_right, bg_uv_bottom }, { bg_uv_right, bg_uv_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom } });
+ }
+ }
+
+#if ENABLE_SVG_ICONS
+ top_x += scaled_border;
+ top_y -= scaled_border;
+ float scaled_gap_y = m_overlay_gap_y * m_overlay_scale * inv_zoom;
+
+ float scaled_icons_size = m_overlay_icons_size * m_overlay_scale * inv_zoom;
+ float scaled_stride_y = scaled_icons_size + scaled_gap_y;
+ unsigned int icons_texture_id = m_icons_texture.get_id();
+ unsigned int tex_width = m_icons_texture.get_width();
+ unsigned int tex_height = m_icons_texture.get_height();
+ float inv_tex_width = (tex_width != 0) ? 1.0f / (float)tex_width : 0.0f;
+ float inv_tex_height = (tex_height != 0) ? 1.0f / (float)tex_height : 0.0f;
+#else
+ top_x += m_overlay_border * inv_zoom;
+ top_y -= m_overlay_border * inv_zoom;
+ float scaled_gap_y = m_overlay_gap_y * inv_zoom;
+
+ float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale * inv_zoom;
+ unsigned int icons_texture_id = m_icons_texture.texture.get_id();
+ unsigned int texture_size = m_icons_texture.texture.get_width();
+ float inv_texture_size = (texture_size != 0) ? 1.0f / (float)texture_size : 0.0f;
+#endif // ENABLE_SVG_ICONS
+
+#if ENABLE_SVG_ICONS
+ if ((icons_texture_id == 0) || (tex_width <= 0) || (tex_height <= 0))
+ return;
+#endif // ENABLE_SVG_ICONS
+
+ for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
+ {
+ if ((it->second == nullptr) || !it->second->is_selectable())
+ continue;
+
+ unsigned int sprite_id = it->second->get_sprite_id();
+ GLGizmoBase::EState state = it->second->get_state();
+
+#if ENABLE_SVG_ICONS
+ float u_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_width;
+ float v_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_height;
+ float v_top = sprite_id * v_icon_size;
+ float u_left = state * u_icon_size;
+ float v_bottom = v_top + v_icon_size;
+ float u_right = u_left + u_icon_size;
+#else
+ float uv_icon_size = (float)m_icons_texture.metadata.icon_size * inv_texture_size;
+ float v_top = sprite_id * uv_icon_size;
+ float u_left = state * uv_icon_size;
+ float v_bottom = v_top + uv_icon_size;
+ float u_right = u_left + uv_icon_size;
+#endif // ENABLE_SVG_ICONS
+
+ GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + scaled_icons_size, top_y - scaled_icons_size, top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } });
+ if (it->second->get_state() == GLGizmoBase::On) {
+ float toolbar_top = (float)cnv_h - canvas.get_view_toolbar_height();
+#if ENABLE_SVG_ICONS
+ it->second->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection);
+#else
+ it->second->render_input_window(2.0f * m_overlay_border + icon_size * zoom, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection);
+#endif // ENABLE_SVG_ICONS
+ }
+#if ENABLE_SVG_ICONS
+ top_y -= scaled_stride_y;
+#else
+ top_y -= (scaled_icons_size + scaled_gap_y);
+#endif // ENABLE_SVG_ICONS
+ }
+}
+
+float GLGizmosManager::get_total_overlay_height() const
+{
+#if ENABLE_SVG_ICONS
+ float scaled_icons_size = m_overlay_icons_size * m_overlay_scale;
+ float scaled_border = m_overlay_border * m_overlay_scale;
+ float scaled_gap_y = m_overlay_gap_y * m_overlay_scale;
+ float scaled_stride_y = scaled_icons_size + scaled_gap_y;
+ float height = 2.0f * scaled_border;
+#else
+ float height = 2.0f * m_overlay_border;
+
+ float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale;
+#endif // ENABLE_SVG_ICONS
+
+ for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
+ {
+ if ((it->second == nullptr) || !it->second->is_selectable())
+ continue;
+
+#if ENABLE_SVG_ICONS
+ height += scaled_stride_y;
+#else
+ height += (scaled_icons_size + m_overlay_gap_y);
+#endif // ENABLE_SVG_ICONS
+ }
+
+#if ENABLE_SVG_ICONS
+ return height - scaled_gap_y;
+#else
+ return height - m_overlay_gap_y;
+#endif // ENABLE_SVG_ICONS
+}
+
+float GLGizmosManager::get_total_overlay_width() const
+{
+#if ENABLE_SVG_ICONS
+ return (2.0f * m_overlay_border + m_overlay_icons_size) * m_overlay_scale;
+#else
+ return (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale + 2.0f * m_overlay_border;
+#endif // ENABLE_SVG_ICONS
+}
+
+GLGizmoBase* GLGizmosManager::get_current() const
+{
+ GizmosMap::const_iterator it = m_gizmos.find(m_current);
+ return (it != m_gizmos.end()) ? it->second : nullptr;
+}
+
+#if ENABLE_SVG_ICONS
+bool GLGizmosManager::generate_icons_texture() const
+{
+ std::string path = resources_dir() + "/icons/";
+ std::vector filenames;
+ for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
+ {
+ if (it->second != nullptr)
+ {
+ const std::string& icon_filename = it->second->get_icon_filename();
+ if (!icon_filename.empty())
+ filenames.push_back(path + icon_filename);
+ }
+ }
+
+ std::vector> states;
+ states.push_back(std::make_pair(1, false));
+ states.push_back(std::make_pair(0, false));
+ states.push_back(std::make_pair(0, true));
+
+ bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, (unsigned int)(m_overlay_icons_size * m_overlay_scale));
+ if (res)
+ m_icons_texture_dirty = false;
+
+ return res;
+}
+#endif // ENABLE_SVG_ICONS
+
+void GLGizmosManager::update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection)
+{
+ if (!m_enabled)
+ return;
+
+ float cnv_h = (float)canvas.get_canvas_size().get_height();
+ float height = get_total_overlay_height();
+
+#if ENABLE_SVG_ICONS
+ float scaled_icons_size = m_overlay_icons_size * m_overlay_scale;
+ float scaled_border = m_overlay_border * m_overlay_scale;
+ float scaled_gap_y = m_overlay_gap_y * m_overlay_scale;
+ float scaled_stride_y = scaled_icons_size + scaled_gap_y;
+ float top_y = 0.5f * (cnv_h - height) + scaled_border;
+#else
+ float top_y = 0.5f * (cnv_h - height) + m_overlay_border;
+ float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale;
+#endif // ENABLE_SVG_ICONS
+
+ for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
+ {
+ if ((it->second == nullptr) || !it->second->is_selectable())
+ continue;
+
+#if ENABLE_SVG_ICONS
+ bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size);
+#else
+ bool inside = (m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size);
+#endif // ENABLE_SVG_ICONS
+ if (it->second->is_activable(selection) && inside)
+ {
+ if ((it->second->get_state() == GLGizmoBase::On))
+ {
+ it->second->set_state(GLGizmoBase::Hover);
+ m_current = Undefined;
+ }
+ else if ((it->second->get_state() == GLGizmoBase::Hover))
+ {
+ it->second->set_state(GLGizmoBase::On);
+ m_current = it->first;
+ }
+ }
+ else
+ it->second->set_state(GLGizmoBase::Off);
+
+#if ENABLE_SVG_ICONS
+ top_y += scaled_stride_y;
+#else
+ top_y += (scaled_icons_size + m_overlay_gap_y);
+#endif // ENABLE_SVG_ICONS
+ }
+
+ GizmosMap::iterator it = m_gizmos.find(m_current);
+ if ((it != m_gizmos.end()) && (it->second != nullptr) && (it->second->get_state() != GLGizmoBase::On))
+ it->second->set_state(GLGizmoBase::On);
+}
+
+std::string GLGizmosManager::update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos)
+{
+ std::string name = "";
+
+ if (!m_enabled)
+ return name;
+
+ const Selection& selection = canvas.get_selection();
+
+ float cnv_h = (float)canvas.get_canvas_size().get_height();
+ float height = get_total_overlay_height();
+#if ENABLE_SVG_ICONS
+ float scaled_icons_size = m_overlay_icons_size * m_overlay_scale;
+ float scaled_border = m_overlay_border * m_overlay_scale;
+ float scaled_gap_y = m_overlay_gap_y * m_overlay_scale;
+ float scaled_stride_y = scaled_icons_size + scaled_gap_y;
+ float top_y = 0.5f * (cnv_h - height) + scaled_border;
+#else
+ float top_y = 0.5f * (cnv_h - height) + m_overlay_border;
+ float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale;
+#endif // ENABLE_SVG_ICONS
+
+ for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
+ {
+ if ((it->second == nullptr) || !it->second->is_selectable())
+ continue;
+
+#if ENABLE_SVG_ICONS
+ bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size);
+#else
+ bool inside = (m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size);
+#endif // ENABLE_SVG_ICONS
+ if (inside)
+ name = it->second->get_name();
+
+ if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On))
+ it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off);
+
+#if ENABLE_SVG_ICONS
+ top_y += scaled_stride_y;
+#else
+ top_y += (scaled_icons_size + m_overlay_gap_y);
+#endif // ENABLE_SVG_ICONS
+ }
+
+ return name;
+}
+
+bool GLGizmosManager::overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const
+{
+ if (!m_enabled)
+ return false;
+
+ float cnv_h = (float)canvas.get_canvas_size().get_height();
+ float height = get_total_overlay_height();
+
+#if ENABLE_SVG_ICONS
+ float scaled_icons_size = m_overlay_icons_size * m_overlay_scale;
+ float scaled_border = m_overlay_border * m_overlay_scale;
+ float scaled_gap_y = m_overlay_gap_y * m_overlay_scale;
+ float scaled_stride_y = scaled_icons_size + scaled_gap_y;
+ float top_y = 0.5f * (cnv_h - height) + scaled_border;
+#else
+ float top_y = 0.5f * (cnv_h - height) + m_overlay_border;
+ float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale;
+#endif // ENABLE_SVG_ICONS
+
+ for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
+ {
+ if ((it->second == nullptr) || !it->second->is_selectable())
+ continue;
+
+#if ENABLE_SVG_ICONS
+ if ((scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size))
+#else
+ if ((m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size))
+#endif // ENABLE_SVG_ICONS
+ return true;
+
+#if ENABLE_SVG_ICONS
+ top_y += scaled_stride_y;
+#else
+ top_y += (scaled_icons_size + m_overlay_gap_y);
+#endif // ENABLE_SVG_ICONS
+ }
+
+ return false;
+}
+
+bool GLGizmosManager::grabber_contains_mouse() const
+{
+ if (!m_enabled)
+ return false;
+
+ GLGizmoBase* curr = get_current();
+ return (curr != nullptr) ? (curr->get_hover_id() != -1) : false;
+}
+
+} // namespace GUI
+} // namespace Slic3r
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp
new file mode 100644
index 0000000000..82dbdc0db6
--- /dev/null
+++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp
@@ -0,0 +1,184 @@
+#ifndef slic3r_GUI_GLGizmosManager_hpp_
+#define slic3r_GUI_GLGizmosManager_hpp_
+
+#include "slic3r/GUI/GLTexture.hpp"
+#include "slic3r/GUI/GLToolbar.hpp"
+#include "slic3r/GUI/Gizmos/GLGizmos.hpp"
+
+#include