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. +![Slic3rPE logo](/resources/icons/Slic3r.png?raw=true) + +# 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 @@ - + - + viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + 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 + +namespace Slic3r { +namespace GUI { + +class Selection; +class GLGizmoBase; +class GLCanvas3D; + +class Rect +{ + float m_left; + float m_top; + float m_right; + float m_bottom; + +public: + Rect() : m_left(0.0f) , m_top(0.0f) , m_right(0.0f) , m_bottom(0.0f) {} + + Rect(float left, float top, float right, float bottom) : m_left(left) , m_top(top) , m_right(right) , m_bottom(bottom) {} + + float get_left() const { return m_left; } + void set_left(float left) { m_left = left; } + + float get_top() const { return m_top; } + void set_top(float top) { m_top = top; } + + float get_right() const { return m_right; } + void set_right(float right) { m_right = right; } + + float get_bottom() const { return m_bottom; } + void set_bottom(float bottom) { m_bottom = bottom; } + + float get_width() const { return m_right - m_left; } + float get_height() const { return m_top - m_bottom; } +}; + +class GLGizmosManager +{ +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; + + struct MouseCapture + { + bool left; + bool middle; + bool right; + GLCanvas3D* parent; + + MouseCapture() { reset(); } + + bool any() const { return left || middle || right; } + void reset() { left = middle = right = false; parent = nullptr; } + }; + + MouseCapture m_mouse_capture; + std::string m_tooltip; + +public: + GLGizmosManager(); + ~GLGizmosManager(); + + bool init(GLCanvas3D& parent); + + bool is_enabled() const { return m_enabled; } + void set_enabled(bool enable) { m_enabled = enable; } + +#if ENABLE_SVG_ICONS + void set_overlay_icon_size(float size); +#endif // ENABLE_SVG_ICONS + void set_overlay_scale(float scale); + + void refresh_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); + + void update(const Linef3& mouse_ray, const Selection& selection, bool shift_down, const Point* mouse_pos = nullptr); + void update_data(GLCanvas3D& canvas); + + Rect get_reset_rect_viewport(const GLCanvas3D& canvas) const; + EType get_current_type() const { return m_current; } + + 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; + + const std::string& get_tooltip() const { return m_tooltip; } + + bool on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas); + bool on_char(wxKeyEvent& evt, GLCanvas3D& canvas); + bool on_key(wxKeyEvent& evt, GLCanvas3D& canvas); + +private: + void reset(); + + void do_render_overlay(const GLCanvas3D& canvas, 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 + + void update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection); + std::string update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos); + bool overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const; + bool grabber_contains_mouse() const; +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GUI_GLGizmosManager_hpp_ diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 8a6a71b1b9..03c83cf519 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -19,6 +19,7 @@ #include "libslic3r/libslic3r.h" #include "libslic3r/Utils.hpp" +#include "3DScene.hpp" #include "GUI.hpp" namespace Slic3r { @@ -359,19 +360,19 @@ void ImGuiWrapper::init_font() // Upload texture to graphics system GLint last_texture; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &m_font_texture); - glBindTexture(GL_TEXTURE_2D, m_font_texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + glsafe(::glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture)); + glsafe(::glGenTextures(1, &m_font_texture)); + glsafe(::glBindTexture(GL_TEXTURE_2D, m_font_texture)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + glsafe(::glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)); + glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels)); // Store our identifier io.Fonts->TexID = (ImTextureID)(intptr_t)m_font_texture; // Restore state - glBindTexture(GL_TEXTURE_2D, last_texture); + glsafe(::glBindTexture(GL_TEXTURE_2D, last_texture)); } void ImGuiWrapper::init_input() @@ -466,39 +467,39 @@ void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) // We are using the OpenGL fixed pipeline to make the example code simpler to read! // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers, polygon fill. - GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); - GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); - GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); - glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - glDisable(GL_COLOR_MATERIAL); - glEnable(GL_SCISSOR_TEST); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glEnable(GL_TEXTURE_2D); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + GLint last_texture; glsafe(::glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture)); + GLint last_polygon_mode[2]; glsafe(::glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode)); + GLint last_viewport[4]; glsafe(::glGetIntegerv(GL_VIEWPORT, last_viewport)); + GLint last_scissor_box[4]; glsafe(::glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box)); + glsafe(::glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT)); + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + glsafe(::glDisable(GL_CULL_FACE)); + glsafe(::glDisable(GL_DEPTH_TEST)); + glsafe(::glDisable(GL_LIGHTING)); + glsafe(::glDisable(GL_COLOR_MATERIAL)); + glsafe(::glEnable(GL_SCISSOR_TEST)); + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + glsafe(::glEnableClientState(GL_TEXTURE_COORD_ARRAY)); + glsafe(::glEnableClientState(GL_COLOR_ARRAY)); + glsafe(::glEnable(GL_TEXTURE_2D)); + glsafe(::glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)); + glsafe(::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE)); GLint texture_env_mode = GL_MODULATE; - glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &texture_env_mode); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glsafe(::glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &texture_env_mode)); + glsafe(::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE)); //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound // Setup viewport, orthographic projection matrix // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps. - glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(draw_data->DisplayPos.x, draw_data->DisplayPos.x + draw_data->DisplaySize.x, draw_data->DisplayPos.y + draw_data->DisplaySize.y, draw_data->DisplayPos.y, -1.0f, +1.0f); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); + glsafe(::glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height)); + glsafe(::glMatrixMode(GL_PROJECTION)); + glsafe(::glPushMatrix()); + glsafe(::glLoadIdentity()); + glsafe(::glOrtho(draw_data->DisplayPos.x, draw_data->DisplayPos.x + draw_data->DisplaySize.x, draw_data->DisplayPos.y + draw_data->DisplaySize.y, draw_data->DisplayPos.y, -1.0f, +1.0f)); + glsafe(::glMatrixMode(GL_MODELVIEW)); + glsafe(::glPushMatrix()); + glsafe(::glLoadIdentity()); // Render command lists ImVec2 pos = draw_data->DisplayPos; @@ -507,9 +508,9 @@ void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) const ImDrawList* cmd_list = draw_data->CmdLists[n]; const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; - glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, pos))); - glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv))); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col))); + glsafe(::glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, pos)))); + glsafe(::glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv)))); + glsafe(::glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col)))); for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) { @@ -525,11 +526,11 @@ void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) { // Apply scissor/clipping rectangle - glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)); + glsafe(::glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y))); // Bind texture, Draw - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); - glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer); + glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId)); + glsafe(::glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer)); } } idx_buffer += pcmd->ElemCount; @@ -537,19 +538,19 @@ void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) } // Restore modified state - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, texture_env_mode); - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - glBindTexture(GL_TEXTURE_2D, (GLuint)last_texture); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glPopAttrib(); - glPolygonMode(GL_FRONT, (GLenum)last_polygon_mode[0]); glPolygonMode(GL_BACK, (GLenum)last_polygon_mode[1]); - glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); - glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); + glsafe(::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, texture_env_mode)); + glsafe(::glDisableClientState(GL_COLOR_ARRAY)); + glsafe(::glDisableClientState(GL_TEXTURE_COORD_ARRAY)); + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)last_texture)); + glsafe(::glMatrixMode(GL_MODELVIEW)); + glsafe(::glPopMatrix()); + glsafe(::glMatrixMode(GL_PROJECTION)); + glsafe(::glPopMatrix()); + glsafe(::glPopAttrib()); + glsafe(::glPolygonMode(GL_FRONT, (GLenum)last_polygon_mode[0]); glPolygonMode(GL_BACK, (GLenum)last_polygon_mode[1])); + glsafe(::glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3])); + glsafe(::glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3])); } bool ImGuiWrapper::display_initialized() const @@ -563,7 +564,7 @@ void ImGuiWrapper::destroy_font() if (m_font_texture != 0) { ImGuiIO& io = ImGui::GetIO(); io.Fonts->TexID = 0; - glDeleteTextures(1, &m_font_texture); + glsafe(::glDeleteTextures(1, &m_font_texture)); m_font_texture = 0; } } diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 66e9deec86..b4d25fa5da 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -17,8 +17,7 @@ KBShortcutsDialog::KBShortcutsDialog() auto main_sizer = new wxBoxSizer(wxVERTICAL); // logo -// wxBitmap logo_bmp = wxBitmap(from_u8(Slic3r::var("Slic3r_32px.png")), wxBITMAP_TYPE_PNG); - const wxBitmap logo_bmp = create_scaled_bitmap("Slic3r_32px.png"); + const wxBitmap logo_bmp = create_scaled_bitmap("Slic3r_32px.png", 32); // fonts wxFont head_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold(); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index d69480b28f..059d04f856 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -32,7 +32,7 @@ namespace Slic3r { namespace GUI { MainFrame::MainFrame() : -wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe"), +DPIFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe"), m_printhost_queue_dlg(new PrintHostQueueDialog(this)) { // Load the icon either from the exe, or from the ico file. @@ -56,9 +56,13 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL // initialize default width_unit according to the width of the one symbol ("x") of the current system font const wxSize size = GetTextExtent("m"); -// wxGetApp().set_em_unit(size.x-1); wxGetApp().set_em_unit(std::max(10, size.x - 1)); + /* Load default preset bitmaps before a tabpanel initialization, + * but after filling of an em_unit value + */ + wxGetApp().preset_bundle->load_default_preset_bitmaps(); + // initialize tabpanel and menubar init_tabpanel(); init_menubar(); @@ -252,6 +256,11 @@ bool MainFrame::can_delete_all() const return (m_plater != nullptr) ? !m_plater->model().objects.empty() : false; } +void MainFrame::on_dpi_changed(const wxRect &suggested_rect) +{ + // TODO +} + void MainFrame::init_menubar() { #ifdef __APPLE__ @@ -388,11 +397,11 @@ void MainFrame::init_menubar() windowMenu->AppendSeparator(); } append_menu_item(windowMenu, wxID_HIGHEST + 2, _(L("P&rint Settings Tab")) + "\tCtrl+2", _(L("Show the print settings")), - [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog.png"); + [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog"); append_menu_item(windowMenu, wxID_HIGHEST + 3, _(L("&Filament Settings Tab")) + "\tCtrl+3", _(L("Show the filament settings")), [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 1); }, "spool.png"); append_menu_item(windowMenu, wxID_HIGHEST + 4, _(L("Print&er Settings Tab")) + "\tCtrl+4", _(L("Show the printer settings")), - [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer_empty.png"); + [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer"); if (m_plater) { windowMenu->AppendSeparator(); wxMenuItem* item_3d = append_menu_item(windowMenu, wxID_HIGHEST + 5, _(L("3&D")) + "\tCtrl+5", _(L("Show the 3D editing view")), diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index b44c73f91f..625e70b83b 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -9,6 +9,7 @@ #include #include +#include "GUI_Utils.hpp" #include "Plater.hpp" #include "Event.hpp" @@ -40,7 +41,7 @@ struct PresetTab { PrinterTechnology technology; }; -class MainFrame : public wxFrame +class MainFrame : public DPIFrame { bool m_loaded {false}; @@ -68,6 +69,9 @@ class MainFrame : public wxFrame bool can_delete() const; bool can_delete_all() const; +protected: + virtual void on_dpi_changed(const wxRect &suggested_rect); + public: MainFrame(); ~MainFrame() {} diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index cc2b9c8425..beb0c97cd6 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -24,7 +24,7 @@ namespace GUI { MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxWindowID button_id) : - MsgDialog(parent, title, headline, create_scaled_bitmap("Slic3r_192px.png"), button_id) + MsgDialog(parent, title, headline, create_scaled_bitmap("Slic3r_192px.png", 192), button_id) {} MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxBitmap bitmap, wxWindowID button_id) : diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 435d9548f8..8f75d0ec9b 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -38,6 +38,8 @@ #include "libslic3r/SLA/SLARotfinder.hpp" #include "libslic3r/Utils.hpp" +#include "libnest2d/optimizers/nlopt/genetic.hpp" + #include "GUI.hpp" #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" @@ -284,7 +286,7 @@ wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(15 * #ifdef __WINDOWS__ edit_btn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); #endif - edit_btn->SetBitmap(create_scaled_bitmap("cog.png")); + edit_btn->SetBitmap(create_scaled_bitmap("cog")); edit_btn->SetToolTip(_(L("Click to edit preset"))); edit_btn->Bind(wxEVT_BUTTON, ([preset_type, this](wxCommandEvent) @@ -1199,7 +1201,7 @@ struct Plater::priv BoundingBox scaled_bed_shape_bb() const; std::vector load_files(const std::vector& input_files, bool load_model, bool load_config); std::vector load_model_objects(const ModelObjectPtrs &model_objects); - std::unique_ptr get_export_file(GUI::FileType file_type); + wxString get_export_file(GUI::FileType file_type); const Selection& get_selection() const; Selection& get_selection(); @@ -1617,6 +1619,45 @@ std::vector Plater::priv::load_files(const std::vector& input_ } #if ENABLE_VOLUMES_CENTERING_FIXES } + else if ((wxGetApp().get_mode() == comSimple) && (type_3mf || type_any_amf)) + { + bool advanced = false; + for (const ModelObject* model_object : model.objects) + { + // is there more than one instance ? + if (model_object->instances.size() > 1) + { + advanced = true; + break; + } + + // is there any modifier ? + for (const ModelVolume* model_volume : model_object->volumes) + { + if (!model_volume->is_model_part()) + { + advanced = true; + break; + } + } + + if (advanced) + break; + } + + if (advanced) + { + wxMessageDialog dlg(q, _(L("This file cannot be loaded in simple mode. Do you want to switch to expert mode?\n")), + _(L("Detected advanced data")), wxICON_WARNING | wxYES | wxNO); + if (dlg.ShowModal() == wxID_YES) + { + Slic3r::GUI::wxGetApp().save_mode(comExpert); + view3D->set_as_dirty(); + } + else + return obj_idxs; + } + } #endif // ENABLE_VOLUMES_CENTERING_FIXES #if !ENABLE_VOLUMES_CENTERING_FIXES @@ -1642,7 +1683,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ Slic3r::GUI::show_error(nullptr, wxString::Format(_(L("You can't to add the object(s) from %s because of one or some of them is(are) multi-part")), from_path(filename))); - return std::vector(); + return obj_idxs; } } @@ -1784,7 +1825,7 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode return obj_idxs; } -std::unique_ptr Plater::priv::get_export_file(GUI::FileType file_type) +wxString Plater::priv::get_export_file(GUI::FileType file_type) { wxString wildcard; switch (file_type) { @@ -1801,34 +1842,56 @@ std::unique_ptr Plater::priv::get_export_file(GUI::FileType // Update printbility state of each of the ModelInstances. this->update_print_volume_state(); - // Find the file name of the first printable object. - fs::path output_file = this->model.propose_export_file_name_and_path(); + const Selection& selection = get_selection(); + int obj_idx = selection.get_object_idx(); + + fs::path output_file; + // first try to get the file name from the current selection + if ((0 <= obj_idx) && (obj_idx < (int)this->model.objects.size())) + output_file = this->model.objects[obj_idx]->get_export_filename(); + + if (output_file.empty()) + // Find the file name of the first printable object. + output_file = this->model.propose_export_file_name_and_path(); + + wxString dlg_title; switch (file_type) { - case FT_STL: output_file.replace_extension("stl"); break; - case FT_AMF: output_file.replace_extension("zip.amf"); break; // XXX: Problem on OS X with double extension? - case FT_3MF: output_file.replace_extension("3mf"); break; + case FT_STL: + { + output_file.replace_extension("stl"); + dlg_title = _(L("Export STL file:")); + break; + } + case FT_AMF: + { + // XXX: Problem on OS X with double extension? + output_file.replace_extension("zip.amf"); + dlg_title = _(L("Export AMF file:")); + break; + } + case FT_3MF: + { + output_file.replace_extension("3mf"); + dlg_title = _(L("Save file as:")); + break; + } default: break; } - auto dlg = Slic3r::make_unique(q, - ((file_type == FT_AMF) || (file_type == FT_3MF)) ? _(L("Export print config")) : "", - true, - _(L("Save file as:")), - from_path(output_file.parent_path()), - from_path(output_file.filename()), - wildcard, - wxFD_SAVE | wxFD_OVERWRITE_PROMPT - ); + wxFileDialog* dlg = new wxFileDialog(q, dlg_title, + from_path(output_file.parent_path()), from_path(output_file.filename()), + wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (dlg->ShowModal() != wxID_OK) { - return nullptr; + return wxEmptyString; } - fs::path path(into_path(dlg->GetPath())); + wxString out_path = dlg->GetPath(); + fs::path path(into_path(out_path)); wxGetApp().app_config->update_last_output_dir(path.parent_path().string()); - return dlg; + return out_path; } const Selection& Plater::priv::get_selection() const @@ -2039,7 +2102,9 @@ void Plater::priv::sla_optimize_rotation() { rotoptimizing.store(true); int obj_idx = get_selected_object_idx(); - ModelObject * o = model.objects[obj_idx]; + if(obj_idx < 0) { rotoptimizing.store(false); return; } + + ModelObject * o = model.objects[size_t(obj_idx)]; background_process.stop(); @@ -2047,7 +2112,7 @@ void Plater::priv::sla_optimize_rotation() { statusbar()->set_range(100); auto stfn = [this] (unsigned st, const std::string& msg) { - statusbar()->set_progress(st); + statusbar()->set_progress(int(st)); statusbar()->set_status_text(msg); // could be problematic, but we need the cancel button. @@ -2065,8 +2130,59 @@ void Plater::priv::sla_optimize_rotation() { [this](){ return !rotoptimizing.load(); } ); + const auto *bed_shape_opt = config->opt("bed_shape"); + assert(bed_shape_opt); + + auto& bedpoints = bed_shape_opt->values; + Polyline bed; bed.points.reserve(bedpoints.size()); + for(auto& v : bedpoints) bed.append(Point::new_scale(v(0), v(1))); + + double mindist = 6.0; // FIXME + double offs = mindist / 2.0 - EPSILON; + if(rotoptimizing.load()) // wasn't canceled - for(ModelInstance * oi : o->instances) oi->set_rotation({r[X], r[Y], r[Z]}); + for(ModelInstance * oi : o->instances) { + oi->set_rotation({r[X], r[Y], r[Z]}); + + auto trchull = o->convex_hull_2d(oi->get_transformation().get_matrix()); + + namespace opt = libnest2d::opt; + opt::StopCriteria stopcr; + stopcr.relative_score_difference = 0.01; + stopcr.max_iterations = 10000; + stopcr.stop_score = 0.0; + opt::GeneticOptimizer solver(stopcr); + Polygon pbed(bed); + + auto bin = pbed.bounding_box(); + double binw = bin.size()(X) * SCALING_FACTOR - offs; + double binh = bin.size()(Y) * SCALING_FACTOR - offs; + + auto result = solver.optimize_min([&trchull, binw, binh](double rot){ + auto chull = trchull; + chull.rotate(rot); + + auto bb = chull.bounding_box(); + double bbw = bb.size()(X) * SCALING_FACTOR; + double bbh = bb.size()(Y) * SCALING_FACTOR; + + auto wdiff = bbw - binw; + auto hdiff = bbh - binh; + double diff = 0; + if(wdiff < 0 && hdiff < 0) diff = wdiff + hdiff; + if(wdiff > 0) diff += wdiff; + if(hdiff > 0) diff += hdiff; + + return diff; + }, opt::initvals(0.0), opt::bound(-PI/2, PI/2)); + + double r = std::get<0>(result.optimum); + + Vec3d rt = oi->get_rotation(); rt(Z) += r; + oi->set_rotation(rt); + } + + arr::find_new_position(model, o->instances, coord_t(mindist/SCALING_FACTOR), bed); // Correct the z offset of the object which was corrupted be the rotation o->ensure_on_bed(); @@ -2713,12 +2829,15 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/ wxMenuItem* item_delete = nullptr; if (is_part) { item_delete = append_menu_item(menu, wxID_ANY, _(L("Delete")) + "\tDel", _(L("Remove the selected object")), - [this](wxCommandEvent&) { q->remove_selected(); }, "brick_delete.png"); - } else { + [this](wxCommandEvent&) { q->remove_selected(); }, "remove"); + + sidebar->obj_list()->append_menu_item_export_stl(menu); + } + else { wxMenuItem* item_increase = append_menu_item(menu, wxID_ANY, _(L("Increase copies")) + "\t+", _(L("Place one more copy of the selected object")), - [this](wxCommandEvent&) { q->increase_instances(); }, "add.png"); + [this](wxCommandEvent&) { q->increase_instances(); }, "instance_add"); wxMenuItem* item_decrease = append_menu_item(menu, wxID_ANY, _(L("Decrease copies")) + "\t-", _(L("Remove one copy of the selected object")), - [this](wxCommandEvent&) { q->decrease_instances(); }, "delete.png"); + [this](wxCommandEvent&) { q->decrease_instances(); }, "instance_remove"); wxMenuItem* item_set_number_of_copies = append_menu_item(menu, wxID_ANY, _(L("Set number of copies")) + dots, _(L("Change the number of copies of the selected object")), [this](wxCommandEvent&) { q->set_number_of_copies(); }, "textfield.png"); @@ -2728,7 +2847,7 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/ // Delete menu was moved to be after +/- instace to make it more difficult to be selected by mistake. item_delete = append_menu_item(menu, wxID_ANY, _(L("Delete")) + "\tDel", _(L("Remove the selected object")), - [this](wxCommandEvent&) { q->remove_selected(); }, "brick_delete.png"); + [this](wxCommandEvent&) { q->remove_selected(); }, "remove"); menu->AppendSeparator(); wxMenuItem* item_instance_to_object = sidebar->obj_list()->append_menu_item_instance_to_object(menu); @@ -2745,10 +2864,11 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/ append_menu_item(menu, wxID_ANY, _(L("Reload from Disk")), _(L("Reload the selected file from Disk")), [this](wxCommandEvent&) { reload_from_disk(); }); - append_menu_item(menu, wxID_ANY, _(L("Export object as STL")) + dots, _(L("Export this single object as STL file")), + append_menu_item(menu, wxID_ANY, _(L("Export as STL")) + dots, _(L("Export the selected object as STL file")), [this](wxCommandEvent&) { q->export_stl(true); }); + + menu->AppendSeparator(); } - menu->AppendSeparator(); sidebar->obj_list()->append_menu_item_fix_through_netfabb(menu); @@ -2782,11 +2902,11 @@ bool Plater::priv::complit_init_object_menu() return false; wxMenuItem* item_split_objects = append_menu_item(split_menu, wxID_ANY, _(L("To objects")), _(L("Split the selected object into individual objects")), - [this](wxCommandEvent&) { split_object(); }, "shape_ungroup_o.png", &object_menu); + [this](wxCommandEvent&) { split_object(); }, "split_objects.png", &object_menu); wxMenuItem* item_split_volumes = append_menu_item(split_menu, wxID_ANY, _(L("To parts")), _(L("Split the selected object into individual sub-parts")), - [this](wxCommandEvent&) { split_volume(); }, "shape_ungroup_p.png", &object_menu); + [this](wxCommandEvent&) { split_volume(); }, "split_parts.png", &object_menu); - wxMenuItem* item_split = append_submenu(&object_menu, split_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object")), "shape_ungroup.png"); + wxMenuItem* item_split = append_submenu(&object_menu, split_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object"))/*, "shape_ungroup.png"*/); object_menu.AppendSeparator(); // "Add (volumes)" popupmenu will be added later in append_menu_items_add_volume() @@ -2907,6 +3027,9 @@ bool Plater::priv::can_split() const bool Plater::priv::layers_height_allowed() const { + if (printer_technology != ptFFF) + return false; + int obj_idx = get_selected_object_idx(); return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && config->opt_bool("variable_layer_height") && view3D->is_layers_editing_allowed(); } @@ -3243,11 +3366,8 @@ void Plater::export_stl(bool selection_only) { if (p->model.objects.empty()) { return; } - auto dialog = p->get_export_file(FT_STL); - if (! dialog) { return; } - - // Store a binary STL - const wxString path = dialog->GetPath(); + wxString path = p->get_export_file(FT_STL); + if (path.empty()) { return; } const std::string path_u8 = into_u8(path); wxBusyCursor wait; @@ -3259,10 +3379,25 @@ void Plater::export_stl(bool selection_only) const auto obj_idx = selection.get_object_idx(); if (obj_idx == -1) { return; } - mesh = p->model.objects[obj_idx]->mesh(); - } else { - mesh = p->model.mesh(); + + const ModelObject* model_object = p->model.objects[obj_idx]; + if (selection.get_mode() == Selection::Instance) + { + if (selection.is_single_full_object()) + mesh = model_object->mesh(); + else + mesh = model_object->full_raw_mesh(); + } + else + { + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + mesh = model_object->volumes[volume->volume_idx()]->mesh; + mesh.transform(volume->get_volume_transformation().get_matrix()); + mesh.translate(-model_object->origin_translation.cast()); + } } + else + mesh = p->model.mesh(); Slic3r::store_stl(path_u8.c_str(), &mesh, true); p->statusbar()->set_status_text(wxString::Format(_(L("STL file exported to %s")), path)); @@ -3272,15 +3407,14 @@ void Plater::export_amf() { if (p->model.objects.empty()) { return; } - auto dialog = p->get_export_file(FT_AMF); - if (! dialog) { return; } - - const wxString path = dialog->GetPath(); + wxString path = p->get_export_file(FT_AMF); + if (path.empty()) { return; } const std::string path_u8 = into_u8(path); - DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure(); wxBusyCursor wait; - if (Slic3r::store_amf(path_u8.c_str(), &p->model, dialog->get_checkbox_value() ? &cfg : nullptr)) { + bool export_config = true; + DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure(); + if (Slic3r::store_amf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr)) { // Success p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path)); } else { @@ -3297,10 +3431,8 @@ void Plater::export_3mf(const boost::filesystem::path& output_path) bool export_config = true; if (output_path.empty()) { - auto dialog = p->get_export_file(FT_3MF); - if (!dialog) { return; } - path = dialog->GetPath(); - export_config = dialog->get_checkbox_value(); + path = p->get_export_file(FT_3MF); + if (path.empty()) { return; } } else path = from_path(output_path); diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index 74e40c15f1..9faa830da5 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -4,6 +4,7 @@ #include "AppConfig.hpp" #include "BitmapCache.hpp" #include "I18N.hpp" +#include "wxExtensions.hpp" #ifdef _MSC_VER #define WIN32_LEAN_AND_MEAN @@ -798,12 +799,14 @@ bool PresetCollection::delete_current_preset() bool PresetCollection::load_bitmap_default(const std::string &file_name) { - return m_bitmap_main_frame->LoadFile(wxString::FromUTF8(Slic3r::var(file_name).c_str()), wxBITMAP_TYPE_PNG); +// return m_bitmap_main_frame->LoadFile(wxString::FromUTF8(Slic3r::var(file_name).c_str()), wxBITMAP_TYPE_PNG); + return load_scaled_bitmap(&m_bitmap_main_frame, file_name); } bool PresetCollection::load_bitmap_add(const std::string &file_name) { - return m_bitmap_add->LoadFile(wxString::FromUTF8(Slic3r::var(file_name).c_str()), wxBITMAP_TYPE_PNG); +// return m_bitmap_add->LoadFile(wxString::FromUTF8(Slic3r::var(file_name).c_str()), wxBITMAP_TYPE_PNG); + return load_scaled_bitmap(&m_bitmap_add, file_name); } const Preset* PresetCollection::get_selected_preset_parent() const diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index f78a04126f..19f9040bf4 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -4,6 +4,7 @@ #include "BitmapCache.hpp" #include "Plater.hpp" #include "I18N.hpp" +#include "wxExtensions.hpp" #include #include @@ -102,13 +103,14 @@ PresetBundle::PresetBundle() : } // Load the default preset bitmaps. - this->prints .load_bitmap_default("cog.png"); - this->sla_prints .load_bitmap_default("package_green.png"); - this->filaments .load_bitmap_default("spool.png"); - this->sla_materials.load_bitmap_default("package_green.png"); - this->printers .load_bitmap_default("printer_empty.png"); - this->printers .load_bitmap_add("add.png"); - this->load_compatible_bitmaps(); + // #ys_FIXME_to_delete we'll load them later, using em_unit() +// this->prints .load_bitmap_default("cog"); +// this->sla_prints .load_bitmap_default("package_green.png"); +// this->filaments .load_bitmap_default("spool.png"); +// this->sla_materials.load_bitmap_default("package_green.png"); +// this->printers .load_bitmap_default("printer_empty.png"); +// this->printers .load_bitmap_add("add.png"); +// this->load_compatible_bitmaps(); // Re-activate the default presets, so their "edited" preset copies will be updated with the additional configuration values above. this->prints .select_preset(0); @@ -400,14 +402,20 @@ bool PresetBundle::load_compatible_bitmaps() const std::string path_bitmap_incompatible = "flag-red-icon.png"; const std::string path_bitmap_lock = "sys_lock.png";//"lock.png"; const std::string path_bitmap_lock_open = "sys_unlock.png";//"lock_open.png"; - bool loaded_compatible = m_bitmapCompatible ->LoadFile( - wxString::FromUTF8(Slic3r::var(path_bitmap_compatible).c_str()), wxBITMAP_TYPE_PNG); - bool loaded_incompatible = m_bitmapIncompatible->LoadFile( - wxString::FromUTF8(Slic3r::var(path_bitmap_incompatible).c_str()), wxBITMAP_TYPE_PNG); - bool loaded_lock = m_bitmapLock->LoadFile( - wxString::FromUTF8(Slic3r::var(path_bitmap_lock).c_str()), wxBITMAP_TYPE_PNG); - bool loaded_lock_open = m_bitmapLockOpen->LoadFile( - wxString::FromUTF8(Slic3r::var(path_bitmap_lock_open).c_str()), wxBITMAP_TYPE_PNG); +// bool loaded_compatible = m_bitmapCompatible ->LoadFile( +// wxString::FromUTF8(Slic3r::var(path_bitmap_compatible).c_str()), wxBITMAP_TYPE_PNG); +// bool loaded_incompatible = m_bitmapIncompatible->LoadFile( +// wxString::FromUTF8(Slic3r::var(path_bitmap_incompatible).c_str()), wxBITMAP_TYPE_PNG); +// bool loaded_lock = m_bitmapLock->LoadFile( +// wxString::FromUTF8(Slic3r::var(path_bitmap_lock).c_str()), wxBITMAP_TYPE_PNG); +// bool loaded_lock_open = m_bitmapLockOpen->LoadFile( +// wxString::FromUTF8(Slic3r::var(path_bitmap_lock_open).c_str()), wxBITMAP_TYPE_PNG); + + bool loaded_compatible = load_scaled_bitmap(&m_bitmapCompatible, path_bitmap_compatible); + bool loaded_incompatible = load_scaled_bitmap(&m_bitmapIncompatible,path_bitmap_incompatible); + bool loaded_lock = load_scaled_bitmap(&m_bitmapLock, path_bitmap_lock); + bool loaded_lock_open = load_scaled_bitmap(&m_bitmapLockOpen, path_bitmap_lock_open); + if (loaded_compatible) { prints .set_bitmap_compatible(m_bitmapCompatible); filaments .set_bitmap_compatible(m_bitmapCompatible); @@ -1438,6 +1446,17 @@ bool PresetBundle::parse_color(const std::string &scolor, unsigned char *rgb_out return true; } +void PresetBundle::load_default_preset_bitmaps() +{ + this->prints.load_bitmap_default("cog"); + this->sla_prints.load_bitmap_default("cog"); + this->filaments.load_bitmap_default("spool.png"); + this->sla_materials.load_bitmap_default("package_green.png"); + this->printers.load_bitmap_default("printer"); + this->printers.load_bitmap_add("add.png"); + this->load_compatible_bitmaps(); +} + void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui) { if (ui == nullptr || this->printers.get_edited_preset().printer_technology() == ptSLA || diff --git a/src/slic3r/GUI/PresetBundle.hpp b/src/slic3r/GUI/PresetBundle.hpp index 9f289f1e9b..abd936ee21 100644 --- a/src/slic3r/GUI/PresetBundle.hpp +++ b/src/slic3r/GUI/PresetBundle.hpp @@ -127,6 +127,8 @@ public: static bool parse_color(const std::string &scolor, unsigned char *rgb_out); + void load_default_preset_bitmaps(); + private: std::string load_system_presets(); // Merge one vendor's presets with the other vendor's presets, report duplicates. diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index ca245029c1..6f148ca24a 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -6,6 +6,7 @@ #include "GUI_ObjectManipulation.hpp" #include "GUI_ObjectList.hpp" #include "Gizmos/GLGizmoBase.hpp" +#include "slic3r/GUI/3DScene.hpp" #include @@ -75,7 +76,7 @@ Selection::~Selection() void Selection::set_volumes(GLVolumePtrs* volumes) { m_volumes = volumes; - _update_valid(); + update_valid(); } bool Selection::init(bool useVBOs) @@ -95,7 +96,7 @@ bool Selection::init(bool useVBOs) void Selection::set_model(Model* model) { m_model = model; - _update_valid(); + update_valid(); } void Selection::add(unsigned int volume_idx, bool as_single_selection) @@ -134,18 +135,18 @@ void Selection::add(unsigned int volume_idx, bool as_single_selection) case Volume: { if ((volume->volume_idx() >= 0) && (is_empty() || (volume->instance_idx() == get_instance_idx()))) - _add_volume(volume_idx); + do_add_volume(volume_idx); break; } case Instance: { - _add_instance(volume->object_idx(), volume->instance_idx()); + do_add_instance(volume->object_idx(), volume->instance_idx()); break; } } - _update_type(); + update_type(); m_bounding_box_dirty = true; } @@ -160,17 +161,17 @@ void Selection::remove(unsigned int volume_idx) { case Volume: { - _remove_volume(volume_idx); + do_remove_volume(volume_idx); break; } case Instance: { - _remove_instance(volume->object_idx(), volume->instance_idx()); + do_remove_instance(volume->object_idx(), volume->instance_idx()); break; } } - _update_type(); + update_type(); m_bounding_box_dirty = true; } @@ -185,9 +186,9 @@ void Selection::add_object(unsigned int object_idx, bool as_single_selection) m_mode = Instance; - _add_object(object_idx); + do_add_object(object_idx); - _update_type(); + update_type(); m_bounding_box_dirty = true; } @@ -196,9 +197,9 @@ void Selection::remove_object(unsigned int object_idx) if (!m_valid) return; - _remove_object(object_idx); + do_remove_object(object_idx); - _update_type(); + update_type(); m_bounding_box_dirty = true; } @@ -213,9 +214,9 @@ void Selection::add_instance(unsigned int object_idx, unsigned int instance_idx, m_mode = Instance; - _add_instance(object_idx, instance_idx); + do_add_instance(object_idx, instance_idx); - _update_type(); + update_type(); m_bounding_box_dirty = true; } @@ -224,9 +225,9 @@ void Selection::remove_instance(unsigned int object_idx, unsigned int instance_i if (!m_valid) return; - _remove_instance(object_idx, instance_idx); + do_remove_instance(object_idx, instance_idx); - _update_type(); + update_type(); m_bounding_box_dirty = true; } @@ -247,11 +248,11 @@ void Selection::add_volume(unsigned int object_idx, unsigned int volume_idx, int if ((v->object_idx() == object_idx) && (v->volume_idx() == volume_idx)) { if ((instance_idx != -1) && (v->instance_idx() == instance_idx)) - _add_volume(i); + do_add_volume(i); } } - _update_type(); + update_type(); m_bounding_box_dirty = true; } @@ -264,10 +265,10 @@ void Selection::remove_volume(unsigned int object_idx, unsigned int volume_idx) { GLVolume* v = (*m_volumes)[i]; if ((v->object_idx() == object_idx) && (v->volume_idx() == volume_idx)) - _remove_volume(i); + do_remove_volume(i); } - _update_type(); + update_type(); m_bounding_box_dirty = true; } @@ -282,10 +283,10 @@ void Selection::add_all() for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { if (!(*m_volumes)[i]->is_wipe_tower) - _add_volume(i); + do_add_volume(i); } - _update_type(); + update_type(); m_bounding_box_dirty = true; } @@ -301,7 +302,7 @@ void Selection::clear() m_list.clear(); - _update_type(); + update_type(); m_bounding_box_dirty = true; // resets the cache in the sidebar @@ -341,11 +342,11 @@ void Selection::volumes_changed(const std::vector &map_volume_old_to_new const GLVolume* volume = (*m_volumes)[i]; for (const std::pair &model_instance : model_instances) if (volume->object_idx() == model_instance.first && volume->instance_idx() == model_instance.second) - this->_add_volume(i); + do_add_volume(i); } } - _update_type(); + update_type(); m_bounding_box_dirty = true; } @@ -426,7 +427,7 @@ const GLVolume* Selection::get_volume(unsigned int volume_idx) const const BoundingBoxf3& Selection::get_bounding_box() const { if (m_bounding_box_dirty) - _calc_bounding_box(); + calc_bounding_box(); return m_bounding_box; } @@ -436,7 +437,7 @@ void Selection::start_dragging() if (!m_valid) return; - _set_caches(); + set_caches(); } void Selection::translate(const Vec3d& displacement, bool local) @@ -460,7 +461,7 @@ void Selection::translate(const Vec3d& displacement, bool local) } else if (m_mode == Instance) { - if (_is_from_fully_selected_instance(i)) + if (is_from_fully_selected_instance(i)) (*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement); else { @@ -473,38 +474,14 @@ void Selection::translate(const Vec3d& displacement, bool local) #if !DISABLE_INSTANCES_SYNCH if (translation_type == Instance) - _synchronize_unselected_instances(SYNC_ROTATION_NONE); + synchronize_unselected_instances(SYNC_ROTATION_NONE); else if (translation_type == Volume) - _synchronize_unselected_volumes(); + synchronize_unselected_volumes(); #endif // !DISABLE_INSTANCES_SYNCH m_bounding_box_dirty = true; } -static 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. -static 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; -} - // Rotate an object around one of the axes. Only one rotation component is expected to be changing. void Selection::rotate(const Vec3d& rotation, TransformationType transformation_type) { @@ -547,7 +524,7 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ assert(is_approx(rotation.z(), 0.0)); const GLVolume &first_volume = *(*m_volumes)[first_volume_idx]; const Vec3d &rotation = first_volume.get_instance_rotation(); - double z_diff = rotation_diff_z(m_cache.volumes_data[first_volume_idx].get_instance_rotation(), m_cache.volumes_data[i].get_instance_rotation()); + double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[first_volume_idx].get_instance_rotation(), m_cache.volumes_data[i].get_instance_rotation()); volume.set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2) + z_diff)); } else { @@ -604,9 +581,9 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ #if !DISABLE_INSTANCES_SYNCH if (m_mode == Instance) - _synchronize_unselected_instances((rot_axis_max == 2) ? SYNC_ROTATION_NONE : SYNC_ROTATION_GENERAL); + synchronize_unselected_instances((rot_axis_max == 2) ? SYNC_ROTATION_NONE : SYNC_ROTATION_GENERAL); else if (m_mode == Volume) - _synchronize_unselected_volumes(); + synchronize_unselected_volumes(); #endif // !DISABLE_INSTANCES_SYNCH m_bounding_box_dirty = true; @@ -647,7 +624,7 @@ void Selection::flattening_rotate(const Vec3d& normal) // we want to synchronize z-rotation as well, otherwise the flattening behaves funny // when applied on one of several identical instances if (m_mode == Instance) - _synchronize_unselected_instances(SYNC_ROTATION_FULL); + synchronize_unselected_instances(SYNC_ROTATION_FULL); #endif // !DISABLE_INSTANCES_SYNCH m_bounding_box_dirty = true; @@ -694,12 +671,12 @@ void Selection::scale(const Vec3d& scale, bool local) #if !DISABLE_INSTANCES_SYNCH if (m_mode == Instance) - _synchronize_unselected_instances(SYNC_ROTATION_NONE); + synchronize_unselected_instances(SYNC_ROTATION_NONE); else if (m_mode == Volume) - _synchronize_unselected_volumes(); + synchronize_unselected_volumes(); #endif // !DISABLE_INSTANCES_SYNCH - _ensure_on_bed(); + ensure_on_bed(); m_bounding_box_dirty = true; } @@ -721,9 +698,9 @@ void Selection::mirror(Axis axis) #if !DISABLE_INSTANCES_SYNCH if (m_mode == Instance) - _synchronize_unselected_instances(SYNC_ROTATION_NONE); + synchronize_unselected_instances(SYNC_ROTATION_NONE); else if (m_mode == Volume) - _synchronize_unselected_volumes(); + synchronize_unselected_volumes(); #endif // !DISABLE_INSTANCES_SYNCH m_bounding_box_dirty = true; @@ -941,8 +918,8 @@ void Selection::render(float scale_factor) const m_scale_factor = scale_factor; // render cumulative bounding box of selected volumes - _render_selected_volumes(); - _render_synchronized_volumes(); + render_selected_volumes(); + render_synchronized_volumes(); } #if ENABLE_RENDER_SELECTION_CENTER @@ -953,17 +930,17 @@ void Selection::render_center() const const Vec3d& center = get_bounding_box().center(); - ::glDisable(GL_DEPTH_TEST); + glsafe(::glDisable(GL_DEPTH_TEST))); - ::glEnable(GL_LIGHTING); + glsafe(::glEnable(GL_LIGHTING)); - ::glColor3f(1.0f, 1.0f, 1.0f); - ::glPushMatrix(); - ::glTranslated(center(0), center(1), center(2)); - ::gluSphere(m_quadric, 0.75, 32, 32); - ::glPopMatrix(); + glsafe(::glColor3f(1.0f, 1.0f, 1.0f)); + glsafe(::glPushMatrix()); + glsafe(::glTranslated(center(0), center(1), center(2))); + glsafe(::gluSphere(m_quadric, 0.75, 32, 32)); + glsafe(::glPopMatrix()); - ::glDisable(GL_LIGHTING); + glsafe(::glDisable(GL_LIGHTING)); } #endif // ENABLE_RENDER_SELECTION_CENTER @@ -972,18 +949,18 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field) const if (sidebar_field.empty()) return; - ::glClear(GL_DEPTH_BUFFER_BIT); - ::glEnable(GL_DEPTH_TEST); + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); + glsafe(::glEnable(GL_DEPTH_TEST)); - ::glEnable(GL_LIGHTING); + glsafe(::glEnable(GL_LIGHTING)); - ::glPushMatrix(); + glsafe(::glPushMatrix()); const Vec3d& center = get_bounding_box().center(); if (is_single_full_instance()) { - ::glTranslated(center(0), center(1), center(2)); + glsafe(::glTranslated(center(0), center(1), center(2))); if (!boost::starts_with(sidebar_field, "position")) { Transform3d orient_matrix = Transform3d::Identity(); @@ -1003,40 +980,40 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field) const } } - ::glMultMatrixd(orient_matrix.data()); + glsafe(::glMultMatrixd(orient_matrix.data())); } } else if (is_single_volume() || is_single_modifier()) { - ::glTranslated(center(0), center(1), center(2)); + glsafe(::glTranslated(center(0), center(1), center(2))); Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); if (!boost::starts_with(sidebar_field, "position")) orient_matrix = orient_matrix * (*m_volumes)[*m_list.begin()]->get_volume_transformation().get_matrix(true, false, true, true); - ::glMultMatrixd(orient_matrix.data()); + glsafe(::glMultMatrixd(orient_matrix.data())); } else { - ::glTranslated(center(0), center(1), center(2)); + glsafe(::glTranslated(center(0), center(1), center(2))); if (requires_local_axes()) { Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); - ::glMultMatrixd(orient_matrix.data()); + glsafe(::glMultMatrixd(orient_matrix.data())); } } if (boost::starts_with(sidebar_field, "position")) - _render_sidebar_position_hints(sidebar_field); + render_sidebar_position_hints(sidebar_field); else if (boost::starts_with(sidebar_field, "rotation")) - _render_sidebar_rotation_hints(sidebar_field); + render_sidebar_rotation_hints(sidebar_field); else if (boost::starts_with(sidebar_field, "scale")) - _render_sidebar_scale_hints(sidebar_field); + render_sidebar_scale_hints(sidebar_field); else if (boost::starts_with(sidebar_field, "size")) - _render_sidebar_size_hints(sidebar_field); + render_sidebar_size_hints(sidebar_field); - ::glPopMatrix(); + glsafe(::glPopMatrix()); - ::glDisable(GL_LIGHTING); + glsafe(::glDisable(GL_LIGHTING)); } bool Selection::requires_local_axes() const @@ -1044,12 +1021,12 @@ bool Selection::requires_local_axes() const return (m_mode == Volume) && is_from_single_instance(); } -void Selection::_update_valid() +void Selection::update_valid() { m_valid = (m_volumes != nullptr) && (m_model != nullptr); } -void Selection::_update_type() +void Selection::update_type() { m_cache.content.clear(); m_type = Mixed; @@ -1147,15 +1124,11 @@ void Selection::_update_type() } if (modifiers_count == 0) - { m_type = MultipleVolume; - requires_disable = true; - } else if (modifiers_count == (unsigned int)m_list.size()) - { m_type = MultipleModifier; - requires_disable = true; - } + + requires_disable = true; } } else if ((selected_instances_count > 1) && (selected_instances_count * volumes_count == (unsigned int)m_list.size())) @@ -1277,7 +1250,7 @@ void Selection::_update_type() #endif // ENABLE_SELECTION_DEBUG_OUTPUT } -void Selection::_set_caches() +void Selection::set_caches() { m_cache.volumes_data.clear(); for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) @@ -1288,33 +1261,33 @@ void Selection::_set_caches() m_cache.dragging_center = get_bounding_box().center(); } -void Selection::_add_volume(unsigned int volume_idx) +void Selection::do_add_volume(unsigned int volume_idx) { m_list.insert(volume_idx); (*m_volumes)[volume_idx]->selected = true; } -void Selection::_add_instance(unsigned int object_idx, unsigned int instance_idx) +void Selection::do_add_instance(unsigned int object_idx, unsigned int instance_idx) { for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { GLVolume* v = (*m_volumes)[i]; if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) - _add_volume(i); + do_add_volume(i); } } -void Selection::_add_object(unsigned int object_idx) +void Selection::do_add_object(unsigned int object_idx) { for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { GLVolume* v = (*m_volumes)[i]; if (v->object_idx() == object_idx) - _add_volume(i); + do_add_volume(i); } } -void Selection::_remove_volume(unsigned int volume_idx) +void Selection::do_remove_volume(unsigned int volume_idx) { IndicesList::iterator v_it = m_list.find(volume_idx); if (v_it == m_list.end()) @@ -1325,27 +1298,27 @@ void Selection::_remove_volume(unsigned int volume_idx) (*m_volumes)[volume_idx]->selected = false; } -void Selection::_remove_instance(unsigned int object_idx, unsigned int instance_idx) +void Selection::do_remove_instance(unsigned int object_idx, unsigned int instance_idx) { for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { GLVolume* v = (*m_volumes)[i]; if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) - _remove_volume(i); + do_remove_volume(i); } } -void Selection::_remove_object(unsigned int object_idx) +void Selection::do_remove_object(unsigned int object_idx) { for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { GLVolume* v = (*m_volumes)[i]; if (v->object_idx() == object_idx) - _remove_volume(i); + do_remove_volume(i); } } -void Selection::_calc_bounding_box() const +void Selection::calc_bounding_box() const { m_bounding_box = BoundingBoxf3(); if (m_valid) @@ -1358,13 +1331,13 @@ void Selection::_calc_bounding_box() const m_bounding_box_dirty = false; } -void Selection::_render_selected_volumes() const +void Selection::render_selected_volumes() const { float color[3] = { 1.0f, 1.0f, 1.0f }; - _render_bounding_box(get_bounding_box(), color); + render_bounding_box(get_bounding_box(), color); } -void Selection::_render_synchronized_volumes() const +void Selection::render_synchronized_volumes() const { if (m_mode == Instance) return; @@ -1386,12 +1359,12 @@ void Selection::_render_synchronized_volumes() const if ((v->object_idx() != object_idx) || (v->volume_idx() != volume_idx)) continue; - _render_bounding_box(v->transformed_convex_hull_bounding_box(), color); + render_bounding_box(v->transformed_convex_hull_bounding_box(), color); } } } -void Selection::_render_bounding_box(const BoundingBoxf3& box, float* color) const +void Selection::render_bounding_box(const BoundingBoxf3& box, float* color) const { if (color == nullptr) return; @@ -1400,10 +1373,10 @@ void Selection::_render_bounding_box(const BoundingBoxf3& box, float* color) con Vec3f b_max = box.max.cast(); Vec3f size = 0.2f * box.size().cast(); - ::glEnable(GL_DEPTH_TEST); + glsafe(::glEnable(GL_DEPTH_TEST)); - ::glColor3fv(color); - ::glLineWidth(2.0f * m_scale_factor); + glsafe(::glColor3fv(color)); + glsafe(::glLineWidth(2.0f * m_scale_factor)); ::glBegin(GL_LINES); @@ -1439,109 +1412,109 @@ void Selection::_render_bounding_box(const BoundingBoxf3& box, float* color) con ::glVertex3f(b_min(0), b_max(1), b_max(2)); ::glVertex3f(b_min(0), b_max(1) - size(1), b_max(2)); ::glVertex3f(b_min(0), b_max(1), b_max(2)); ::glVertex3f(b_min(0), b_max(1), b_max(2) - size(2)); - ::glEnd(); + glsafe(::glEnd()); } -void Selection::_render_sidebar_position_hints(const std::string& sidebar_field) const +void Selection::render_sidebar_position_hints(const std::string& sidebar_field) const { if (boost::ends_with(sidebar_field, "x")) { - ::glRotated(-90.0, 0.0, 0.0, 1.0); - _render_sidebar_position_hint(X); + glsafe(::glRotated(-90.0, 0.0, 0.0, 1.0)); + render_sidebar_position_hint(X); } else if (boost::ends_with(sidebar_field, "y")) - _render_sidebar_position_hint(Y); + render_sidebar_position_hint(Y); else if (boost::ends_with(sidebar_field, "z")) { - ::glRotated(90.0, 1.0, 0.0, 0.0); - _render_sidebar_position_hint(Z); + glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); + render_sidebar_position_hint(Z); } } -void Selection::_render_sidebar_rotation_hints(const std::string& sidebar_field) const +void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) const { if (boost::ends_with(sidebar_field, "x")) { - ::glRotated(90.0, 0.0, 1.0, 0.0); - _render_sidebar_rotation_hint(X); + glsafe(::glRotated(90.0, 0.0, 1.0, 0.0)); + render_sidebar_rotation_hint(X); } else if (boost::ends_with(sidebar_field, "y")) { - ::glRotated(-90.0, 1.0, 0.0, 0.0); - _render_sidebar_rotation_hint(Y); + glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0)); + render_sidebar_rotation_hint(Y); } else if (boost::ends_with(sidebar_field, "z")) - _render_sidebar_rotation_hint(Z); + render_sidebar_rotation_hint(Z); } -void Selection::_render_sidebar_scale_hints(const std::string& sidebar_field) const +void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) const { bool uniform_scale = requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling(); if (boost::ends_with(sidebar_field, "x") || uniform_scale) { - ::glPushMatrix(); - ::glRotated(-90.0, 0.0, 0.0, 1.0); - _render_sidebar_scale_hint(X); - ::glPopMatrix(); + glsafe(::glPushMatrix()); + glsafe(::glRotated(-90.0, 0.0, 0.0, 1.0)); + render_sidebar_scale_hint(X); + glsafe(::glPopMatrix()); } if (boost::ends_with(sidebar_field, "y") || uniform_scale) { - ::glPushMatrix(); - _render_sidebar_scale_hint(Y); - ::glPopMatrix(); + glsafe(::glPushMatrix()); + render_sidebar_scale_hint(Y); + glsafe(::glPopMatrix()); } if (boost::ends_with(sidebar_field, "z") || uniform_scale) { - ::glPushMatrix(); - ::glRotated(90.0, 1.0, 0.0, 0.0); - _render_sidebar_scale_hint(Z); - ::glPopMatrix(); + glsafe(::glPushMatrix()); + glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); + render_sidebar_scale_hint(Z); + glsafe(::glPopMatrix()); } } -void Selection::_render_sidebar_size_hints(const std::string& sidebar_field) const +void Selection::render_sidebar_size_hints(const std::string& sidebar_field) const { - _render_sidebar_scale_hints(sidebar_field); + render_sidebar_scale_hints(sidebar_field); } -void Selection::_render_sidebar_position_hint(Axis axis) const +void Selection::render_sidebar_position_hint(Axis axis) const { m_arrow.set_color(AXES_COLOR[axis], 3); m_arrow.render(); } -void Selection::_render_sidebar_rotation_hint(Axis axis) const +void Selection::render_sidebar_rotation_hint(Axis axis) const { m_curved_arrow.set_color(AXES_COLOR[axis], 3); m_curved_arrow.render(); - ::glRotated(180.0, 0.0, 0.0, 1.0); + glsafe(::glRotated(180.0, 0.0, 0.0, 1.0)); m_curved_arrow.render(); } -void Selection::_render_sidebar_scale_hint(Axis axis) const +void Selection::render_sidebar_scale_hint(Axis axis) const { m_arrow.set_color(((requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling()) ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis]), 3); - ::glTranslated(0.0, 5.0, 0.0); + glsafe(::glTranslated(0.0, 5.0, 0.0)); m_arrow.render(); - ::glTranslated(0.0, -10.0, 0.0); - ::glRotated(180.0, 0.0, 0.0, 1.0); + glsafe(::glTranslated(0.0, -10.0, 0.0)); + glsafe(::glRotated(180.0, 0.0, 0.0, 1.0)); m_arrow.render(); } -void Selection::_render_sidebar_size_hint(Axis axis, double length) const +void Selection::render_sidebar_size_hint(Axis axis, double length) const { } #ifndef NDEBUG static bool is_rotation_xy_synchronized(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to) { - Eigen::AngleAxisd angle_axis(rotation_xyz_diff(rot_xyz_from, rot_xyz_to)); + Eigen::AngleAxisd angle_axis(Geometry::rotation_xyz_diff(rot_xyz_from, rot_xyz_to)); Vec3d axis = angle_axis.axis(); double angle = angle_axis.angle(); if (std::abs(angle) < 1e-8) @@ -1575,7 +1548,7 @@ static void verify_instances_rotation_synchronized(const Model &model, const GLV } #endif /* NDEBUG */ -void Selection::_synchronize_unselected_instances(SyncRotationType sync_rotation_type) +void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_type) { std::set done; // prevent processing volumes twice done.insert(m_list.begin(), m_list.end()); @@ -1621,7 +1594,7 @@ void Selection::_synchronize_unselected_instances(SyncRotationType sync_rotation break; case SYNC_ROTATION_GENERAL: // generic rotation -> update instance z with the delta of the rotation. - double z_diff = rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation()); + double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation()); v->set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2) + z_diff)); break; } @@ -1638,7 +1611,7 @@ void Selection::_synchronize_unselected_instances(SyncRotationType sync_rotation #endif /* NDEBUG */ } -void Selection::_synchronize_unselected_volumes() +void Selection::synchronize_unselected_volumes() { for (unsigned int i : m_list) { @@ -1671,7 +1644,7 @@ void Selection::_synchronize_unselected_volumes() } } -void Selection::_ensure_on_bed() +void Selection::ensure_on_bed() { typedef std::map, double> InstancesToZMap; InstancesToZMap instances_min_z; @@ -1699,7 +1672,7 @@ void Selection::_ensure_on_bed() } } -bool Selection::_is_from_fully_selected_instance(unsigned int volume_idx) const +bool Selection::is_from_fully_selected_instance(unsigned int volume_idx) const { struct SameInstance { diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 4b2a72d7c0..ae121f942d 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -133,10 +133,12 @@ private: const Transform3d& get_instance_full_matrix() const { return m_instance.full_matrix; } }; +public: typedef std::map VolumesCache; typedef std::set InstanceIdxsList; typedef std::map ObjectIdxsToInstanceIdxsMap; +private: struct Cache { // Cache of GLVolume derived transformation matrices, valid during mouse dragging. @@ -265,27 +267,27 @@ public: bool requires_local_axes() const; private: - void _update_valid(); - void _update_type(); - void _set_caches(); - void _add_volume(unsigned int volume_idx); - void _add_instance(unsigned int object_idx, unsigned int instance_idx); - void _add_object(unsigned int object_idx); - void _remove_volume(unsigned int volume_idx); - void _remove_instance(unsigned int object_idx, unsigned int instance_idx); - void _remove_object(unsigned int object_idx); - void _calc_bounding_box() const; - void _render_selected_volumes() const; - void _render_synchronized_volumes() const; - void _render_bounding_box(const BoundingBoxf3& box, float* color) const; - void _render_sidebar_position_hints(const std::string& sidebar_field) const; - void _render_sidebar_rotation_hints(const std::string& sidebar_field) const; - void _render_sidebar_scale_hints(const std::string& sidebar_field) const; - void _render_sidebar_size_hints(const std::string& sidebar_field) const; - void _render_sidebar_position_hint(Axis axis) const; - void _render_sidebar_rotation_hint(Axis axis) const; - void _render_sidebar_scale_hint(Axis axis) const; - void _render_sidebar_size_hint(Axis axis, double length) const; + void update_valid(); + void update_type(); + void set_caches(); + void do_add_volume(unsigned int volume_idx); + void do_add_instance(unsigned int object_idx, unsigned int instance_idx); + void do_add_object(unsigned int object_idx); + void do_remove_volume(unsigned int volume_idx); + void do_remove_instance(unsigned int object_idx, unsigned int instance_idx); + void do_remove_object(unsigned int object_idx); + void calc_bounding_box() const; + void render_selected_volumes() const; + void render_synchronized_volumes() const; + void render_bounding_box(const BoundingBoxf3& box, float* color) const; + void render_sidebar_position_hints(const std::string& sidebar_field) const; + void render_sidebar_rotation_hints(const std::string& sidebar_field) const; + void render_sidebar_scale_hints(const std::string& sidebar_field) const; + void render_sidebar_size_hints(const std::string& sidebar_field) const; + void render_sidebar_position_hint(Axis axis) const; + void render_sidebar_rotation_hint(Axis axis) const; + void render_sidebar_scale_hint(Axis axis) const; + void render_sidebar_size_hint(Axis axis, double length) const; enum SyncRotationType { // Do not synchronize rotation. Either not rotating at all, or rotating by world Z axis. SYNC_ROTATION_NONE = 0, @@ -294,10 +296,10 @@ private: // Synchronize after rotation by an axis not parallel with Z. SYNC_ROTATION_GENERAL = 2, }; - void _synchronize_unselected_instances(SyncRotationType sync_rotation_type); - void _synchronize_unselected_volumes(); - void _ensure_on_bed(); - bool _is_from_fully_selected_instance(unsigned int volume_idx) const; + void synchronize_unselected_instances(SyncRotationType sync_rotation_type); + void synchronize_unselected_volumes(); + void ensure_on_bed(); + bool is_from_fully_selected_instance(unsigned int volume_idx) const; }; } // namespace GUI diff --git a/src/slic3r/GUI/SysInfoDialog.cpp b/src/slic3r/GUI/SysInfoDialog.cpp index dfaba71ae6..95bd7e61ad 100644 --- a/src/slic3r/GUI/SysInfoDialog.cpp +++ b/src/slic3r/GUI/SysInfoDialog.cpp @@ -52,9 +52,7 @@ SysInfoDialog::SysInfoDialog() main_sizer->Add(hsizer, 1, wxEXPAND | wxALL, 10); // 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, 0, wxALIGN_CENTER_VERTICAL); wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 5fb0a9558e..1c74d4b441 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -116,18 +116,14 @@ void Tab::create_preset_tab() //buttons wxBitmap bmpMenu; -// bmpMenu = wxBitmap(from_u8(Slic3r::var("disk.png")), wxBITMAP_TYPE_PNG); bmpMenu = create_scaled_bitmap("disk.png"); m_btn_save_preset = new wxBitmapButton(panel, wxID_ANY, bmpMenu, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); if (wxMSW) m_btn_save_preset->SetBackgroundColour(color); -// bmpMenu = wxBitmap(from_u8(Slic3r::var("delete.png")), wxBITMAP_TYPE_PNG); bmpMenu = create_scaled_bitmap("delete.png"); m_btn_delete_preset = new wxBitmapButton(panel, wxID_ANY, bmpMenu, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); if (wxMSW) m_btn_delete_preset->SetBackgroundColour(color); m_show_incompatible_presets = false; -// m_bmp_show_incompatible_presets.LoadFile(from_u8(Slic3r::var("flag-red-icon.png")), wxBITMAP_TYPE_PNG); -// m_bmp_hide_incompatible_presets.LoadFile(from_u8(Slic3r::var("flag-green-icon.png")), wxBITMAP_TYPE_PNG); m_bmp_show_incompatible_presets = create_scaled_bitmap("flag-red-icon.png"); m_bmp_hide_incompatible_presets = create_scaled_bitmap("flag-green-icon.png"); m_btn_hide_incompatible_presets = new wxBitmapButton(panel, wxID_ANY, m_bmp_hide_incompatible_presets, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); @@ -152,15 +148,10 @@ void Tab::create_preset_tab() // Determine the theme color of OS (dark or light) auto luma = wxGetApp().get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); // Bitmaps to be shown on the "Revert to system" aka "Lock to system" button next to each input field. -// m_bmp_value_lock .LoadFile(from_u8(var("sys_lock.png")), wxBITMAP_TYPE_PNG); -// m_bmp_value_unlock .LoadFile(from_u8(var(luma >= 128 ? "sys_unlock.png" : "sys_unlock_grey.png")), wxBITMAP_TYPE_PNG); m_bmp_value_lock = create_scaled_bitmap("sys_lock.png"); m_bmp_value_unlock = create_scaled_bitmap(luma >= 128 ? "sys_unlock.png" : "sys_unlock_grey.png"); m_bmp_non_system = &m_bmp_white_bullet; // Bitmaps to be shown on the "Undo user changes" button next to each input field. -// m_bmp_value_revert .LoadFile(from_u8(var(luma >= 128 ? "action_undo.png" : "action_undo_grey.png")), wxBITMAP_TYPE_PNG); -// m_bmp_white_bullet .LoadFile(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG); -// m_bmp_question .LoadFile(from_u8(var("question_mark_01.png")), wxBITMAP_TYPE_PNG); m_bmp_value_revert = create_scaled_bitmap(luma >= 128 ? "action_undo.png" : "action_undo_grey.png"); m_bmp_white_bullet = create_scaled_bitmap("bullet_white.png"); m_bmp_question = create_scaled_bitmap("question_mark_01.png"); @@ -954,7 +945,7 @@ void TabPrint::build() m_presets = &m_preset_bundle->prints; load_initial_data(); - auto page = add_options_page(_(L("Layers and perimeters")), "layers.png"); + auto page = add_options_page(_(L("Layers and perimeters")), "layers"); auto optgroup = page->new_optgroup(_(L("Layer height"))); optgroup->append_single_option_line("layer_height"); optgroup->append_single_option_line("first_layer_height"); @@ -987,7 +978,7 @@ void TabPrint::build() optgroup->append_single_option_line("seam_position"); optgroup->append_single_option_line("external_perimeters_first"); - page = add_options_page(_(L("Infill")), "infill.png"); + page = add_options_page(_(L("Infill")), "infill"); optgroup = page->new_optgroup(_(L("Infill"))); optgroup->append_single_option_line("fill_density"); optgroup->append_single_option_line("fill_pattern"); @@ -1006,7 +997,7 @@ void TabPrint::build() optgroup->append_single_option_line("only_retract_when_crossing_perimeters"); optgroup->append_single_option_line("infill_first"); - page = add_options_page(_(L("Skirt and brim")), "box.png"); + page = add_options_page(_(L("Skirt and brim")), "skirt+brim"); optgroup = page->new_optgroup(_(L("Skirt"))); optgroup->append_single_option_line("skirts"); optgroup->append_single_option_line("skirt_distance"); @@ -1016,7 +1007,7 @@ void TabPrint::build() optgroup = page->new_optgroup(_(L("Brim"))); optgroup->append_single_option_line("brim_width"); - page = add_options_page(_(L("Support material")), "building.png"); + page = add_options_page(_(L("Support material")), "support"); optgroup = page->new_optgroup(_(L("Support material"))); optgroup->append_single_option_line("support_material"); optgroup->append_single_option_line("support_material_auto"); @@ -1041,7 +1032,7 @@ void TabPrint::build() optgroup->append_single_option_line("dont_support_bridges"); optgroup->append_single_option_line("support_material_synchronize_layers"); - page = add_options_page(_(L("Speed")), "time.png"); + page = add_options_page(_(L("Speed")), "time"); optgroup = page->new_optgroup(_(L("Speed for print moves"))); optgroup->append_single_option_line("perimeter_speed"); optgroup->append_single_option_line("small_perimeter_speed"); @@ -1075,7 +1066,7 @@ void TabPrint::build() optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_negative"); #endif /* HAS_PRESSURE_EQUALIZER */ - page = add_options_page(_(L("Multiple Extruders")), "funnel.png"); + page = add_options_page(_(L("Multiple Extruders")), "funnel"); optgroup = page->new_optgroup(_(L("Extruders"))); optgroup->append_single_option_line("perimeter_extruder"); optgroup->append_single_option_line("infill_extruder"); @@ -1125,7 +1116,7 @@ void TabPrint::build() optgroup = page->new_optgroup(_(L("Other"))); optgroup->append_single_option_line("clip_multipart_objects"); - page = add_options_page(_(L("Output options")), "page_white_go.png"); + page = add_options_page(_(L("Output options")), "output+page_white"); optgroup = page->new_optgroup(_(L("Sequential printing"))); optgroup->append_single_option_line("complete_objects"); line = { _(L("Extruder clearance (mm)")), "" }; @@ -1446,7 +1437,7 @@ void TabFilament::build() line.append_option(optgroup->get_option("bed_temperature")); optgroup->append_line(line); - page = add_options_page(_(L("Cooling")), "hourglass.png"); + page = add_options_page(_(L("Cooling")), "cooling"); optgroup = page->new_optgroup(_(L("Enable"))); optgroup->append_single_option_line("fan_always_on"); optgroup->append_single_option_line("cooling"); @@ -1520,7 +1511,7 @@ void TabFilament::build() const int gcode_field_height = 15 * m_em_unit; // 150 const int notes_field_height = 25 * m_em_unit; // 250 - page = add_options_page(_(L("Custom G-code")), "cog.png"); + page = add_options_page(_(L("Custom G-code")), "cog"); optgroup = page->new_optgroup(_(L("Start G-code")), 0); Option option = optgroup->get_option("start_filament_gcode"); option.opt.full_width = true; @@ -1638,7 +1629,8 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup) } auto printhost_browse = [=](wxWindow* parent) { - auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); + auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse ")) + dots, + wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); btn->SetBitmap(create_scaled_bitmap("zoom.png")); auto sizer = new wxBoxSizer(wxHORIZONTAL); @@ -1695,7 +1687,6 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup) auto printhost_cafile_browse = [this, optgroup] (wxWindow* parent) { auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); -// btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); btn->SetBitmap(create_scaled_bitmap("zoom.png")); auto sizer = new wxBoxSizer(wxHORIZONTAL); @@ -1768,15 +1759,14 @@ void TabPrinter::build_fff() m_sys_extruders_count = parent_preset == nullptr ? 0 : static_cast(parent_preset->config.option("nozzle_diameter"))->values.size(); - auto page = add_options_page(_(L("General")), "printer_empty.png"); + auto page = add_options_page(_(L("General")), "printer"); auto optgroup = page->new_optgroup(_(L("Size and coordinates"))); Line line = optgroup->create_single_option_line("bed_shape");//{ _(L("Bed shape")), "" }; line.widget = [this](wxWindow* parent) { auto btn = new wxButton(parent, wxID_ANY, _(L(" Set "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); btn->SetFont(wxGetApp().small_font()); -// btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("printer_empty.png")), wxBITMAP_TYPE_PNG)); - btn->SetBitmap(create_scaled_bitmap("printer_empty.png")); + btn->SetBitmap(create_scaled_bitmap("printer")); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(btn); @@ -1905,7 +1895,7 @@ void TabPrinter::build_fff() const int gcode_field_height = 15 * m_em_unit; // 150 const int notes_field_height = 25 * m_em_unit; // 250 - page = add_options_page(_(L("Custom G-code")), "cog.png"); + page = add_options_page(_(L("Custom G-code")), "cog"); optgroup = page->new_optgroup(_(L("Start G-code")), 0); option = optgroup->get_option("start_gcode"); option.opt.full_width = true; @@ -1970,15 +1960,14 @@ void TabPrinter::build_sla() { if (!m_pages.empty()) m_pages.resize(0); - auto page = add_options_page(_(L("General")), "printer_empty.png"); + auto page = add_options_page(_(L("General")), "printer"); auto optgroup = page->new_optgroup(_(L("Size and coordinates"))); Line line = optgroup->create_single_option_line("bed_shape");//{ _(L("Bed shape")), "" }; line.widget = [this](wxWindow* parent) { auto btn = new wxButton(parent, wxID_ANY, _(L(" Set ")) + dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); btn->SetFont(wxGetApp().small_font()); -// btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("printer_empty.png")), wxBITMAP_TYPE_PNG)); - btn->SetBitmap(create_scaled_bitmap("printer_empty.png")); + btn->SetBitmap(create_scaled_bitmap("printer")); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(btn); @@ -2080,7 +2069,7 @@ void TabPrinter::append_option_line(ConfigOptionsGroupShp optgroup, const std::s PageShp TabPrinter::build_kinematics_page() { - auto page = add_options_page(_(L("Machine limits")), "cog.png", true); + auto page = add_options_page(_(L("Machine limits")), "cog", true); if (m_use_silent_mode) { // Legend for OptionsGroups @@ -2172,7 +2161,7 @@ void TabPrinter::build_extruder_pages() } if (m_extruders_count > 1 && m_config->opt_bool("single_extruder_multi_material") && !m_has_single_extruder_MM_page) { // create a page, but pretend it's an extruder page, so we can add it to m_pages ourselves - auto page = add_options_page(_(L("Single extruder MM setup")), "printer_empty.png", true); + auto page = add_options_page(_(L("Single extruder MM setup")), "printer", true); auto optgroup = page->new_optgroup(_(L("Single extruder multimaterial parameters"))); optgroup->append_single_option_line("cooling_tube_retraction"); optgroup->append_single_option_line("cooling_tube_length"); @@ -2188,7 +2177,7 @@ void TabPrinter::build_extruder_pages() //# build page char buf[512]; sprintf(buf, _CHB(L("Extruder %d")), extruder_idx + 1); - auto page = add_options_page(from_u8(buf), "funnel.png", true); + auto page = add_options_page(from_u8(buf), "funnel", true); m_pages.insert(m_pages.begin() + n_before_extruders + extruder_idx, page); auto optgroup = page->new_optgroup(_(L("Size"))); @@ -2920,8 +2909,7 @@ wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &dep deps.btn = new wxButton(parent, wxID_ANY, _(L(" Set "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); deps.btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); -// deps.btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("printer_empty.png")), wxBITMAP_TYPE_PNG)); - deps.btn->SetBitmap(create_scaled_bitmap("printer_empty.png")); + deps.btn->SetBitmap(create_scaled_bitmap("printer")); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add((deps.checkbox), 0, wxALIGN_CENTER_VERTICAL); @@ -3112,7 +3100,6 @@ ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_la bmp_name = mode == comExpert ? "mode_expert_.png" : mode == comAdvanced ? "mode_middle_.png" : "mode_simple_.png"; } -// auto bmp = new wxStaticBitmap(parent, wxID_ANY, bmp_name.empty() ? wxNullBitmap : wxBitmap(from_u8(var(bmp_name)), wxBITMAP_TYPE_PNG)); auto bmp = new wxStaticBitmap(parent, wxID_ANY, bmp_name.empty() ? wxNullBitmap : create_scaled_bitmap(bmp_name)); bmp->SetBackgroundStyle(wxBG_STYLE_PAINT); return bmp; @@ -3311,13 +3298,13 @@ void TabSLAPrint::build() m_presets = &m_preset_bundle->sla_prints; load_initial_data(); - auto page = add_options_page(_(L("Layers and perimeters")), "package_green.png"); + auto page = add_options_page(_(L("Layers and perimeters")), "layers"); auto optgroup = page->new_optgroup(_(L("Layers"))); optgroup->append_single_option_line("layer_height"); optgroup->append_single_option_line("faded_layers"); - page = add_options_page(_(L("Supports")), "building.png"); + page = add_options_page(_(L("Supports")), "sla_supports"); optgroup = page->new_optgroup(_(L("Supports"))); optgroup->append_single_option_line("supports_enable"); @@ -3355,17 +3342,17 @@ void TabSLAPrint::build() // optgroup->append_single_option_line("pad_edge_radius"); optgroup->append_single_option_line("pad_wall_slope"); - page = add_options_page(_(L("Advanced")), "wrench.png"); + page = add_options_page(_(L("Advanced")), "wrench"); optgroup = page->new_optgroup(_(L("Slicing"))); optgroup->append_single_option_line("slice_closing_radius"); - page = add_options_page(_(L("Output options")), "page_white_go.png"); + page = add_options_page(_(L("Output options")), "output+page_white"); optgroup = page->new_optgroup(_(L("Output file"))); Option option = optgroup->get_option("output_filename_format"); option.opt.full_width = true; optgroup->append_single_option_line(option); - page = add_options_page(_(L("Dependencies")), "wrench.png"); + page = add_options_page(_(L("Dependencies")), "wrench"); optgroup = page->new_optgroup(_(L("Profile dependencies"))); Line line = optgroup->create_single_option_line("compatible_printers");//Line { _(L("Compatible printers")), "" }; line.widget = [this](wxWindow* parent) { diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 55544f28e3..a27b84058d 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -8,6 +8,8 @@ #include #include +#include + #include "BitmapCache.hpp" #include "GUI.hpp" #include "GUI_App.hpp" @@ -416,31 +418,42 @@ void PrusaCollapsiblePaneMSW::Collapse(bool collapse) } #endif //__WXMSW__ + +// If an icon has horizontal orientation (width > height) call this function with is_horizontal = true +bool load_scaled_bitmap(wxBitmap** bmp, const std::string& bmp_name_in, const int px_cnt/* = 16*/, const bool is_horizontal /*= false*/) +{ + static Slic3r::GUI::BitmapCache cache; + + unsigned int height, width = height = 0; + unsigned int& scale_base = is_horizontal ? width : height; + scale_base = (unsigned int)(Slic3r::GUI::wxGetApp().em_unit() * px_cnt * 0.1f + 0.5f); + + std::string bmp_name = bmp_name_in; + boost::replace_last(bmp_name, ".png", ""); + *bmp = cache.load_svg(bmp_name, width, height); + if (*bmp == nullptr) + *bmp = cache.load_png(bmp_name, width, height); + return *bmp != nullptr; +} + +// If an icon has horizontal orientation (width > height) call this function with is_horizontal = true +wxBitmap create_scaled_bitmap(const std::string& bmp_name_in, const int px_cnt/* = 16*/, const bool is_horizontal /* = false*/) +{ + wxBitmap *bmp {nullptr}; + load_scaled_bitmap(&bmp, bmp_name_in, px_cnt, is_horizontal); + return *bmp; +} + // ***************************************************************************** // ---------------------------------------------------------------------------- // PrusaObjectDataViewModelNode // ---------------------------------------------------------------------------- -wxBitmap create_scaled_bitmap(const std::string& bmp_name) -{ - const double scale_f = Slic3r::GUI::wxGetApp().em_unit()* 0.1;//GetContentScaleFactor(); - if (scale_f == 1.0) - return wxBitmap(Slic3r::GUI::from_u8(Slic3r::var(bmp_name)), wxBITMAP_TYPE_PNG); -// else if (scale_f == 2.0) // use biger icon -// return wxBitmap(Slic3r::GUI::from_u8(Slic3r::var(bmp_name_X2)), wxBITMAP_TYPE_PNG); - - wxImage img = wxImage(Slic3r::GUI::from_u8(Slic3r::var(bmp_name)), wxBITMAP_TYPE_PNG); - const int sz_w = int(img.GetWidth()*scale_f); - const int sz_h = int(img.GetHeight()*scale_f); - img.Rescale(sz_w, sz_h, wxIMAGE_QUALITY_BILINEAR); - return wxBitmap(img); -} - void PrusaObjectDataViewModelNode::set_object_action_icon() { m_action_icon = create_scaled_bitmap("add_object.png"); } void PrusaObjectDataViewModelNode::set_part_action_icon() { - m_action_icon = create_scaled_bitmap(m_type == itVolume ? "cog.png" : "brick_go.png"); + m_action_icon = create_scaled_bitmap(m_type == itVolume ? "cog" : "brick_go.png"); } Slic3r::GUI::BitmapCache *m_bitmap_cache = nullptr; @@ -1239,6 +1252,32 @@ unsigned int PrusaObjectDataViewModel::GetChildren(const wxDataViewItem &parent, return count; } +void PrusaObjectDataViewModel::GetAllChildren(const wxDataViewItem &parent, wxDataViewItemArray &array) const +{ + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)parent.GetID(); + if (!node) { + for (auto object : m_objects) + array.Add(wxDataViewItem((void*)object)); + } + else if (node->GetChildCount() == 0) + return; + else { + const size_t count = node->GetChildren().GetCount(); + for (size_t pos = 0; pos < count; pos++) { + PrusaObjectDataViewModelNode *child = node->GetChildren().Item(pos); + array.Add(wxDataViewItem((void*)child)); + } + } + + wxDataViewItemArray new_array = array; + for (const auto item : new_array) + { + wxDataViewItemArray children; + GetAllChildren(item, children); + WX_APPEND_ARRAY(array, children); + } +} + ItemType PrusaObjectDataViewModel::GetItemType(const wxDataViewItem &item) const { if (!item.IsOk()) @@ -1456,8 +1495,8 @@ PrusaDoubleSlider::PrusaDoubleSlider(wxWindow *parent, if (!is_osx) SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX - m_bmp_thumb_higher = wxBitmap(create_scaled_bitmap(style == wxSL_HORIZONTAL ? "right_half_circle.png" : "up_half_circle.png")); - m_bmp_thumb_lower = wxBitmap(create_scaled_bitmap(style == wxSL_HORIZONTAL ? "left_half_circle.png" : "down_half_circle.png")); + m_bmp_thumb_higher = wxBitmap(style == wxSL_HORIZONTAL ? create_scaled_bitmap("right_half_circle.png") : create_scaled_bitmap("up_half_circle.png", 16, true)); + m_bmp_thumb_lower = wxBitmap(style == wxSL_HORIZONTAL ? create_scaled_bitmap("left_half_circle.png" ) : create_scaled_bitmap("down_half_circle.png",16, true)); m_thumb_size = m_bmp_thumb_lower.GetSize(); m_bmp_add_tick_on = create_scaled_bitmap("colorchange_add_on.png"); diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 27da67deb7..1a50a8adb7 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -31,7 +31,8 @@ wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxStrin wxMenuItem* append_menu_radio_item(wxMenu* menu, int id, const wxString& string, const wxString& description, std::function cb, wxEvtHandler* event_handler); -wxBitmap create_scaled_bitmap(const std::string& bmp_name); +bool load_scaled_bitmap(wxBitmap** bmp, const std::string& bmp_name, const int px_cnt=16, const bool is_horizontal = false); +wxBitmap create_scaled_bitmap(const std::string& bmp_name, const int px_cnt=16, const bool is_horizontal = false); class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup { @@ -511,7 +512,7 @@ public: virtual bool IsContainer(const wxDataViewItem &item) const override; virtual unsigned int GetChildren(const wxDataViewItem &parent, wxDataViewItemArray &array) const override; - + void GetAllChildren(const wxDataViewItem &parent,wxDataViewItemArray &array) const; // Is the container just a header or an item with all columns // In our case it is an item with all columns virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; } diff --git a/src/slic3r_app_msvc.cpp b/src/slic3r_app_msvc.cpp index 48c166406e..380d30cf40 100644 --- a/src/slic3r_app_msvc.cpp +++ b/src/slic3r_app_msvc.cpp @@ -212,7 +212,12 @@ int wmain(int argc, wchar_t **argv) argv_extended.emplace_back(nullptr); OpenGLVersionCheck opengl_version_check; - bool load_mesa = ! opengl_version_check.load_opengl_dll() || ! opengl_version_check.is_version_greater_or_equal_to(2, 0); + bool load_mesa = + // Running over a rempote desktop, and the RemoteFX is not enabled, therefore Windows will only provide SW OpenGL 1.1 context. + // In that case, use Mesa. + ::GetSystemMetrics(SM_REMOTESESSION) || + // Try to load the default OpenGL driver and test its context version. + ! opengl_version_check.load_opengl_dll() || ! opengl_version_check.is_version_greater_or_equal_to(2, 0); wchar_t path_to_exe[MAX_PATH + 1] = { 0 }; ::GetModuleFileNameW(nullptr, path_to_exe, MAX_PATH);