Merge remote-tracking branch 'origin/vb_faster_tabs' into ys_comboboxes

This commit is contained in:
YuSanka 2019-03-19 08:52:58 +01:00
commit 8be8b604f5
109 changed files with 8288 additions and 6669 deletions

View File

@ -7,7 +7,7 @@ as the versions listed - generally versions available on conservative Linux dist
Perl is not required any more.
In a typical situaction, one would open a command line, go to the Slic3r sources, create a directory called `build` or similar,
In a typical situation, one would open a command line, go to the Slic3r sources, create a directory called `build` or similar,
`cd` into it and call:
cmake ..
@ -19,7 +19,7 @@ Additional CMake flags may be applicable as explained below.
### Dependency resolution
By default Slic3r looks for dependencies the default way CMake looks for them, ie. in default system locations.
By default Slic3r looks for dependencies the default way CMake looks for them, i.e. in default system locations.
On Linux this will typically make Slic3r depend on dynamically loaded libraries from the system, however, Slic3r can be told
to specifically look for static libraries with the `SLIC3R_STATIC` flag passed to cmake:
@ -65,9 +65,9 @@ To create a debug build, use the following CMake flag:
### Installation
In runtime, Slic3r needs a way to access its resource files. By default, it looks for a `resources` directory relative to its binary.
At runtime, Slic3r needs a way to access its resource files. By default, it looks for a `resources` directory relative to its binary.
If you instead wnat Slic3r installed in a structure according to the Filesystem Hierarchy Standard, use the `SLIC3R_FHS` flag
If you instead want Slic3r installed in a structure according to the File System Hierarchy Standard, use the `SLIC3R_FHS` flag
cmake .. -DSLIC3R_FHS=1

View File

@ -18,12 +18,12 @@ This will create a dependencies bundle inside the `build/destdir` directory.
You can also customize the bundle output path using the `-DDESTDIR=<some path>` option passed to `cmake`.
**Warning**: Once the dependency bundle is installed in a destdir, the destdir cannot be moved elsewhere.
(This is because wxWidgets hardcode the installation path.)
(This is because wxWidgets hardcodes the installation path.)
### Building Slic3r
If dependencies built without an error, you can proceed to build Slic3r itself.
If dependencies are built without errors, you can proceed to build Slic3r itself.
Go back to top level Slic3r sources directory and use these commands:
mkdir build

View File

@ -7,7 +7,7 @@ CMake installer can be downloaded from [the official website](https://cmake.org/
Building with newer versions of MSVS (2015, 2017) may work too as reported by some of our users.
_Note:_ Thanks to [**@supermerill**](https://github.com/supermerill) for testing and inspiration on this guide.
_Note:_ Thanks to [**@supermerill**](https://github.com/supermerill) for testing and inspiration for this guide.
### Dependencies
@ -20,7 +20,7 @@ The package comes in a several variants:
- [32 bit, Release mode only](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=destdir-32.7z) (38 MB, 520 MB unpacked)
- [32 bit, Release and Debug mode](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=destdir-32-dev.7z) (74 MB, 1.1 GB unpacked)
When unsure, use the _Release mode only_ variant, the _Release and Debug_ variant is only needed for debugging & developement.
When unsure, use the _Release mode only_ variant, the _Release and Debug_ variant is only needed for debugging & development.
If you're unsure where to unpack the package, unpack it into `C:\local\` (but it can really be anywhere).
@ -46,13 +46,13 @@ Conversely, if you're using Visual Studio version other than 2013, the version n
If `cmake` has finished without errors, go to the build directory and open the `Slic3r.sln` solution file in Visual Studio.
Before building, make sure you're building the right project (use one of those starting with `slic3r_app_...`) and that you're building
with the right configuration, ie. _Release_ vs. _Debug_. When unsure, choose _Release_.
with the right configuration, i.e. _Release_ vs. _Debug_. When unsure, choose _Release_.
Note that you won't be able to build a _Debug_ variant against a _Release_-only dependencies package.
#### Installing using the `INSTALL` project
Slic3r PE can be run from the Visual Studio or from Visual Studio's build directory (`src\Release` or `src\Debug`),
but for longer-term usage you migth want to install somewhere using the `INSTALL` project.
but for longer-term usage you might want to install somewhere using the `INSTALL` project.
By default, this installs into `C:\Program Files\Slic3r`.
To customize the install path, use the `-DCMAKE_INSTALL_PREFIX=<path of your choice>` when invoking `cmake`.
@ -91,7 +91,7 @@ You can also use the Visual Studio GUI or other generators as mentioned above.
The `DESTDIR` option is the location where the bundle will be installed.
This may be customized. If you leave it empty, the `DESTDIR` will be places inside the same `build` directory.
Note that the build variant that you may choose using Visual Studio (ie. _Release_ or _Debug_ etc.) when building the dependency package is **not relevant**.
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.

View File

@ -2,20 +2,20 @@
The purpose of this guide is to describe how to contribute to the Slic3rPE translations. We use GNUgettext for extracting string resources from the project and PoEdit for editing translations.
Those are possible to download here:
Those can be downloaded here:
- https://sourceforge.net/directory/os:windows/?q=gnu+gettext GNUgettext package contains a set of tools to extract strings from the source code and to create the translation Catalog.
- https://poedit.net PoEdit provides good interface for the translators.
After GNUgettext is installed it is recommended to add the path to gettext/bin to PATH variable.
After GNUgettext is installed, it is recommended to add the path to gettext/bin to PATH variable.
Full manual for GNUgettext you can see here: http://www.gnu.org/software/gettext/manual/gettext.html
Full manual for GNUgettext can be seen here: http://www.gnu.org/software/gettext/manual/gettext.html
### Scenario 1. How do I add a translation or fix the existing translation
### Scenario 1. How do I add a translation or fix an existing translation
1. Get PO-file from corresponding folder here:
https://github.com/prusa3d/Slic3r/tree/master/resources/localization
2. Open this file in PoEdit as "Edit a translation"
3. Apply your corrections to translation
3. Apply your corrections to the translation
4. Push changed Slic3rPE.po and Slic3rPE.mo (will create automatically after saving of Slic3r.po in PoEdit) back to to the enter folder.
### Scenario 2. How do I add a new language support
@ -23,10 +23,10 @@ https://github.com/prusa3d/Slic3r/tree/master/resources/localization
https://github.com/prusa3d/Slic3r/tree/master/resources/localization
2. Open it in PoEdit for "Create new translation"
3. Select Translation Language (for example French).
4. As a result you will have fr.po - the file contaning translation to French.
Notice. When the transtation is complete you need to:
4. As a result you will have fr.po - the file containing translation to French.
Notice. When the translation is complete you need to:
- Rename the file to Slic3rPE.po
- Click "Save file" button. Slic3rPE.mo will be created immediatly
- Click "Save file" button. Slic3rPE.mo will be created immediately
- Both Slic3rPE.po and Slic3rPE.mo have to be saved here:
https://github.com/prusa3d/Slic3r/tree/master/resources/localization/fr
( name of folder "fr" means "French" - the translation language).
@ -37,11 +37,11 @@ Each string resource in Slic3rPE available for translation needs to be explicitl
auto msg = L("This message to be localized")
```
To get translated text use one of needed macro/function (`_(s)`, `_CHB(s)` or `L_str(s)` ).
If you add new file resourse, add it to list of files contaned macro `L()`
If you add new file resource, add it to the list of files containing macro `L()`
### Scenario 4. How do I use GNUgettext to localize my own application taking Slic3rPE as an example
1. For conviniance create list of files with this macro `L(s)`. We have
1. For convenience create a list of files with this macro `L(s)`. We have
https://github.com/prusa3d/Slic3r/tree/master/resources/localization/list.txt.
2. Create template file(*.POT) with GNUgettext command:
@ -50,11 +50,11 @@ https://github.com/prusa3d/Slic3r/tree/master/resources/localization/list.txt.
```
Use flag `--from-code=UTF-8` to specify that the source strings are in UTF-8 encoding
Use flag `--debug` to correctly extract formated strings(used %d, %s etc.)
Use flag `--debug` to correctly extract formatted strings(used %d, %s etc.)
3. Create PO- and MO-files for your project as described above.
4. To merge old PO-file with strings from creaded new POT-file use command:
4. To merge old PO-file with strings from created new POT-file use command:
```
msgmerge -N -o new.po old.po new.pot
```
@ -71,6 +71,6 @@ https://github.com/prusa3d/Slic3r/tree/master/resources/localization/list.txt.
```
Notice, in this Catalog it will be totally same strings for initial text and translated.
When you have Catalog to translation open POT or PO file in PoEdit and start to translation.
It's very important to keep attention to every gaps and punctuation. Especially with
formated strings. (used %d, %s etc.)
When you have Catalog to translation open POT or PO file in PoEdit and start to translation,
it's very important to keep attention to every gaps and punctuation. Especially with
formatted strings. (using %d, %s, etc.)

View File

@ -12,7 +12,7 @@ There are three new features:
- _System preset_: These are the presets that come with Slic3r PE installation. They come from a vendor configuration bundle (not individual files like before). They are **read-only** a user cannot modify them, but may instead create a derived User preset based on a System preset
- _User preset_: These are regular presets stored in files just like before. Additionally, they may be derived (inherited) from one of the System presets
A derived User preset keeps track of wich settings are inherited from the parent System preset and which are modified by the user. When a system preset is updated (either via installation of a new Slic3r or automatically from the internet), in a User preset the settings that are modified by the user will stay that way, while the ones that are inherited reflect the updated System preset.
A derived User preset keeps track of which settings are inherited from the parent System preset and which are modified by the user. When a system preset is updated (either via installation of a new Slic3r or automatically from the internet), in a User preset the settings that are modified by the user will stay that way, while the ones that are inherited reflect the updated System preset.
This system ensures that we don't overwrite user's settings when there is an update to the built in presets.
@ -25,9 +25,9 @@ A settings modified in a User preset has an open lock icon:
![a user setting](setting_user.png)
Clickign the open lock icon restored the system setting.
Clicking the open lock icon restores the system setting.
Additionaly, any setting that is modified but not yet saved onto disk is represented by orange label and a back-arrow:
Additionally, any setting that is modified but not yet saved onto disk is represented by orange label and a back-arrow:
![a modified setting](setting_mod.png)

View File

@ -1,14 +1,22 @@
min_slic3r_version = 1.42.0-alpha6
0.8.0-alpha7
0.8.0-alpha6
0.8.0-beta Updated SLA profiles
0.8.0-alpha9 Updated SLA and FFF profiles
0.8.0-alpha8 Updated SLA profiles
0.8.0-alpha7 Updated SLA profiles
0.8.0-alpha6 Updated SLA profiles
min_slic3r_version = 1.42.0-alpha
0.8.0-alpha
0.8.0-alpha Updated SLA profiles
0.4.0-alpha4 Updated SLA profiles
0.4.0-alpha3 Update of SLA profiles
0.4.0-alpha2 First SLA profiles
min_slic3r_version = 1.41.3-alpha
0.4.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
0.4.3 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
0.4.2 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
0.4.1 New MK2.5S and MK3S FW versions
0.4.0 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
min_slic3r_version = 1.41.1
0.3.5 New MK2.5 and MK3 FW versions
0.3.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
0.3.3 Prusament PETG released
0.3.2 New MK2.5 and MK3 FW versions
@ -41,6 +49,7 @@ min_slic3r_version = 1.41.0-alpha
0.2.0-alpha1 added initial profiles for the i3 MK3 Multi Material Upgrade 2.0
0.2.0-alpha moved machine limits from the start G-code to the new print profile parameters
min_slic3r_version = 1.40.0
0.1.13 New MK2.5 and MK3 FW versions
0.1.12 New MK2.5 and MK3 FW versions
0.1.11 fw version changed to 3.3.1
0.1.10 MK3 jerk and acceleration update

View File

@ -5,7 +5,7 @@
name = Prusa Research
# Configuration version of this file. Config file will only be installed, if the config_version differs.
# This means, the server may force the Slic3r configuration to be downgraded.
config_version = 0.8.0-alpha7
config_version = 0.8.0-beta
# Where to get the updates from?
config_update_url = https://raw.githubusercontent.com/prusa3d/Slic3r-settings/master/live/PrusaResearch/
@ -190,6 +190,14 @@ travel_speed = 180
wipe_tower_x = 170
wipe_tower_y = 125
[print:*MK306*]
fill_pattern = gyroid
fill_density = 15%
single_extruder_multi_material_priming = 0
travel_speed = 180
wipe_tower_x = 170
wipe_tower_y = 125
# Print parameters common to a 0.25mm diameter nozzle.
[print:*0.25nozzle*]
external_perimeter_extrusion_width = 0.25
@ -205,6 +213,38 @@ support_material_interface_spacing = 0.15
support_material_spacing = 1
support_material_xy_spacing = 150%
[print:*0.25nozzleMK3*]
external_perimeter_extrusion_width = 0.25
extrusion_width = 0.25
first_layer_extrusion_width = 0.35
infill_extrusion_width = 0.25
perimeter_extrusion_width = 0.25
solid_infill_extrusion_width = 0.25
top_infill_extrusion_width = 0.25
support_material_extrusion_width = 0.2
support_material_interface_layers = 0
support_material_interface_spacing = 0.15
support_material_spacing = 1
support_material_xy_spacing = 150%
perimeter_speed = 30
external_perimeter_speed = 20
small_perimeter_speed = 20
infill_speed = 45
solid_infill_speed = 45
top_solid_infill_speed = 30
support_material_speed = 40
bridge_speed = 20
gap_fill_speed = 30
perimeter_acceleration = 500
infill_acceleration = 1000
bridge_acceleration = 500
first_layer_acceleration = 500
default_acceleration = 1000
max_print_speed = 80
perimeters = 3
fill_pattern = grid
fill_density = 20%
# Print parameters common to a 0.6mm diameter nozzle.
[print:*0.6nozzle*]
external_perimeter_extrusion_width = 0.61
@ -216,6 +256,18 @@ solid_infill_extrusion_width = 0.65
top_infill_extrusion_width = 0.6
support_material_extrusion_width = 0.55
[print:*0.6nozzleMK3*]
external_perimeter_extrusion_width = 0.65
extrusion_width = 0.65
first_layer_extrusion_width = 0.65
infill_extrusion_width = 0.7
perimeter_extrusion_width = 0.65
solid_infill_extrusion_width = 0.7
top_infill_extrusion_width = 0.6
support_material_extrusion_width = 0.55
bridge_flow_ratio = 0.95
bridge_speed = 25
[print:*soluble_support*]
overhangs = 1
skirts = 0
@ -288,6 +340,9 @@ support_material_speed = 20
[print:0.05mm ULTRADETAIL 0.25 nozzle MK3]
inherits = *0.05mm*; *0.25nozzle*; *MK3*
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 and num_extruders==1
fill_pattern = grid
fill_density = 20%
first_layer_extrusion_width = 0.35
# XXXXXXXXXXXXXXXXXXXX
# XXX--- 0.07mm ---XXX
@ -326,6 +381,17 @@ fill_pattern = gyroid
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material
top_infill_extrusion_width = 0.4
[print:0.07mm ULTRADETAIL 0.25 nozzle MK3]
inherits = *0.07mm*; *0.25nozzle*; *MK3*
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 and num_extruders==1
infill_speed = 30
solid_infill_speed = 30
support_material_speed = 30
top_solid_infill_speed = 20
fill_pattern = grid
fill_density = 20%
first_layer_extrusion_width = 0.35
# XXXXXXXXXXXXXXXXXXXX
# XXX--- 0.10mm ---XXX
# XXXXXXXXXXXXXXXXXXXX
@ -382,29 +448,10 @@ top_solid_infill_speed = 30
# MK3 #
[print:0.10mm DETAIL 0.25 nozzle MK3]
inherits = *0.10mm*; *0.25nozzle*; *MK3*
bridge_speed = 30
inherits = *0.10mm*; *0.25nozzleMK3*; *MK3*
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25
external_perimeter_speed = 35
infill_acceleration = 1250
infill_speed = 200
max_print_speed = 200
perimeter_speed = 45
solid_infill_speed = 200
top_solid_infill_speed = 50
# MK3 #
[print:0.10mm DETAIL 0.6 nozzle MK3]
inherits = *0.10mm*; *0.6nozzle*; *MK3*
bridge_speed = 30
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6
external_perimeter_speed = 35
infill_acceleration = 1250
infill_speed = 200
max_print_speed = 200
perimeter_speed = 45
solid_infill_speed = 200
top_solid_infill_speed = 50
fill_pattern = grid
fill_density = 20%
# XXXXXXXXXXXXXXXXXXXX
# XXX--- 0.15mm ---XXX
@ -531,17 +578,27 @@ support_material_with_sheath = 0
support_material_xy_spacing = 80%
# MK3 #
[print:0.15mm OPTIMAL 0.25 nozzle MK3]
inherits = *0.15mm*; *0.25nozzle*; *MK3*
bridge_speed = 30
[print:0.15mm QUALITY 0.25 nozzle MK3]
inherits = *0.15mm*; *0.25nozzleMK3*; *MK3*
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25
fill_pattern = grid
fill_density = 20%
# MK3 #
[print:0.15mm DETAIL 0.6 nozzle MK3]
inherits = *0.15mm*; *0.6nozzleMK3*; *MK306*
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6
external_perimeter_speed = 35
infill_acceleration = 1250
infill_speed = 200
max_print_speed = 200
infill_speed = 70
max_print_speed = 100
perimeter_speed = 45
solid_infill_speed = 200
top_solid_infill_speed = 50
solid_infill_speed = 70
top_solid_infill_speed = 45
# XXXXXXXXXXXXXXXXXXXX
# XXX--- 0.20mm ---XXX
# XXXXXXXXXXXXXXXXXXXX
[print:*0.20mm*]
inherits = *common*
@ -557,23 +614,6 @@ solid_infill_speed = 50
top_infill_extrusion_width = 0.4
top_solid_layers = 5
# MK3 #
[print:0.15mm OPTIMAL 0.6 nozzle MK3]
inherits = *0.15mm*; *0.6nozzle*; *MK3*
bridge_speed = 30
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6
external_perimeter_speed = 35
infill_acceleration = 1250
infill_speed = 200
max_print_speed = 200
perimeter_speed = 45
solid_infill_speed = 200
top_solid_infill_speed = 50
# XXXXXXXXXXXXXXXXXXXX
# XXX--- 0.20mm ---XXX
# XXXXXXXXXXXXXXXXXXXX
# MK2 #
[print:0.20mm 100mms Linear Advance]
inherits = *0.20mm*
@ -664,17 +704,68 @@ support_material_with_sheath = 0
support_material_xy_spacing = 80%
# MK3 #
[print:0.20mm FAST 0.6 nozzle MK3]
inherits = *0.20mm*; *0.6nozzle*; *MK3*
bridge_speed = 30
[print:0.20mm DETAIL 0.6 nozzle MK3]
inherits = *0.20mm*; *0.6nozzleMK3*; *MK306*
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6
external_perimeter_speed = 35
infill_acceleration = 1250
infill_speed = 200
max_print_speed = 200
infill_speed = 70
max_print_speed = 100
perimeter_speed = 45
solid_infill_speed = 200
top_solid_infill_speed = 50
solid_infill_speed = 70
top_solid_infill_speed = 45
# XXXXXXXXXXXXXXXXXXXX
# XXX--- 0.30mm ---XXX
# XXXXXXXXXXXXXXXXXXXX
[print:*0.30mm*]
inherits = *common*
bottom_solid_layers = 4
bridge_flow_ratio = 0.95
external_perimeter_speed = 40
infill_acceleration = 2000
infill_speed = 60
layer_height = 0.3
perimeter_acceleration = 800
perimeter_speed = 50
solid_infill_speed = 50
top_infill_extrusion_width = 0.4
top_solid_layers = 4
[print:0.30mm QUALITY 0.6 nozzle MK3]
inherits = *0.30mm*; *0.6nozzleMK3*; *MK306*
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6
external_perimeter_speed = 35
infill_acceleration = 1250
infill_speed = 70
max_print_speed = 100
perimeter_speed = 45
solid_infill_speed = 70
top_solid_infill_speed = 45
[print:0.30mm DRAFT MK3]
inherits = *0.30mm*; *MK3*
bottom_solid_layers = 3
bridge_speed = 30
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4
external_perimeter_speed = 35
infill_acceleration = 1250
infill_speed = 85
max_print_speed = 200
perimeter_speed = 50
small_perimeter_speed = 30
solid_infill_speed = 80
top_solid_infill_speed = 40
support_material_speed = 45
external_perimeter_extrusion_width = 0.6
extrusion_width = 0.5
first_layer_extrusion_width = 0.42
infill_extrusion_width = 0.5
perimeter_extrusion_width = 0.5
solid_infill_extrusion_width = 0.5
top_infill_extrusion_width = 0.45
support_material_extrusion_width = 0.35
# XXXXXXXXXXXXXXXXXXXX
# XXX--- 0.35mm ---XXX
@ -732,6 +823,57 @@ support_material_interface_layers = 2
support_material_with_sheath = 0
support_material_xy_spacing = 150%
# MK3 #
[print:0.35mm SPEED 0.6 nozzle MK3]
inherits = *0.35mm*; *0.6nozzleMK3*; *MK306*
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6
external_perimeter_speed = 35
infill_acceleration = 1250
infill_speed = 70
max_print_speed = 100
perimeter_speed = 45
solid_infill_speed = 70
top_solid_infill_speed = 45
external_perimeter_extrusion_width = 0.68
perimeter_extrusion_width = 0.68
# XXXXXXXXXXXXXXXXXXXX
# XXX--- 0.40mm ---XXX
# XXXXXXXXXXXXXXXXXXXX
[print:*0.40mm*]
inherits = *common*
bottom_solid_layers = 3
external_perimeter_extrusion_width = 0.6
external_perimeter_speed = 40
first_layer_extrusion_width = 0.65
infill_acceleration = 2000
infill_speed = 60
layer_height = 0.4
perimeter_acceleration = 800
perimeter_extrusion_width = 0.65
perimeter_speed = 50
solid_infill_extrusion_width = 0.65
solid_infill_speed = 60
top_solid_infill_speed = 40
top_solid_layers = 4
# MK3 #
[print:0.40mm DRAFT 0.6 nozzle MK3]
inherits = *0.40mm*; *0.6nozzleMK3*; *MK306*
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6
external_perimeter_speed = 35
infill_acceleration = 1250
infill_speed = 70
max_print_speed = 100
perimeter_speed = 45
solid_infill_speed = 70
top_solid_infill_speed = 45
external_perimeter_extrusion_width = 0.7
perimeter_extrusion_width = 0.7
infill_extrusion_width = 0.72
solid_infill_extrusion_width = 0.72
# XXXXXXXXXXXXXXXXXXXXXX
# XXX----- MK2.5 ----XXX
# XXXXXXXXXXXXXXXXXXXXXX
@ -827,7 +969,7 @@ filament_settings_id = ""
filament_soluble = 0
min_print_speed = 15
slowdown_below_layer_time = 20
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}30{endif}; Filament gcode"
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}30{endif} ; Filament gcode"
[filament:*PLA*]
inherits = *common*
@ -844,6 +986,7 @@ first_layer_temperature = 215
max_fan_speed = 100
min_fan_speed = 100
temperature = 210
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{elsif nozzle_diameter[0]==0.6}15{else}30{endif} ; Filament gcode"
[filament:*PET*]
inherits = *common*
@ -859,9 +1002,14 @@ first_layer_bed_temperature = 85
first_layer_temperature = 230
max_fan_speed = 50
min_fan_speed = 30
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}45{endif}; Filament gcode"
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{elsif nozzle_diameter[0]==0.6}22{else}45{endif} ; Filament gcode"
temperature = 240
[filament:*PET06*]
inherits = *PET*
compatible_printers_condition = nozzle_diameter[0]==0.6 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
filament_max_volumetric_speed = 15
[filament:*ABS*]
inherits = *common*
bed_temperature = 110
@ -879,6 +1027,7 @@ first_layer_temperature = 255
max_fan_speed = 30
min_fan_speed = 20
temperature = 255
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{elsif nozzle_diameter[0]==0.6}15{else}30{endif} ; Filament gcode"
[filament:*FLEX*]
inherits = *common*
@ -906,10 +1055,11 @@ inherits = *PLA*
# For now, all but selected filaments are disabled for the MMU 2.0
compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
extrusion_multiplier = 1.2
filament_cost = 80.65
filament_density = 4
filament_cost = 56.64
filament_density = 3.9
filament_colour = #804040
filament_max_volumetric_speed = 10
filament_max_volumetric_speed = 9
filament_notes = "List of materials tested with standard print settings:\n\nColorFabb bronzeFill\nColorFabb brassFill\nColorFabb steelFill\nColorFabb copperFill"
[filament:ColorFabb HT]
inherits = *PET*
@ -933,19 +1083,31 @@ inherits = *PLA*
filament_cost = 55.5
filament_density = 1.24
[filament:ColorFabb Woodfil]
[filament:ColorFabb woodFill]
inherits = *PLA*
# For now, all but selected filaments are disabled for the MMU 2.0
compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
extrusion_multiplier = 1.2
filament_cost = 62.9
filament_density = 1.15
filament_colour = #804040
filament_colour = #dfc287
filament_max_volumetric_speed = 10
first_layer_temperature = 200
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode"
temperature = 200
[filament:ColorFabb corkFill]
inherits = *PLA*
compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
extrusion_multiplier = 1.2
filament_cost = 45.45
filament_density = 1.18
filament_colour = #634d33
filament_max_volumetric_speed = 6
first_layer_temperature = 220
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode"
temperature = 220
[filament:ColorFabb XT]
inherits = *PET*
filament_type = PET
@ -1001,7 +1163,7 @@ temperature = 260
inherits = *PET*
filament_cost = 56.9
filament_density = 1.26
filament_notes = "List of manufacturers tested with standart PET print settings:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladec PETG"
filament_notes = "List of manufacturers tested with standard PET print settings:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladec PETG"
[filament:E3D PC-ABS]
inherits = *ABS*
@ -1036,7 +1198,7 @@ max_fan_speed = 50
min_fan_speed = 50
temperature = 275
[filament:Fillamentum Timberfil]
[filament:Fillamentum Timberfill]
inherits = *PLA*
# For now, all but selected filaments are disabled for the MMU 2.0
compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
@ -1053,19 +1215,19 @@ temperature = 190
inherits = *ABS*
filament_cost = 27.82
filament_density = 1.04
filament_notes = "List of materials tested with standart ABS print settings:\n\nEsun ABS\nFil-A-Gehr ABS\nHatchboxABS\nPlasty Mladec ABS"
filament_notes = "List of materials tested with standard ABS print settings:\n\nEsun ABS\nFil-A-Gehr ABS\nHatchboxABS\nPlasty Mladec ABS"
[filament:Generic PET]
inherits = *PET*
filament_cost = 27.82
filament_density = 1.27
filament_notes = "List of manufacturers tested with standart PET print settings:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladec PETG"
filament_notes = "List of manufacturers tested with standard PET print settings:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladec PETG"
[filament:Generic PLA]
inherits = *PLA*
filament_cost = 25.4
filament_density = 1.24
filament_notes = "List of materials tested with standart PLA print settings:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladec PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nVerbatim PLA\nVerbatim BVOH"
filament_notes = "List of materials tested with standard PLA print settings:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladec PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nVerbatim PLA\nVerbatim BVOH"
[filament:Polymaker PC-Max]
inherits = *ABS*
@ -1084,11 +1246,11 @@ filament_density = 1.23
cooling = 0
fan_always_on = 0
filament_colour = #FFFFD7
filament_max_volumetric_speed = 10
filament_notes = "List of materials tested with standart PVA print settings:\n\nPrimaSelect PVA+\nICE FILAMENTS PVA 'NAUGHTY NATURAL'\nVerbatim BVOH"
filament_max_volumetric_speed = 4
filament_notes = "List of materials tested with standard PVA print settings:\n\nPrimaSelect PVA+\nICE FILAMENTS PVA 'NAUGHTY NATURAL'"
filament_ramming_parameters = "120 100 8.3871 8.6129 8.93548 9.22581 9.48387 9.70968 9.87097 10.0323 10.2258 10.4194 10.6452 10.8065| 0.05 8.34193 0.45 8.73548 0.95 9.34836 1.45 9.78385 1.95 10.0871 2.45 10.5161 2.95 10.8903 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6"
filament_soluble = 1
filament_type = PVA
filament_type = PLA
first_layer_temperature = 195
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode"
temperature = 195
@ -1097,7 +1259,7 @@ temperature = 195
inherits = *ABS*
filament_cost = 27.82
filament_density = 1.08
filament_notes = "List of materials tested with standart ABS print settings:\n\nEsun ABS\nFil-A-Gehr ABS\nHatchboxABS\nPlasty Mladec ABS"
filament_notes = "List of materials tested with standard ABS print settings:\n\nEsun ABS\nFil-A-Gehr ABS\nHatchboxABS\nPlasty Mladec ABS"
[filament:*ABS MMU2*]
inherits = Prusa ABS
@ -1140,7 +1302,8 @@ temperature = 220
inherits = *PET*
filament_cost = 27.82
filament_density = 1.27
filament_notes = "List of manufacturers tested with standart PET print settings:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladec PETG"
filament_notes = "List of manufacturers tested with standard PET print settings:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladec PETG"
compatible_printers_condition = nozzle_diameter[0]!=0.6 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Prusament PETG]
inherits = *PET*
@ -1148,6 +1311,20 @@ first_layer_temperature = 240
temperature = 250
filament_cost = 24.99
filament_density = 1.27
compatible_printers_condition = nozzle_diameter[0]!=0.6 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Prusa PET 0.6 nozzle]
inherits = *PET06*
filament_cost = 27.82
filament_density = 1.27
filament_notes = "List of manufacturers tested with standard PET print settings:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladec PETG"
[filament:Prusament PETG 0.6 nozzle]
inherits = *PET06*
first_layer_temperature = 240
temperature = 250
filament_cost = 24.99
filament_density = 1.27
[filament:*PET MMU2*]
inherits = Prusa PET
@ -1179,7 +1356,7 @@ inherits = *PET MMU2*
inherits = *PLA*
filament_cost = 25.4
filament_density = 1.24
filament_notes = "List of materials tested with standart PLA print settings:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladec PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nVerbatim PLA\nVerbatim BVOH"
filament_notes = "List of materials tested with standard PLA print settings:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladec PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nVerbatim PLA\nVerbatim BVOH"
[filament:Prusament PLA]
inherits = *PLA*
@ -1265,7 +1442,7 @@ fan_always_on = 0
fan_below_layer_time = 100
filament_colour = #FFFFD7
filament_max_volumetric_speed = 4
filament_notes = "List of materials tested with standart PLA print settings:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladec PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nVerbatim PLA\nVerbatim BVOH"
filament_notes = "List of materials tested with standard PVA print settings:\n\nVerbatim BVOH"
filament_soluble = 1
filament_type = PLA
first_layer_bed_temperature = 60
@ -1279,7 +1456,6 @@ temperature = 210
inherits = Verbatim BVOH
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material
temperature = 195
filament_notes = BVOH
fan_always_on = 1
first_layer_temperature = 200
filament_cooling_final_speed = 1
@ -1316,7 +1492,7 @@ filament_loading_speed = 14
filament_loading_speed_start = 19
filament_max_volumetric_speed = 4
filament_minimal_purge_on_wipe_tower = 5
filament_notes = PVA
filament_notes = "List of materials tested with standard PVA print settings:\n\nPrimaSelect PVA+"
filament_ramming_parameters = "120 110 3.83871 3.90323 3.96774 4.03226 4.09677 4.19355 4.3871 4.83871 5.67742 6.93548 8.54839 10.3226 11.9677 13.2581 14.129 14.5806| 0.05 3.8258 0.45 3.89676 0.95 4.05807 1.45 4.23548 1.95 5.18386 2.45 7.80651 2.95 11.5356 3.45 13.9872 3.95 14.7613 4.45 7.6 4.95 7.6"
filament_soluble = 1
filament_toolchange_delay = 0
@ -1346,7 +1522,7 @@ fan_always_on = 1
fan_below_layer_time = 100
filament_colour = #DEE0E6
filament_max_volumetric_speed = 5
filament_notes = "List of materials tested with standart PLA print settings:\n\nEsun PLA\nFiberlogy HD-PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladec PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nEUMAKERS PLA"
filament_notes = "List of materials tested with standard PLA print settings:\n\nEsun PLA\nFiberlogy HD-PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladec PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nEUMAKERS PLA"
filament_type = PLA
first_layer_bed_temperature = 100
first_layer_temperature = 220
@ -1358,14 +1534,15 @@ temperature = 220
[sla_print:*common*]
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_SL1.*/
layer_height = 0.05
output_filename_format = [input_filename_base].dwz
output_filename_format = [input_filename_base].sl1
pad_edge_radius = 0.5
pad_enable = 1
pad_max_merge_distance = 50
pad_wall_height = 3
pad_wall_height = 0
pad_wall_thickness = 1
pad_wall_slope = 90
support_base_diameter = 3
support_base_height = 0.5
support_base_height = 1
support_critical_angle = 45
support_density_at_45 = 250
support_density_at_horizontal = 500
@ -1376,6 +1553,7 @@ support_max_bridge_length = 10
support_minimal_z = 0
support_object_elevation = 5
support_pillar_diameter = 1
support_pillar_connection_mode = zigzag
support_pillar_widening_factor = 0
supports_enable = 1
@ -1441,6 +1619,11 @@ inherits = *common 0.025*
exposure_time = 5
initial_exposure_time = 35
[sla_material:SL1 Orange solid 0.025]
inherits = *common 0.025*
exposure_time = 5
initial_exposure_time = 35
########### Materials 0.05
[sla_material:3DM-HTR140 (high temperature) 0.05]
@ -1458,11 +1641,31 @@ inherits = *common 0.05*
exposure_time = 8
initial_exposure_time = 45
[sla_material:Bluecast Keramaster Dental 0.05]
inherits = *common 0.05*
exposure_time = 7
initial_exposure_time = 45
[sla_material:Bluecast LCD-DLP Original 0.05]
inherits = *common 0.05*
exposure_time = 10
initial_exposure_time = 60
[sla_material:Bluecast Phrozen Wax 0.05]
inherits = *common 0.05*
exposure_time = 10
initial_exposure_time = 55
[sla_material:Bluecast S+ 0.05]
inherits = *common 0.05*
exposure_time = 9
initial_exposure_time = 45
[sla_material:Bluecast X2 0.05]
inherits = *common 0.05*
exposure_time = 10
initial_exposure_time = 60
[sla_material:Jamg He PJHC-00 Yellow 0.05]
inherits = *common 0.05*
exposure_time = 7
@ -1475,7 +1678,7 @@ initial_exposure_time = 45
[sla_material:Jamg He PJHC-30 Orange 0.05]
inherits = *common 0.05*
exposure_time = 7
exposure_time = 7.5
initial_exposure_time = 45
[sla_material:Jamg He PJHC-60 Gray 0.05]
@ -1513,8 +1716,6 @@ inherits = *common 0.05*
exposure_time = 7
initial_exposure_time = 40
# v2
[sla_material:3DM-ABS 0.05]
inherits = *common 0.05*
exposure_time = 9
@ -1560,11 +1761,101 @@ inherits = *common 0.05*
exposure_time = 6.5
initial_exposure_time = 40
[sla_material:Harz Labs Model Resin Cherry 0.05]
inherits = *common 0.05*
exposure_time = 8
initial_exposure_time = 45
[sla_material:Jamg He CRX-70C High Tenacity Black 0.05]
inherits = *common 0.05*
exposure_time = 7
initial_exposure_time = 40
[sla_material:Jamg He MC-2000 Casting Green 0.05]
inherits = *common 0.05*
exposure_time = 13
initial_exposure_time = 40
[sla_material:Jamg He PJHC-00 Solid Yellow 0.05]
inherits = *common 0.05*
exposure_time = 7
initial_exposure_time = 40
[sla_material:Jamg He PJHC-20 White 0.05]
inherits = *common 0.05*
exposure_time = 7
initial_exposure_time = 45
[sla_material:Jamg He PJHC-80 Transparent Green 0.05]
inherits = *common 0.05*
exposure_time = 8
initial_exposure_time = 45
[sla_material:Jamg He PJHC-80 Transparent Red 0.05]
inherits = *common 0.05*
exposure_time = 7
initial_exposure_time = 45
[sla_material:Jamg He PJHC-81 Solid Maroon 0.05]
inherits = *common 0.05*
exposure_time = 9
initial_exposure_time = 45
[sla_material:Jamg He PJHC-90 Solid Pink 0.05]
inherits = *common 0.05*
exposure_time = 7
initial_exposure_time = 40
[sla_material:Jamg He RJHC-00 Yellow Flexible 0.05]
inherits = *common 0.05*
exposure_time = 9
initial_exposure_time = 40
[sla_material:Jamg He RJHC-10 Clear Flexible 0.05]
inherits = *common 0.05*
exposure_time = 9
initial_exposure_time = 40
[sla_material:Jamg He RJHC-20 White Flexible 0.05]
inherits = *common 0.05*
exposure_time = 9
initial_exposure_time = 40
[sla_material:Jamg He RJHC-50 Blue Flexible 0.05]
inherits = *common 0.05*
exposure_time = 9
initial_exposure_time = 40
[sla_material:Jamg He RJHC-70 Black Flexible 0.05]
inherits = *common 0.05*
exposure_time = 9
initial_exposure_time = 40
[sla_material:Jamg He RJHC-81 Red Flexible 0.05]
inherits = *common 0.05*
exposure_time = 9
initial_exposure_time = 40
[sla_material:SL1 Orange solid 0.05]
inherits = *common 0.05*
exposure_time = 7.5
initial_exposure_time = 45
[sla_material:SL1 Red transparent 0.05]
inherits = *common 0.05*
exposure_time = 7.5
initial_exposure_time = 45
########### Materials 0.035
[sla_material:Jamg He PJHC-30 Orange 0.035]
inherits = *common 0.035*
exposure_time = 9
exposure_time = 6
initial_exposure_time = 35
[sla_material:SL1 Orange solid 0.035]
inherits = *common 0.035*
exposure_time = 6
initial_exposure_time = 35
########### Materials 0.1
@ -1574,6 +1865,11 @@ inherits = *common 0.1*
exposure_time = 10
initial_exposure_time = 45
[sla_material:SL1 Orange solid 0.1]
inherits = *common 0.1*
exposure_time = 10
initial_exposure_time = 45
[printer:*common*]
printer_technology = FFF
bed_shape = 0x0,250x0,250x210,0x210
@ -1740,19 +2036,19 @@ min_layer_height = 0.1
inherits = Original Prusa i3 MK2S
printer_model = MK2.5
remaining_times = 1
start_gcode = M115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
start_gcode = M115 U3.6.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
[printer:Original Prusa i3 MK2.5 0.25 nozzle]
inherits = Original Prusa i3 MK2S 0.25 nozzle
printer_model = MK2.5
remaining_times = 1
start_gcode = M115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
start_gcode = M115 U3.6.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
[printer:Original Prusa i3 MK2.5 0.6 nozzle]
inherits = Original Prusa i3 MK2S 0.6 nozzle
printer_model = MK2.5
remaining_times = 1
start_gcode = M115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
start_gcode = M115 U3.6.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
[printer:Original Prusa i3 MK2.5 MMU2 Single]
inherits = Original Prusa i3 MK2.5; *mm2*
@ -1781,7 +2077,7 @@ machine_min_travel_rate = 0
default_print_profile = 0.15mm OPTIMAL MK2.5
default_filament_profile = Prusament PLA
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2.5\n
start_gcode = M107\nM115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
start_gcode = M107\nM115 U3.6.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
end_gcode = G1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200; home X axis\nM84 ; disable motors
[printer:Original Prusa i3 MK2.5 MMU2]
@ -1815,20 +2111,23 @@ single_extruder_multi_material = 1
# to be defined explicitely.
nozzle_diameter = 0.4,0.4,0.4,0.4,0.4
extruder_colour = #FF8000;#DB5182;#00FFFF;#FF4F4F;#9FFF9F
start_gcode = M107\nM115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
start_gcode = M107\nM115 U3.6.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
end_gcode = {if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n; Lift print head a bit\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200; home X axis\nM84 ; disable motors\n
[printer:Original Prusa i3 MK2.5S]
inherits = Original Prusa i3 MK2.5
printer_model = MK2.5S
start_gcode = M115 U3.6.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
[printer:Original Prusa i3 MK2.5S 0.25 nozzle]
inherits = Original Prusa i3 MK2.5 0.25 nozzle
printer_model = MK2.5S
start_gcode = M115 U3.6.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
[printer:Original Prusa i3 MK2.5S 0.6 nozzle]
inherits = Original Prusa i3 MK2.5 0.6 nozzle
printer_model = MK2.5S
start_gcode = M115 U3.6.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
[printer:Original Prusa i3 MK2.5S MMU2S Single]
inherits = Original Prusa i3 MK2.5; *mm2s*
@ -1857,7 +2156,7 @@ machine_min_travel_rate = 0
default_print_profile = 0.15mm OPTIMAL MK2.5
default_filament_profile = Prusament PLA
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2.5\n
start_gcode = M107\nM115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
start_gcode = M107\nM115 U3.6.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
end_gcode = G1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200; home X axis\nM84 ; disable motors
[printer:Original Prusa i3 MK2.5S MMU2S]
@ -1891,7 +2190,7 @@ single_extruder_multi_material = 1
# to be defined explicitely.
nozzle_diameter = 0.4,0.4,0.4,0.4,0.4
extruder_colour = #FF8000;#DB5182;#00FFFF;#FF4F4F;#9FFF9F
start_gcode = M107\nM115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
start_gcode = M107\nM115 U3.6.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
end_gcode = {if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n; Lift print head a bit\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200; home X axis\nM84 ; disable motors\n
@ -1923,7 +2222,7 @@ remaining_times = 1
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK3\n
retract_lift_below = 209
max_print_height = 210
start_gcode = M115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif}
start_gcode = M115 U3.6.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif}
printer_model = MK3
default_print_profile = 0.15mm QUALITY MK3
@ -1933,27 +2232,31 @@ nozzle_diameter = 0.25
max_layer_height = 0.15
min_layer_height = 0.05
printer_variant = 0.25
start_gcode = M115 U3.6.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E8.0 F700.0 ; intro line\nG1 X100.0 E12.5 F700.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif}
default_print_profile = 0.10mm DETAIL 0.25 nozzle MK3
[printer:Original Prusa i3 MK3 0.6 nozzle]
inherits = Original Prusa i3 MK3
nozzle_diameter = 0.6
max_layer_height = 0.35
min_layer_height = 0.1
max_layer_height = 0.40
min_layer_height = 0.15
printer_variant = 0.6
default_print_profile = 0.15mm OPTIMAL 0.6 nozzle MK3
default_print_profile = 0.30mm QUALITY 0.6 nozzle MK3
[printer:Original Prusa i3 MK3S]
inherits = Original Prusa i3 MK3
printer_model = MK3S
start_gcode = M115 U3.6.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif}
[printer:Original Prusa i3 MK3S 0.25 nozzle]
inherits = Original Prusa i3 MK3 0.25 nozzle
printer_model = MK3S
start_gcode = M115 U3.6.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E8.0 F700.0 ; intro line\nG1 X100.0 E12.5 F700.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif}
[printer:Original Prusa i3 MK3S 0.6 nozzle]
inherits = Original Prusa i3 MK3 0.6 nozzle
printer_model = MK3S
start_gcode = M115 U3.6.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif}
[printer:*mm2*]
inherits = Original Prusa i3 MK3
@ -1983,7 +2286,7 @@ default_filament_profile = Prusament PLA MMU2
inherits = *mm2*
single_extruder_multi_material = 0
default_filament_profile = Prusament PLA
start_gcode = M107\nM115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
start_gcode = M107\nM115 U3.6.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
end_gcode = G1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200; home X axis\nM84 ; disable motors
[printer:Original Prusa i3 MK3 MMU2]
@ -1994,14 +2297,14 @@ inherits = *mm2*
machine_max_acceleration_e = 8000,8000
nozzle_diameter = 0.4,0.4,0.4,0.4,0.4
extruder_colour = #FF8000;#DB5182;#00FFFF;#FF4F4F;#9FFF9F
start_gcode = M107\nM115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
start_gcode = M107\nM115 U3.6.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
end_gcode = {if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n; Lift print head a bit\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200; home X axis\nM84 ; disable motors\n
[printer:Original Prusa i3 MK3S MMU2S Single]
inherits = *mm2s*
single_extruder_multi_material = 0
default_filament_profile = Prusament PLA
start_gcode = M107\nM115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
start_gcode = M107\nM115 U3.6.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
end_gcode = G1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200; home X axis\nM84 ; disable motors
[printer:Original Prusa i3 MK3S MMU2S]
@ -2009,14 +2312,14 @@ inherits = *mm2s*
machine_max_acceleration_e = 8000,8000
nozzle_diameter = 0.4,0.4,0.4,0.4,0.4
extruder_colour = #FF8000;#DB5182;#00FFFF;#FF4F4F;#9FFF9F
start_gcode = M107\nM115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
start_gcode = M107\nM115 U3.6.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
end_gcode = {if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n; Lift print head a bit\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200; home X axis\nM84 ; disable motors\n
[printer:Original Prusa SL1]
printer_technology = SLA
printer_model = SL1
printer_variant = default
default_sla_material_profile = Jamg He Transparent Green 0.05
default_sla_material_profile = Jamg He PJHC-30 Orange 0.05
default_sla_print_profile = 0.05 Normal
bed_shape = 0.98x1.02,119.98x1.02,119.98x68.02,0.98x68.02
display_height = 68.04

View File

@ -88,9 +88,9 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/msw/slic3r.rc.in ${CMAKE_CUR
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/msw/slic3r.manifest.in ${CMAKE_CURRENT_BINARY_DIR}/slic3r.manifest @ONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/osx/Info.plist.in ${CMAKE_CURRENT_BINARY_DIR}/Info.plist @ONLY)
if (MSVC)
add_library(slic3r SHARED slic3r.cpp)
add_library(slic3r SHARED slic3r.cpp slic3r.hpp)
else ()
add_executable(slic3r slic3r.cpp)
add_executable(slic3r slic3r.cpp slic3r.hpp)
endif ()
if (NOT MSVC)
if(SLIC3R_GUI)
@ -153,19 +153,14 @@ endif ()
# Also the shim may load the Mesa software OpenGL renderer if the default renderer does not support OpenGL 2.0 and higher.
if (MSVC)
add_executable(slic3r_app_gui WIN32 slic3r_app_msvc.cpp ${CMAKE_CURRENT_BINARY_DIR}/slic3r.rc)
target_compile_definitions(slic3r_app_gui PRIVATE -DSLIC3R_WRAPPER_NOCONSOLE -DSLIC3R_WRAPPER_GUI)
target_compile_definitions(slic3r_app_gui PRIVATE -DSLIC3R_WRAPPER_NOCONSOLE)
add_dependencies(slic3r_app_gui slic3r)
set_target_properties(slic3r_app_gui PROPERTIES OUTPUT_NAME "slic3r")
add_executable(slic3r_app_console slic3r_app_msvc.cpp ${CMAKE_CURRENT_BINARY_DIR}/slic3r.rc)
target_compile_definitions(slic3r_app_console PRIVATE -DSLIC3R_WRAPPER_CONSOLE -DSLIC3R_WRAPPER_NOGUI)
target_compile_definitions(slic3r_app_console PRIVATE -DSLIC3R_WRAPPER_CONSOLE)
add_dependencies(slic3r_app_console slic3r)
set_target_properties(slic3r_app_console PROPERTIES OUTPUT_NAME "slic3r-console")
add_executable(slic3r_app_noconsole WIN32 slic3r_app_msvc.cpp ${CMAKE_CURRENT_BINARY_DIR}/slic3r.rc)
target_compile_definitions(slic3r_app_noconsole PRIVATE -DSLIC3R_WRAPPER_NOCONSOLE -DSLIC3R_WRAPPER_NOGUI)
add_dependencies(slic3r_app_noconsole slic3r)
set_target_properties(slic3r_app_noconsole PROPERTIES OUTPUT_NAME "slic3r-noconsole")
endif ()
# Link the resources dir to where Slic3r GUI expects it
@ -213,7 +208,6 @@ if (WIN32)
if (MSVC)
install(TARGETS slic3r_app_gui RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}")
install(TARGETS slic3r_app_console RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}")
install(TARGETS slic3r_app_noconsole RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}")
endif ()
else ()
install(TARGETS slic3r RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")

View File

@ -293,8 +293,8 @@ static int stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge, stl_vertex *
{
// Index of a grid cell spaced by tolerance.
typedef Eigen::Matrix<int32_t, 3, 1, Eigen::DontAlign> Vec3i;
Vec3i vertex1 = (*a / tolerance).cast<int32_t>();
Vec3i vertex2 = (*b / tolerance).cast<int32_t>();
Vec3i vertex1 = ((*a - stl->stats.min) / tolerance).cast<int32_t>();
Vec3i vertex2 = ((*b - stl->stats.min) / tolerance).cast<int32_t>();
static_assert(sizeof(Vec3i) == 12, "size of Vec3i incorrect");
if (vertex1 == vertex2)

View File

@ -143,7 +143,7 @@ stl_generate_shared_vertices(stl_file *stl) {
}
void
stl_write_off(stl_file *stl, char *file) {
stl_write_off(stl_file *stl, const char *file) {
int i;
FILE *fp;
char *error_msg;
@ -179,7 +179,7 @@ stl_write_off(stl_file *stl, char *file) {
}
void
stl_write_vrml(stl_file *stl, char *file) {
stl_write_vrml(stl_file *stl, const char *file) {
int i;
FILE *fp;
char *error_msg;
@ -236,7 +236,7 @@ stl_write_vrml(stl_file *stl, char *file) {
fclose(fp);
}
void stl_write_obj (stl_file *stl, char *file) {
void stl_write_obj (stl_file *stl, const char *file) {
int i;
FILE* fp;

View File

@ -177,10 +177,10 @@ extern void stl_transform(stl_file *stl, const Eigen::Transform<double, 3, Eigen
extern void stl_open_merge(stl_file *stl, char *file);
extern void stl_invalidate_shared_vertices(stl_file *stl);
extern void stl_generate_shared_vertices(stl_file *stl);
extern void stl_write_obj(stl_file *stl, char *file);
extern void stl_write_off(stl_file *stl, char *file);
extern void stl_write_dxf(stl_file *stl, char *file, char *label);
extern void stl_write_vrml(stl_file *stl, char *file);
extern void stl_write_obj(stl_file *stl, const char *file);
extern void stl_write_off(stl_file *stl, const char *file);
extern void stl_write_dxf(stl_file *stl, const char *file, char *label);
extern void stl_write_vrml(stl_file *stl, const char *file);
inline void stl_calculate_normal(stl_normal &normal, stl_facet *facet) {
normal = (facet->vertex[1] - facet->vertex[0]).cross(facet->vertex[2] - facet->vertex[0]);
}

View File

@ -365,7 +365,7 @@ stl_write_quad_object(stl_file *stl, char *file) {
}
void
stl_write_dxf(stl_file *stl, char *file, char *label) {
stl_write_dxf(stl_file *stl, const char *file, char *label) {
int i;
FILE *fp;
char *error_msg;

View File

@ -14,6 +14,8 @@ public:
localmethod_ = m;
return *this;
}
inline void seed(unsigned long val) { nlopt::srand(val); }
};
template<>

View File

@ -3,6 +3,7 @@
#include <assert.h>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <exception> // std::runtime_error
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/classification.hpp>
@ -14,6 +15,7 @@
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/nowide/cenv.hpp>
#include <boost/nowide/iostream.hpp>
#include <boost/nowide/fstream.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/format.hpp>
@ -190,6 +192,123 @@ bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &o
}
}
std::vector<std::string> ConfigOptionDef::cli_args(const std::string &key) const
{
std::vector<std::string> args;
if (this->cli != ConfigOptionDef::nocli) {
std::string cli = this->cli.substr(0, this->cli.find("="));
boost::trim_right_if(cli, boost::is_any_of("!"));
if (cli.empty()) {
// Add the key
std::string opt = key;
boost::replace_all(opt, "_", "-");
args.emplace_back(std::move(opt));
} else
boost::split(args, cli, boost::is_any_of("|"));
}
return args;
}
std::string ConfigOptionDef::nocli = "~~~noCLI";
std::ostream& ConfigDef::print_cli_help(std::ostream& out, bool show_defaults, std::function<bool(const ConfigOptionDef &)> filter) const
{
// prepare a function for wrapping text
auto wrap = [](std::string text, size_t line_length) -> std::string {
std::istringstream words(text);
std::ostringstream wrapped;
std::string word;
if (words >> word) {
wrapped << word;
size_t space_left = line_length - word.length();
while (words >> word) {
if (space_left < word.length() + 1) {
wrapped << '\n' << word;
space_left = line_length - word.length();
} else {
wrapped << ' ' << word;
space_left -= word.length() + 1;
}
}
}
return wrapped.str();
};
// get the unique categories
std::set<std::string> categories;
for (const auto& opt : this->options) {
const ConfigOptionDef& def = opt.second;
if (filter(def))
categories.insert(def.category);
}
for (auto category : categories) {
if (category != "") {
out << category << ":" << std::endl;
} else if (categories.size() > 1) {
out << "Misc options:" << std::endl;
}
for (const auto& opt : this->options) {
const ConfigOptionDef& def = opt.second;
if (def.category != category || def.cli == ConfigOptionDef::nocli || !filter(def))
continue;
// get all possible variations: --foo, --foobar, -f...
std::vector<std::string> cli_args = def.cli_args(opt.first);
if (cli_args.empty())
continue;
for (auto& arg : cli_args) {
arg.insert(0, (arg.size() == 1) ? "-" : "--");
if (def.type == coFloat || def.type == coInt || def.type == coFloatOrPercent
|| def.type == coFloats || def.type == coInts) {
arg += " N";
} else if (def.type == coPoint) {
arg += " X,Y";
} else if (def.type == coPoint3) {
arg += " X,Y,Z";
} else if (def.type == coString || def.type == coStrings) {
arg += " ABCD";
}
}
// left: command line options
const std::string cli = boost::algorithm::join(cli_args, ", ");
out << " " << std::left << std::setw(20) << cli;
// right: option description
std::string descr = def.tooltip;
if (show_defaults && def.default_value != nullptr && def.type != coBool
&& (def.type != coString || !def.default_value->serialize().empty())) {
descr += " (";
if (!def.sidetext.empty()) {
descr += def.sidetext + ", ";
} else if (!def.enum_values.empty()) {
descr += boost::algorithm::join(def.enum_values, ", ") + "; ";
}
descr += "default: " + def.default_value->serialize() + ")";
}
// wrap lines of description
descr = wrap(descr, 80);
std::vector<std::string> lines;
boost::split(lines, descr, boost::is_any_of("\n"));
// if command line options are too long, print description in new line
for (size_t i = 0; i < lines.size(); ++i) {
if (i == 0 && cli.size() > 19)
out << std::endl;
if (i > 0 || cli.size() > 19)
out << std::string(21, ' ');
out << lines[i] << std::endl;
}
}
}
return out;
}
void ConfigBase::apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent)
{
// loop through options and apply them
@ -508,50 +627,53 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre
// Let the parent decide what to do if the opt_key is not defined by this->def().
return nullptr;
ConfigOption *opt = nullptr;
switch (optdef->type) {
case coFloat: opt = new ConfigOptionFloat(); break;
case coFloats: opt = new ConfigOptionFloats(); break;
case coInt: opt = new ConfigOptionInt(); break;
case coInts: opt = new ConfigOptionInts(); break;
case coString: opt = new ConfigOptionString(); break;
case coStrings: opt = new ConfigOptionStrings(); break;
case coPercent: opt = new ConfigOptionPercent(); break;
case coPercents: opt = new ConfigOptionPercents(); break;
case coFloatOrPercent: opt = new ConfigOptionFloatOrPercent(); break;
case coPoint: opt = new ConfigOptionPoint(); break;
case coPoints: opt = new ConfigOptionPoints(); break;
case coBool: opt = new ConfigOptionBool(); break;
case coBools: opt = new ConfigOptionBools(); break;
case coEnum: opt = new ConfigOptionEnumGeneric(optdef->enum_keys_map); break;
default: throw std::runtime_error(std::string("Unknown option type for option ") + opt_key);
if (optdef->default_value != nullptr) {
opt = (optdef->default_value->type() == coEnum) ?
// Special case: For a DynamicConfig, convert a templated enum to a generic enum.
new ConfigOptionEnumGeneric(optdef->enum_keys_map, optdef->default_value->getInt()) :
optdef->default_value->clone();
} else {
switch (optdef->type) {
case coFloat: opt = new ConfigOptionFloat(); break;
case coFloats: opt = new ConfigOptionFloats(); break;
case coInt: opt = new ConfigOptionInt(); break;
case coInts: opt = new ConfigOptionInts(); break;
case coString: opt = new ConfigOptionString(); break;
case coStrings: opt = new ConfigOptionStrings(); break;
case coPercent: opt = new ConfigOptionPercent(); break;
case coPercents: opt = new ConfigOptionPercents(); break;
case coFloatOrPercent: opt = new ConfigOptionFloatOrPercent(); break;
case coPoint: opt = new ConfigOptionPoint(); break;
case coPoints: opt = new ConfigOptionPoints(); break;
case coPoint3: opt = new ConfigOptionPoint3(); break;
// case coPoint3s: opt = new ConfigOptionPoint3s(); break;
case coBool: opt = new ConfigOptionBool(); break;
case coBools: opt = new ConfigOptionBools(); break;
case coEnum: opt = new ConfigOptionEnumGeneric(optdef->enum_keys_map); break;
default: throw std::runtime_error(std::string("Unknown option type for option ") + opt_key);
}
}
this->options[opt_key] = opt;
return opt;
}
void DynamicConfig::read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra)
void DynamicConfig::read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra, t_config_option_keys* keys)
{
std::vector<char*> args;
// push a bogus executable name (argv[0])
args.emplace_back(const_cast<char*>(""));
for (size_t i = 0; i < tokens.size(); ++ i)
args.emplace_back(const_cast<char *>(tokens[i].c_str()));
this->read_cli(int(args.size()), &args[0], extra);
this->read_cli(int(args.size()), &args[0], extra, keys);
}
bool DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra)
bool DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra, t_config_option_keys* keys)
{
// cache the CLI option => opt_key mapping
std::map<std::string,std::string> opts;
for (const auto &oit : this->def()->options) {
std::string cli = oit.second.cli;
cli = cli.substr(0, cli.find("="));
boost::trim_right_if(cli, boost::is_any_of("!"));
std::vector<std::string> tokens;
boost::split(tokens, cli, boost::is_any_of("|"));
for (const std::string &t : tokens)
for (const auto &oit : this->def()->options)
for (auto t : oit.second.cli_args(oit.first))
opts[t] = oit.first;
}
bool parse_options = true;
for (int i = 1; i < argc; ++ i) {
@ -592,11 +714,8 @@ bool DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra)
// Look for the cli -> option mapping.
const auto it = opts.find(token);
if (it == opts.end()) {
printf("Warning: unknown option --%s\n", token.c_str());
// instead of continuing, return false to caller
// to stop execution and print usage
return false;
//continue;
boost::nowide::cerr << "Unknown option --" << token.c_str() << std::endl;
return false;
}
const t_config_option_key opt_key = it->second;
const ConfigOptionDef &optdef = this->def()->options.at(opt_key);
@ -604,13 +723,17 @@ bool DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra)
// look for it in the next token.
if (optdef.type != coBool && optdef.type != coBools && value.empty()) {
if (i == (argc-1)) {
printf("No value supplied for --%s\n", token.c_str());
continue;
boost::nowide::cerr << "No value supplied for --" << token.c_str() << std::endl;
return false;
}
value = argv[++ i];
}
// Store the option value.
const bool existing = this->has(opt_key);
if (keys != nullptr && !existing) {
// Save the order of detected keys.
keys->push_back(opt_key);
}
ConfigOption *opt_base = this->option(opt_key, true);
ConfigOptionVectorBase *opt_vector = opt_base->is_vector() ? static_cast<ConfigOptionVectorBase*>(opt_base) : nullptr;
if (opt_vector) {
@ -634,7 +757,10 @@ bool DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra)
static_cast<ConfigOptionString*>(opt_base)->value = value;
} else {
// Any scalar value of a type different from Bool and String.
this->set_deserialize(opt_key, value, false);
if (! this->set_deserialize(opt_key, value, false)) {
boost::nowide::cerr << "Invalid value supplied for --" << token.c_str() << std::endl;
return false;
}
}
}
return true;

View File

@ -6,6 +6,7 @@
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <stdexcept>
#include <string>
@ -27,6 +28,24 @@ extern std::string escape_strings_cstyle(const std::vector<std::string> &strs);
extern bool unescape_string_cstyle(const std::string &str, std::string &out);
extern bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &out);
/// Specialization of std::exception to indicate that an unknown config option has been encountered.
class UnknownOptionException : public std::runtime_error {
public:
UnknownOptionException() :
std::runtime_error("Unknown option exception") {}
UnknownOptionException(const std::string &opt_key) :
std::runtime_error(std::string("Unknown option exception: ") + opt_key) {}
};
/// Indicate that the ConfigBase derived class does not provide config definition (the method def() returns null).
class NoDefinitionException : public std::runtime_error
{
public:
NoDefinitionException() :
std::runtime_error("No definition exception") {}
NoDefinitionException(const std::string &opt_key) :
std::runtime_error(std::string("No definition exception: ") + opt_key) {}
};
// Type of a configuration value.
enum ConfigOptionType {
@ -54,12 +73,14 @@ enum ConfigOptionType {
coPoint = 6,
// vector of 2d points (Point2f). Currently used for the definition of the print bed and for the extruder offsets.
coPoints = coPoint + coVectorType,
coPoint3 = 7,
// coPoint3s = coPoint3 + coVectorType,
// single boolean value
coBool = 7,
coBool = 8,
// vector of boolean values
coBools = coBool + coVectorType,
// a generic enum
coEnum = 8,
coEnum = 9,
};
enum ConfigOptionMode {
@ -68,6 +89,18 @@ enum ConfigOptionMode {
comExpert
};
enum PrinterTechnology
{
// Fused Filament Fabrication
ptFFF,
// Stereolitography
ptSLA,
// Unknown, useful for command line processing
ptUnknown,
// Any technology, useful for parameters compatible with both ptFFF and ptSLA
ptAny
};
// A generic value of a configuration option.
class ConfigOption {
public:
@ -718,6 +751,39 @@ public:
}
};
class ConfigOptionPoint3 : public ConfigOptionSingle<Vec3d>
{
public:
ConfigOptionPoint3() : ConfigOptionSingle<Vec3d>(Vec3d(0,0,0)) {}
explicit ConfigOptionPoint3(const Vec3d &value) : ConfigOptionSingle<Vec3d>(value) {}
static ConfigOptionType static_type() { return coPoint3; }
ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionPoint3(*this); }
ConfigOptionPoint3& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionPoint3 &rhs) const { return this->value == rhs.value; }
std::string serialize() const override
{
std::ostringstream ss;
ss << this->value(0);
ss << ",";
ss << this->value(1);
ss << ",";
ss << this->value(2);
return ss.str();
}
bool deserialize(const std::string &str, bool append = false) override
{
UNUSED(append);
char dummy;
return sscanf(str.data(), " %lf , %lf , %lf %c", &this->value(0), &this->value(1), &this->value(2), &dummy) == 2 ||
sscanf(str.data(), " %lf x %lf x %lf %c", &this->value(0), &this->value(1), &this->value(2), &dummy) == 2;
}
};
class ConfigOptionBool : public ConfigOptionSingle<bool>
{
public:
@ -893,6 +959,7 @@ class ConfigOptionEnumGeneric : public ConfigOptionInt
{
public:
ConfigOptionEnumGeneric(const t_config_enum_values* keys_map = nullptr) : keys_map(keys_map) {}
explicit ConfigOptionEnumGeneric(const t_config_enum_values* keys_map, int value) : ConfigOptionInt(value), keys_map(keys_map) {}
const t_config_enum_values* keys_map;
@ -960,6 +1027,8 @@ public:
// The full label is shown, when adding an override parameter for an object or a modified object.
std::string label;
std::string full_label;
// With which printer technology is this configuration valid?
PrinterTechnology printer_technology = ptUnknown;
// Category of a configuration field, from the GUI perspective.
// One of: "Layers and Perimeters", "Infill", "Support material", "Speed", "Extruders", "Advanced", "Extrusion Width"
std::string category;
@ -1010,6 +1079,13 @@ public:
return true;
return false;
}
// Returns the alternative CLI arguments for the given option.
// If there are no cli arguments defined, use the key and replace underscores with dashes.
std::vector<std::string> cli_args(const std::string &key) const;
// Assign this key to cli to disable CLI for this option.
static std::string nocli;
};
// Map from a config option name to its definition.
@ -1044,6 +1120,11 @@ public:
return out;
}
/// Iterate through all of the CLI options and write them to a stream.
std::ostream& print_cli_help(
std::ostream& out, bool show_defaults,
std::function<bool(const ConfigOptionDef &)> filter = [](const ConfigOptionDef &){ return true; }) const;
protected:
ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type) {
ConfigOptionDef* opt = &this->options[opt_key];
@ -1089,12 +1170,24 @@ public:
TYPE* option(const t_config_option_key &opt_key, bool create = false)
{
ConfigOption *opt = this->optptr(opt_key, create);
// assert(opt == nullptr || opt->type() == TYPE::static_type());
return (opt == nullptr || opt->type() != TYPE::static_type()) ? nullptr : static_cast<TYPE*>(opt);
}
template<typename TYPE>
const TYPE* option(const t_config_option_key &opt_key) const
{ return const_cast<ConfigBase*>(this)->option<TYPE>(opt_key, false); }
template<typename TYPE>
TYPE* option_throw(const t_config_option_key &opt_key, bool create = false)
{
ConfigOption *opt = this->optptr(opt_key, create);
if (opt == nullptr)
throw UnknownOptionException(opt_key);
if (opt->type() != TYPE::static_type())
throw std::runtime_error("Conversion to a wrong type");
return static_cast<TYPE*>(opt);
}
template<typename TYPE>
const TYPE* option_throw(const t_config_option_key &opt_key) const
{ return const_cast<ConfigBase*>(this)->option_throw<TYPE>(opt_key, false); }
// Apply all keys of other ConfigBase defined by this->def() to this ConfigBase.
// An UnknownOptionException is thrown in case some option keys of other are not defined by this->def(),
// or this ConfigBase is of a StaticConfig type and it does not support some of the keys, and ignore_nonexistent is not set.
@ -1276,8 +1369,8 @@ public:
bool opt_bool(const t_config_option_key &opt_key, unsigned int idx) const { return this->option<ConfigOptionBools>(opt_key)->get_at(idx) != 0; }
// Command line processing
void read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra);
bool read_cli(int argc, char** argv, t_config_option_keys* extra);
void read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra, t_config_option_keys* keys = nullptr);
bool read_cli(int argc, char** argv, t_config_option_keys* extra, t_config_option_keys* keys = nullptr);
typedef std::map<t_config_option_key,ConfigOption*> t_options_map;
t_options_map::const_iterator cbegin() const { return options.cbegin(); }
@ -1303,25 +1396,6 @@ protected:
void set_defaults();
};
/// Specialization of std::exception to indicate that an unknown config option has been encountered.
class UnknownOptionException : public std::runtime_error {
public:
UnknownOptionException() :
std::runtime_error("Unknown option exception") {}
UnknownOptionException(const std::string &opt_key) :
std::runtime_error(std::string("Unknown option exception: ") + opt_key) {}
};
/// Indicate that the ConfigBase derived class does not provide config definition (the method def() returns null).
class NoDefinitionException : public std::runtime_error
{
public:
NoDefinitionException() :
std::runtime_error("No definition exception") {}
NoDefinitionException(const std::string &opt_key) :
std::runtime_error(std::string("No definition exception: ") + opt_key) {}
};
}
#endif

View File

@ -15,7 +15,7 @@ void FillPlanePath::_fill_surface_single(
{
expolygon.rotate(- direction.first);
coord_t distance_between_lines = scale_(this->spacing) / params.density;
coord_t distance_between_lines = coord_t(scale_(this->spacing) / params.density);
// align infill across layers using the object's bounding box
// Rotated bounding box of the whole object.
@ -89,7 +89,8 @@ Pointfs FillArchimedeanChords::_generate(coord_t min_x, coord_t min_y, coord_t m
out.push_back(Vec2d(0, 0));
out.push_back(Vec2d(1, 0));
while (r < rmax) {
theta += 1. / r;
// Discretization angle to achieve a discretization error lower than RESOLUTION.
theta += 2. * acos(1. - RESOLUTION / r);
r = a + b * theta;
out.push_back(Vec2d(r * cos(theta), r * sin(theta)));
}

View File

@ -587,8 +587,10 @@ namespace Slic3r {
object.second->layer_height_profile = obj_layer_heights_profile->second;
IdToSlaSupportPointsMap::iterator obj_sla_support_points = m_sla_support_points.find(object.first);
if (obj_sla_support_points != m_sla_support_points.end() && !obj_sla_support_points->second.empty())
if (obj_sla_support_points != m_sla_support_points.end() && !obj_sla_support_points->second.empty()) {
object.second->sla_support_points = obj_sla_support_points->second;
object.second->sla_points_status = sla::PointsStatus::UserModified;
}
IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first);
if (obj_metadata != m_objects_metadata.end())

View File

@ -600,6 +600,7 @@ void AMFParserContext::endElement(const char * /* name */)
break;
p = end + 1;
}
m_object->sla_points_status = sla::PointsStatus::UserModified;
}
else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume) {
if (strcmp(opt_key, "modifier") == 0) {

View File

@ -115,4 +115,23 @@ bool load_obj(const char *path, Model *model, const char *object_name_in)
return true;
}
bool store_obj(const char *path, TriangleMesh *mesh)
{
//FIXME returning false even if write failed.
mesh->WriteOBJFile(path);
return true;
}
bool store_obj(const char *path, ModelObject *model_object)
{
TriangleMesh mesh = model_object->mesh();
return store_obj(path, &mesh);
}
bool store_obj(const char *path, Model *model)
{
TriangleMesh mesh = model->mesh();
return store_obj(path, &mesh);
}
}; // namespace Slic3r

View File

@ -9,6 +9,10 @@ class Model;
// Load an OBJ file into a provided model.
extern bool load_obj(const char *path, Model *model, const char *object_name = nullptr);
extern bool store_obj(const char *path, TriangleMesh *mesh);
extern bool store_obj(const char *path, ModelObject *model);
extern bool store_obj(const char *path, Model *model);
}; // namespace Slic3r
#endif /* slic3r_Format_OBJ_hpp_ */

View File

@ -55,4 +55,10 @@ bool store_stl(const char *path, ModelObject *model_object, bool binary)
return store_stl(path, &mesh, binary);
}
bool store_stl(const char *path, Model *model, bool binary)
{
TriangleMesh mesh = model->mesh();
return store_stl(path, &mesh, binary);
}
}; // namespace Slic3r

View File

@ -11,6 +11,7 @@ extern bool load_stl(const char *path, Model *model, const char *object_name = n
extern bool store_stl(const char *path, TriangleMesh *mesh, bool binary);
extern bool store_stl(const char *path, ModelObject *model_object, bool binary);
extern bool store_stl(const char *path, Model *model, bool binary);
}; // namespace Slic3r

View File

@ -487,7 +487,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
// starts analyzer calculations
if (m_enable_analyzer) {
BOOST_LOG_TRIVIAL(debug) << "Preparing G-code preview data";
m_analyzer.calc_gcode_preview_data(*preview_data);
m_analyzer.calc_gcode_preview_data(*preview_data, [print]() { print->throw_if_canceled(); });
m_analyzer.reset();
}

View File

@ -137,22 +137,22 @@ const std::string& GCodeAnalyzer::process_gcode(const std::string& gcode)
return m_process_output;
}
void GCodeAnalyzer::calc_gcode_preview_data(GCodePreviewData& preview_data)
void GCodeAnalyzer::calc_gcode_preview_data(GCodePreviewData& preview_data, std::function<void()> cancel_callback)
{
// resets preview data
preview_data.reset();
// calculates extrusion layers
_calc_gcode_preview_extrusion_layers(preview_data);
_calc_gcode_preview_extrusion_layers(preview_data, cancel_callback);
// calculates travel
_calc_gcode_preview_travel(preview_data);
_calc_gcode_preview_travel(preview_data, cancel_callback);
// calculates retractions
_calc_gcode_preview_retractions(preview_data);
_calc_gcode_preview_retractions(preview_data, cancel_callback);
// calculates unretractions
_calc_gcode_preview_unretractions(preview_data);
_calc_gcode_preview_unretractions(preview_data, cancel_callback);
}
bool GCodeAnalyzer::is_valid_extrusion_role(ExtrusionRole role)
@ -676,7 +676,7 @@ bool GCodeAnalyzer::_is_valid_extrusion_role(int value) const
return ((int)erNone <= value) && (value <= (int)erMixed);
}
void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& preview_data)
void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& preview_data, std::function<void()> cancel_callback)
{
struct Helper
{
@ -725,9 +725,18 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
GCodePreviewData::Range feedrate_range;
GCodePreviewData::Range volumetric_rate_range;
// to avoid to call the callback too often
unsigned int cancel_callback_threshold = (unsigned int)std::max((int)extrude_moves->second.size() / 25, 1);
unsigned int cancel_callback_curr = 0;
// constructs the polylines while traversing the moves
for (const GCodeMove& move : extrude_moves->second)
{
// to avoid to call the callback too often
cancel_callback_curr = (cancel_callback_curr + 1) % cancel_callback_threshold;
if (cancel_callback_curr == 0)
cancel_callback();
if ((data != move.data) || (z != move.start_position.z()) || (position != move.start_position) || (volumetric_rate != move.data.feedrate * (float)move.data.mm3_per_mm))
{
// store current polyline
@ -769,7 +778,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
preview_data.ranges.volumetric_rate.update_from(volumetric_rate_range);
}
void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data)
void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data, std::function<void()> cancel_callback)
{
struct Helper
{
@ -797,9 +806,17 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data)
GCodePreviewData::Range width_range;
GCodePreviewData::Range feedrate_range;
// to avoid to call the callback too often
unsigned int cancel_callback_threshold = (unsigned int)std::max((int)travel_moves->second.size() / 25, 1);
unsigned int cancel_callback_curr = 0;
// constructs the polylines while traversing the moves
for (const GCodeMove& move : travel_moves->second)
{
cancel_callback_curr = (cancel_callback_curr + 1) % cancel_callback_threshold;
if (cancel_callback_curr == 0)
cancel_callback();
GCodePreviewData::Travel::EType move_type = (move.delta_extruder < 0.0f) ? GCodePreviewData::Travel::Retract : ((move.delta_extruder > 0.0f) ? GCodePreviewData::Travel::Extrude : GCodePreviewData::Travel::Move);
GCodePreviewData::Travel::Polyline::EDirection move_direction = ((move.start_position.x() != move.end_position.x()) || (move.start_position.y() != move.end_position.y())) ? GCodePreviewData::Travel::Polyline::Generic : GCodePreviewData::Travel::Polyline::Vertical;
@ -840,28 +857,44 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data)
preview_data.ranges.feedrate.update_from(feedrate_range);
}
void GCodeAnalyzer::_calc_gcode_preview_retractions(GCodePreviewData& preview_data)
void GCodeAnalyzer::_calc_gcode_preview_retractions(GCodePreviewData& preview_data, std::function<void()> cancel_callback)
{
TypeToMovesMap::iterator retraction_moves = m_moves_map.find(GCodeMove::Retract);
if (retraction_moves == m_moves_map.end())
return;
// to avoid to call the callback too often
unsigned int cancel_callback_threshold = (unsigned int)std::max((int)retraction_moves->second.size() / 25, 1);
unsigned int cancel_callback_curr = 0;
for (const GCodeMove& move : retraction_moves->second)
{
cancel_callback_curr = (cancel_callback_curr + 1) % cancel_callback_threshold;
if (cancel_callback_curr == 0)
cancel_callback();
// store position
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);
}
}
void GCodeAnalyzer::_calc_gcode_preview_unretractions(GCodePreviewData& preview_data)
void GCodeAnalyzer::_calc_gcode_preview_unretractions(GCodePreviewData& preview_data, std::function<void()> cancel_callback)
{
TypeToMovesMap::iterator unretraction_moves = m_moves_map.find(GCodeMove::Unretract);
if (unretraction_moves == m_moves_map.end())
return;
// to avoid to call the callback too often
unsigned int cancel_callback_threshold = (unsigned int)std::max((int)unretraction_moves->second.size() / 25, 1);
unsigned int cancel_callback_curr = 0;
for (const GCodeMove& move : unretraction_moves->second)
{
cancel_callback_curr = (cancel_callback_curr + 1) % cancel_callback_threshold;
if (cancel_callback_curr == 0)
cancel_callback();
// store position
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);

View File

@ -122,7 +122,8 @@ public:
const std::string& process_gcode(const std::string& gcode);
// Calculates all data needed for gcode visualization
void calc_gcode_preview_data(GCodePreviewData& preview_data);
// throws CanceledException through print->throw_if_canceled() (sent by the caller as callback).
void calc_gcode_preview_data(GCodePreviewData& preview_data, std::function<void()> cancel_callback = std::function<void()>());
// Return an estimate of the memory consumed by the time estimator.
size_t memory_used() const;
@ -237,10 +238,11 @@ private:
// Checks if the given int is a valid extrusion role (contained into enum ExtrusionRole)
bool _is_valid_extrusion_role(int value) const;
void _calc_gcode_preview_extrusion_layers(GCodePreviewData& preview_data);
void _calc_gcode_preview_travel(GCodePreviewData& preview_data);
void _calc_gcode_preview_retractions(GCodePreviewData& preview_data);
void _calc_gcode_preview_unretractions(GCodePreviewData& preview_data);
// All the following methods throw CanceledException through print->throw_if_canceled() (sent by the caller as callback).
void _calc_gcode_preview_extrusion_layers(GCodePreviewData& preview_data, std::function<void()> cancel_callback);
void _calc_gcode_preview_travel(GCodePreviewData& preview_data, std::function<void()> cancel_callback);
void _calc_gcode_preview_retractions(GCodePreviewData& preview_data, std::function<void()> cancel_callback);
void _calc_gcode_preview_unretractions(GCodePreviewData& preview_data, std::function<void()> cancel_callback);
};
} // namespace Slic3r

View File

@ -2,6 +2,7 @@
#include <boost/algorithm/string.hpp>
#include <boost/log/trivial.hpp>
#include <boost/format.hpp>
#include <boost/filesystem.hpp>
#ifdef WIN32
@ -88,7 +89,7 @@ static DWORD execute_process_winapi(const std::wstring &command_line)
// Run the script. If it is a perl script, run it through the bundled perl interpreter.
// If it is a batch file, run it through the cmd.exe.
// Otherwise run it directly.
static int run_script_win32(const std::string &script, const std::string &gcode)
static int run_script(const std::string &script, const std::string &gcode, std::string &/*std_err*/)
{
// Unpack the argument list provided by the user.
int nArgs;
@ -132,9 +133,46 @@ static int run_script_win32(const std::string &script, const std::string &gcode)
}
#else
#include <sys/stat.h> //for getting filesystem UID/GID
#include <unistd.h> //for getting current UID/GID
#include <boost/process.hpp>
// POSIX
#include <cstdlib> // getenv()
#include <sstream>
#include <boost/process.hpp>
namespace process = boost::process;
static int run_script(const std::string &script, const std::string &gcode, std::string &std_err)
{
// Try to obtain user's default shell
const char *shell = ::getenv("SHELL");
if (shell == nullptr) { shell = "sh"; }
// Quote and escape the gcode path argument
std::string command { script };
command.append(" '");
for (char c : gcode) {
if (c == '\'') { command.append("'\\''"); }
else { command.push_back(c); }
}
command.push_back('\'');
BOOST_LOG_TRIVIAL(debug) << boost::format("Executing script, shell: %1%, command: %2%") % shell % command;
process::ipstream istd_err;
process::child child(shell, "-c", command, process::std_err > istd_err);
std_err.clear();
std::string line;
while (child.running() && std::getline(istd_err, line)) {
std_err.append(line);
std_err.push_back('\n');
}
child.wait();
return child.exit_code();
}
#endif
namespace Slic3r {
@ -158,27 +196,15 @@ void run_post_process_scripts(const std::string &path, const PrintConfig &config
if (script.empty())
continue;
BOOST_LOG_TRIVIAL(info) << "Executing script " << script << " on file " << path;
#ifdef WIN32
int result = run_script_win32(script, gcode_file.string());
#else
//FIXME testing existence of a script is risky, as the script line may contain the script and some additional command line parameters.
// We would have to process the script line into parameters before testing for the existence of the command, the command may be looked up
// in the PATH etc.
if (! boost::filesystem::exists(boost::filesystem::path(script)))
throw std::runtime_error(std::string("The configured post-processing script does not exist: ") + script);
struct stat info;
if (stat(script.c_str(), &info))
throw std::runtime_error(std::string("Cannot read information for post-processing script: ") + script);
boost::filesystem::perms script_perms = boost::filesystem::status(script).permissions();
//if UID matches, check UID perm. else if GID matches, check GID perm. Otherwise check other perm.
if (!(script_perms & ((info.st_uid == geteuid()) ? boost::filesystem::perms::owner_exe
: ((info.st_gid == getegid()) ? boost::filesystem::perms::group_exe
: boost::filesystem::perms::others_exe))))
throw std::runtime_error(std::string("The configured post-processing script is not executable: check permissions. ") + script);
int result = boost::process::system(script, gcode_file);
if (result < 0)
BOOST_LOG_TRIVIAL(error) << "Script " << script << " on file " << path << " failed. Negative error code returned.";
#endif
std::string std_err;
const int result = run_script(script, gcode_file.string(), std_err);
if (result != 0) {
const std::string msg = std_err.empty() ? (boost::format("Post-processing script %1% on file %2% failed.\nError code: %3%") % script % path % result).str()
: (boost::format("Post-processing script %1% on file %2% failed.\nError code: %3%\nOutput:\n%4%") % script % path % result % std_err).str();
BOOST_LOG_TRIVIAL(error) << msg;
throw std::runtime_error(msg);
}
}
}
}

View File

@ -709,12 +709,11 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo
// The tool is supposed to be active and primed at the time when the wipe tower brim is extruded.
// Extrude 4 rounds of a brim around the future wipe tower.
box_coordinates box(wipeTower_box);
box.expand(m_perimeter_width);
for (size_t i = 0; i < 4; ++ i) {
box.expand(m_perimeter_width - m_layer_height*(1.f-M_PI_4)); // the brim shall have 'normal' spacing with no extra void space
writer.travel (box.ld, 7000)
.extrude(box.lu, 2100).extrude(box.ru)
.extrude(box.rd ).extrude(box.ld);
box.expand(m_perimeter_width);
}
writer.travel(wipeTower_box.ld, 7000); // Move to the front left corner.

View File

@ -577,6 +577,11 @@ end:
return input_file;
}
std::string Model::propose_export_file_name_and_path(const std::string &new_extension) const
{
return boost::filesystem::path(this->propose_export_file_name_and_path()).replace_extension(new_extension).string();
}
ModelObject::~ModelObject()
{
this->clear_volumes();
@ -1456,6 +1461,15 @@ int ModelVolume::extruder_id() const
return extruder_id;
}
bool ModelVolume::is_splittable() const
{
// the call mesh.has_multiple_patches() is expensive, so cache the value to calculate it only once
if (m_is_splittable == -1)
m_is_splittable = (int)mesh.has_multiple_patches();
return m_is_splittable == 1;
}
void ModelVolume::center_geometry()
{
#if ENABLE_VOLUMES_CENTERING_FIXES
@ -1568,6 +1582,22 @@ void ModelVolume::scale(const Vec3d& scaling_factors)
set_scaling_factor(get_scaling_factor().cwiseProduct(scaling_factors));
}
void ModelObject::scale_to_fit(const Vec3d &size)
{
/*
BoundingBoxf3 instance_bounding_box(size_t instance_idx, bool dont_translate = false) const;
Vec3d orig_size = this->bounding_box().size();
float factor = fminf(
size.x / orig_size.x,
fminf(
size.y / orig_size.y,
size.z / orig_size.z
)
);
this->scale(factor);
*/
}
void ModelVolume::rotate(double angle, Axis axis)
{
switch (axis)

View File

@ -245,6 +245,10 @@ public:
void scale(const Vec3d &versor);
void scale(const double s) { this->scale(Vec3d(s, s, s)); }
void scale(double x, double y, double z) { this->scale(Vec3d(x, y, z)); }
/// Scale the current ModelObject to fit by altering the scaling factor of ModelInstances.
/// It operates on the total size by duplicating the object according to all the instances.
/// \param size Sizef3 the size vector
void scale_to_fit(const Vec3d &size);
void rotate(double angle, Axis axis);
void rotate(double angle, const Vec3d& axis);
void mirror(Axis axis);
@ -336,8 +340,7 @@ public:
// Extruder ID is only valid for FFF. Returns -1 for SLA or if the extruder ID is not applicable (support volumes).
int extruder_id() const;
void set_splittable(const int val) { m_is_splittable = val; }
int is_splittable() const { return m_is_splittable; }
bool is_splittable() const;
// Split this volume, append the result to the object owning this volume.
// Return the number of volumes created from this one.
@ -417,7 +420,7 @@ private:
// -1 -> is unknown value (before first cheking)
// 0 -> is not splittable
// 1 -> is splittable
int m_is_splittable {-1};
mutable int m_is_splittable{ -1 };
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(ModelVolumeType::MODEL_PART), object(object)
{
@ -619,6 +622,8 @@ public:
// Propose an output file name & path based on the first printable object's name and source input file's path.
std::string propose_export_file_name_and_path() const;
// Propose an output path, replace extension. The new_extension shall contain the initial dot.
std::string propose_export_file_name_and_path(const std::string &new_extension) const;
private:
MODELBASE_DERIVED_PRIVATE_COPY_MOVE(Model)

View File

@ -224,7 +224,7 @@ public:
const ValueType &value = it->second;
const Vec2crd *pt2 = m_point_accessor(value);
if (pt2 != nullptr) {
const double d2 = (pt - *pt2).squaredNorm();
const double d2 = (pt - *pt2).cast<double>().squaredNorm();
if (d2 < dist_min) {
dist_min = d2;
value_min = &value;

View File

@ -661,6 +661,9 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
// Make a copy of the config, normalize it.
DynamicPrintConfig config(config_in);
config.option("print_settings_id", true);
config.option("filament_settings_id", true);
config.option("printer_settings_id", true);
config.normalize();
// Collect changes to print config.
t_config_option_keys print_diff = m_config.diff(config);
@ -688,9 +691,9 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
PlaceholderParser &pp = this->placeholder_parser();
pp.apply_only(config, placeholder_parser_diff);
// Set the profile aliases for the PrintBase::output_filename()
pp.set("print_preset", config_in.option("print_settings_id" )->clone());
pp.set("filament_preset", config_in.option("filament_settings_id")->clone());
pp.set("printer_preset", config_in.option("printer_settings_id" )->clone());
pp.set("print_preset", config.option("print_settings_id")->clone());
pp.set("filament_preset", config.option("filament_settings_id")->clone());
pp.set("printer_preset", config.option("printer_settings_id")->clone());
}
// It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
@ -1509,7 +1512,7 @@ void Print::process()
// The export_gcode may die for various reasons (fails to process output_filename_format,
// write error into the G-code, cannot execute post-processing scripts).
// It is up to the caller to show an error message.
void Print::export_gcode(const std::string &path_template, GCodePreviewData *preview_data)
std::string Print::export_gcode(const std::string &path_template, GCodePreviewData *preview_data)
{
// output everything to a G-code file
// The following call may die if the output_filename_format template substitution fails.
@ -1525,6 +1528,7 @@ void Print::export_gcode(const std::string &path_template, GCodePreviewData *pre
// The following line may die for multiple reasons.
GCode gcode;
gcode.do_export(this, path.c_str(), preview_data);
return path.c_str();
}
void Print::_make_skirt()
@ -1693,8 +1697,10 @@ void Print::_make_brim()
}
polygons_append(loops, offset(islands, -0.5f * float(flow.scaled_spacing())));
}
loops = union_pt_chained(loops, false);
// The function above produces ordering well suited for concentric infill (from outside to inside).
// For Brim, the ordering should be reversed (from inside to outside).
std::reverse(loops.begin(), loops.end());
extrusion_entities_append_loops(m_brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height()));
}

View File

@ -297,7 +297,9 @@ public:
bool apply_config(DynamicPrintConfig config);
void process() override;
void export_gcode(const std::string &path_template, GCodePreviewData *preview_data);
// Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file.
// If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r).
std::string export_gcode(const std::string &path_template, GCodePreviewData *preview_data);
// methods for handling state
bool is_step_done(PrintStep step) const { return Inherited::is_step_done(step); }

View File

@ -272,6 +272,7 @@ public:
NO_RELOAD_SCENE = 0,
RELOAD_SCENE = 1 << 1,
RELOAD_SLA_SUPPORT_POINTS = 1 << 2,
RELOAD_SLA_PREVIEW = 1 << 3,
};
// Bitmap of FlagBits
unsigned int flags;

File diff suppressed because it is too large Load Diff

View File

@ -24,14 +24,6 @@
namespace Slic3r {
enum PrinterTechnology
{
// Fused Filament Fabrication
ptFFF,
// Stereolitography
ptSLA,
};
enum GCodeFlavor {
gcfRepRap, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlin, gcfSailfish, gcfMach3, gcfMachinekit,
gcfSmoothie, gcfNoExtrusion,
@ -806,12 +798,6 @@ public:
ConfigOptionFloats wiping_volumes_matrix;
ConfigOptionFloats wiping_volumes_extruders;
ConfigOptionFloat z_offset;
ConfigOptionFloat bed_size_x;
ConfigOptionFloat bed_size_y;
ConfigOptionInt pixel_width;
ConfigOptionInt pixel_height;
ConfigOptionFloat exp_time;
ConfigOptionFloat exp_time_first;
protected:
PrintConfig(int) : MachineEnvelopeConfig(1), GCodeConfig(1) {}
@ -884,12 +870,6 @@ protected:
OPT_PTR(wiping_volumes_matrix);
OPT_PTR(wiping_volumes_extruders);
OPT_PTR(z_offset);
OPT_PTR(bed_size_x);
OPT_PTR(bed_size_y);
OPT_PTR(pixel_width);
OPT_PTR(pixel_height);
OPT_PTR(exp_time);
OPT_PTR(exp_time_first);
}
};
@ -1006,6 +986,9 @@ public:
// The max length of a bridge in mm
ConfigOptionFloat support_max_bridge_length /*= 15.0*/;
// The max distance of two pillars to get cross linked.
ConfigOptionFloat support_max_pillar_link_distance;
// The elevation in Z direction upwards. This is the space between the pad
// and the model object's bounding box bottom. Units in mm.
ConfigOptionFloat support_object_elevation /*= 5.0*/;
@ -1053,6 +1036,7 @@ protected:
OPT_PTR(support_base_height);
OPT_PTR(support_critical_angle);
OPT_PTR(support_max_bridge_length);
OPT_PTR(support_max_pillar_link_distance);
OPT_PTR(support_points_density_relative);
OPT_PTR(support_points_minimal_distance);
OPT_PTR(support_object_elevation);
@ -1145,72 +1129,32 @@ protected:
#undef STATIC_PRINT_CONFIG_CACHE_DERIVED
#undef OPT_PTR
class CLIConfigDef : public ConfigDef
class CLIActionsConfigDef : public ConfigDef
{
public:
CLIConfigDef();
CLIActionsConfigDef();
};
extern const CLIConfigDef cli_config_def;
#define OPT_PTR(KEY) if (opt_key == #KEY) return &this->KEY
class CLIConfig : public virtual ConfigBase, public StaticConfig
class CLITransformConfigDef : public ConfigDef
{
public:
ConfigOptionFloat cut;
ConfigOptionString datadir;
ConfigOptionBool dont_arrange;
ConfigOptionBool export_3mf;
ConfigOptionBool gui;
ConfigOptionBool info;
ConfigOptionBool help;
ConfigOptionStrings load;
ConfigOptionBool no_gui;
ConfigOptionString output;
ConfigOptionPoint print_center;
ConfigOptionFloat rotate;
ConfigOptionFloat rotate_x;
ConfigOptionFloat rotate_y;
ConfigOptionString save;
ConfigOptionFloat scale;
// ConfigOptionPoint3 scale_to_fit;
ConfigOptionBool slice;
CLIConfig() : ConfigBase(), StaticConfig()
{
this->set_defaults();
};
// Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here.
const ConfigDef* def() const override { return &cli_config_def; }
t_config_option_keys keys() const override { return cli_config_def.keys(); }
ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override
{
OPT_PTR(cut);
OPT_PTR(datadir);
OPT_PTR(dont_arrange);
OPT_PTR(export_3mf);
OPT_PTR(gui);
OPT_PTR(help);
OPT_PTR(info);
OPT_PTR(load);
OPT_PTR(no_gui);
OPT_PTR(output);
OPT_PTR(print_center);
OPT_PTR(rotate);
OPT_PTR(rotate_x);
OPT_PTR(rotate_y);
OPT_PTR(save);
OPT_PTR(scale);
// OPT_PTR(scale_to_fit);
OPT_PTR(slice);
return NULL;
}
CLITransformConfigDef();
};
#undef OPT_PTR
class CLIMiscConfigDef : public ConfigDef
{
public:
CLIMiscConfigDef();
};
// This class defines the command line options representing actions.
extern const CLIActionsConfigDef cli_actions_config_def;
// This class defines the command line options representing transforms.
extern const CLITransformConfigDef cli_transform_config_def;
// This class defines all command line options that are not actions or transforms.
extern const CLIMiscConfigDef cli_misc_config_def;
class DynamicPrintAndCLIConfig : public DynamicPrintConfig
{
@ -1233,19 +1177,16 @@ private:
public:
PrintAndCLIConfigDef() {
this->options.insert(print_config_def.options.begin(), print_config_def.options.end());
this->options.insert(cli_config_def.options.begin(), cli_config_def.options.end());
this->options.insert(cli_actions_config_def.options.begin(), cli_actions_config_def.options.end());
this->options.insert(cli_transform_config_def.options.begin(), cli_transform_config_def.options.end());
this->options.insert(cli_misc_config_def.options.begin(), cli_misc_config_def.options.end());
}
// Do not release the default values, they are handled by print_config_def & cli_config_def.
// Do not release the default values, they are handled by print_config_def & cli_actions_config_def / cli_transform_config_def / cli_misc_config_def.
~PrintAndCLIConfigDef() { this->options.clear(); }
};
static PrintAndCLIConfigDef s_def;
};
/// Iterate through all of the print options and write them to a stream.
std::ostream& print_print_options(std::ostream& out);
/// Iterate through all of the CLI options and write them to a stream.
std::ostream& print_cli_options(std::ostream& out);
} // namespace Slic3r
#endif

View File

@ -496,7 +496,7 @@ void SLAAutoSupports::uniformly_cover(const ExPolygons& islands, Structure& stru
poisson_samples.erase(poisson_samples.begin() + poisson_samples_target, poisson_samples.end());
}
for (const Vec2f &pt : poisson_samples) {
m_output.emplace_back(float(pt(0)), float(pt(1)), structure.height, 0.2f, is_new_island);
m_output.emplace_back(float(pt(0)), float(pt(1)), structure.height, m_config.head_diameter/2.f, is_new_island);
structure.supports_force_this_layer += m_config.support_force();
grid3d.insert(pt, &structure);
}

View File

@ -17,6 +17,7 @@ public:
struct Config {
float density_relative;
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 tear_pressure() const { return 1.f; } // pressure that the display exerts (the force unit per mm2)

View File

@ -601,14 +601,16 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
const double thickness = cfg.min_wall_thickness_mm;
const double wingheight = cfg.min_wall_height_mm;
const double fullheight = wingheight + thickness;
const double slope = cfg.wall_slope;
const double slope = cfg.wall_slope;
const double wingdist = wingheight / std::tan(slope);
const double bottom_offs = (thickness + wingheight) / std::tan(slope);
// scaled values
const coord_t s_thickness = mm(thickness);
const coord_t s_eradius = mm(cfg.edge_radius_mm);
const coord_t s_safety_dist = 2*s_eradius + coord_t(0.8*s_thickness);
const coord_t s_wingdist = mm(wingdist);
const coord_t s_bottom_offs = mm(bottom_offs);
auto& thrcl = cfg.throw_on_cancel;
@ -620,7 +622,7 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
// Get rid of any holes in the concave hull output.
concaveh.holes.clear();
// Here lies the trick that does the smooting only with clipper offset
// Here lies the trick that does the smoothing only with clipper offset
// calls. The offset is configured to round edges. Inner edges will
// be rounded because we offset twice: ones to get the outer (top) plate
// and again to get the inner (bottom) plate
@ -628,10 +630,9 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
outer_base.holes.clear();
offset(outer_base, s_safety_dist + s_wingdist + s_thickness);
ExPolygon bottom_poly = outer_base;
bottom_poly.holes.clear();
if(s_wingdist > 0) offset(bottom_poly, -s_wingdist);
offset(bottom_poly, -s_bottom_offs);
// Punching a hole in the top plate for the cavity
ExPolygon top_poly;
@ -692,7 +693,7 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
// Now that we have the rounded edge connecting the top plate with
// the outer side walls, we can generate and merge the sidewall geometry
pool.merge(walls(ob.contour, bottom_poly.contour, wh, -fullheight,
wingdist, thrcl));
bottom_offs, thrcl));
if(wingheight > 0) {
// Generate the smoothed edge geometry

View File

@ -2,6 +2,7 @@
#define SLACOMMON_HPP
#include <Eigen/Geometry>
#include <memory>
// #define SLIC3R_SLA_NEEDS_WINDTREE
@ -44,7 +45,6 @@ struct SupportPoint {
bool operator!=(const SupportPoint& sp) const { return !(sp == (*this)); }
};
/// An index-triangle structure for libIGL functions. Also serves as an
/// alternative (raw) input format for the SLASupportTree
/*struct EigenMesh3D {
@ -78,24 +78,38 @@ public:
// Result of a raycast
class hit_result {
double m_t = std::numeric_limits<double>::infinity();
double m_t = std::nan("");
int m_face_id = -1;
const EigenMesh3D& m_mesh;
const EigenMesh3D *m_mesh = nullptr;
Vec3d m_dir;
inline hit_result(const EigenMesh3D& em): m_mesh(em) {}
Vec3d m_source;
friend class EigenMesh3D;
// A valid object of this class can only be obtained from
// EigenMesh3D::query_ray_hit method.
explicit inline hit_result(const EigenMesh3D& em): m_mesh(&em) {}
public:
// This can create a placeholder object which is invalid (not created
// by a query_ray_hit call) but the distance can be preset to
// a specific value for distinguishing the placeholder.
inline hit_result(double val = std::nan("")): m_t(val) {}
inline double distance() const { return m_t; }
inline const Vec3d& direction() const { return m_dir; }
inline Vec3d position() const { return m_source + m_dir * m_t; }
inline int face() const { return m_face_id; }
inline bool is_valid() const { return m_mesh != nullptr; }
// Hit_result can decay into a double as the hit distance.
inline operator double() const { return distance(); }
inline Vec3d normal() const {
if(m_face_id < 0) return {};
auto trindex = m_mesh.m_F.row(m_face_id);
const Vec3d& p1 = m_mesh.V().row(trindex(0));
const Vec3d& p2 = m_mesh.V().row(trindex(1));
const Vec3d& p3 = m_mesh.V().row(trindex(2));
if(m_face_id < 0 || !is_valid()) return {};
auto trindex = m_mesh->m_F.row(m_face_id);
const Vec3d& p1 = m_mesh->V().row(trindex(0));
const Vec3d& p2 = m_mesh->V().row(trindex(1));
const Vec3d& p3 = m_mesh->V().row(trindex(2));
Eigen::Vector3d U = p2 - p1;
Eigen::Vector3d V = p3 - p1;
return U.cross(V).normalized();
@ -133,6 +147,8 @@ public:
bool inside(const Vec3d& p) const;
#endif /* SLIC3R_SLA_NEEDS_WINDTREE */
double squared_distance(const Vec3d& p, int& i, Vec3d& c) const;
};
@ -142,4 +158,4 @@ public:
} // namespace Slic3r
#endif // SLASUPPORTTREE_HPP
#endif // SLASUPPORTTREE_HPP

View File

@ -43,6 +43,8 @@ public:
// For testing
size_t size() const;
bool empty() const { return size() == 0; }
void foreach(std::function<void(const SpatElement& el)> fn);
};
}

File diff suppressed because it is too large Load Diff

View File

@ -50,11 +50,6 @@ struct SupportConfig {
// Width in mm from the back sphere center to the front sphere center.
double head_width_mm = 1.0;
// Radius in mm of the support pillars. The actual radius of the pillars
// beginning with a head will not be higher than head_back_radius but the
// headless pillars will have half of this value.
double headless_pillar_radius_mm = 0.4;
// How to connect pillars
PillarConnectionMode pillar_connection_mode = PillarConnectionMode::dynamic;
@ -74,18 +69,34 @@ struct SupportConfig {
double base_height_mm = 1.0;
// The default angle for connecting support sticks and junctions.
double tilt = M_PI/4;
double bridge_slope = M_PI/4;
// The max length of a bridge in mm
double max_bridge_length_mm = 15.0;
double max_bridge_length_mm = 10.0;
// The max distance of a pillar to pillar link.
double max_pillar_link_distance_mm = 10.0;
// The elevation in Z direction upwards. This is the space between the pad
// and the model object's bounding box bottom.
double object_elevation_mm = 10;
// The max Z angle for a normal at which it will get completely ignored.
double normal_cutoff_angle = 150.0 * M_PI / 180.0;
// /////////////////////////////////////////////////////////////////////////
// Compile time configuration values (candidates for runtime)
// /////////////////////////////////////////////////////////////////////////
// The max Z angle for a normal at which it will get completely ignored.
static const double normal_cutoff_angle;
// The shortest distance of any support structure from the model surface
static const double safety_distance_mm;
static const double max_solo_pillar_height_mm;
static const double max_dual_pillar_height_mm;
static const double optimizer_rel_score_diff;
static const unsigned optimizer_max_iterations;
static const unsigned pillar_cascade_neighbors;
static const unsigned max_bridges_on_pillar;
};
struct PoolConfig;
@ -116,21 +127,14 @@ using PointSet = Eigen::MatrixXd;
//EigenMesh3D to_eigenmesh(const ModelObject& model);
// Simple conversion of 'vector of points' to an Eigen matrix
PointSet to_point_set(const std::vector<sla::SupportPoint>&);
//PointSet to_point_set(const std::vector<sla::SupportPoint>&);
/* ************************************************************************** */
/// Just a wrapper to the runtime error to be recognizable in try blocks
class SLASupportsStoppedException: public std::runtime_error {
public:
using std::runtime_error::runtime_error;
SLASupportsStoppedException();
};
/// The class containing mesh data for the generated supports.
class SLASupportTree {
class Impl;
class Impl; // persistent support data
std::unique_ptr<Impl> m_impl;
Impl& get() { return *m_impl; }
@ -140,16 +144,25 @@ class SLASupportTree {
const SupportConfig&,
const Controller&);
/// Generate the 3D supports for a model intended for SLA print.
bool generate(const PointSet& pts,
// The generation algorithm is quite long and will be captured in a separate
// class with private data, helper methods, etc... This data is only needed
// during the calculation whereas the Impl class contains the persistent
// data, mostly the meshes.
class Algorithm;
// Generate the 3D supports for a model intended for SLA print. This
// will instantiate the Algorithm class and call its appropriate methods
// with status indication.
bool generate(const std::vector<SupportPoint>& pts,
const EigenMesh3D& mesh,
const SupportConfig& cfg = {},
const Controller& ctl = {});
public:
SLASupportTree();
SLASupportTree(const PointSet& pts,
SLASupportTree(const std::vector<SupportPoint>& pts,
const EigenMesh3D& em,
const SupportConfig& cfg = {},
const Controller& ctl = {});

View File

@ -17,6 +17,8 @@
#include <igl/remove_duplicate_vertices.h>
#include <igl/signed_distance.h>
#include <tbb/parallel_for.h>
#include "SLASpatIndex.hpp"
#include "ClipperUtils.hpp"
@ -89,6 +91,11 @@ size_t SpatIndex::size() const
return m_impl->m_store.size();
}
void SpatIndex::foreach(std::function<void (const SpatElement &)> fn)
{
for(auto& el : m_impl->m_store) fn(el);
}
/* ****************************************************************************
* EigenMesh3D implementation
* ****************************************************************************/
@ -167,6 +174,7 @@ EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const
hit_result ret(*this);
ret.m_t = double(hit.t);
ret.m_dir = dir;
ret.m_source = s;
if(!std::isinf(hit.t) && !std::isnan(hit.t)) ret.m_face_id = hit.id;
return ret;
@ -186,6 +194,15 @@ bool EigenMesh3D::inside(const Vec3d &p) const {
}
#endif /* SLIC3R_SLA_NEEDS_WINDTREE */
double EigenMesh3D::squared_distance(const Vec3d &p, int& i, Vec3d& c) const {
double sqdst = 0;
Eigen::Matrix<double, 1, 3> pp = p;
Eigen::Matrix<double, 1, 3> cc;
sqdst = m_aabb->squared_distance(m_V, m_F, pp, i, cc);
c = cc;
return sqdst;
}
/* ****************************************************************************
* Misc functions
* ****************************************************************************/
@ -208,22 +225,31 @@ template<class Vec> double distance(const Vec& pp1, const Vec& pp2) {
PointSet normals(const PointSet& points,
const EigenMesh3D& mesh,
double eps,
std::function<void()> throw_on_cancel)
std::function<void()> thr, // throw on cancel
const std::vector<unsigned>& pt_indices = {})
{
if(points.rows() == 0 || mesh.V().rows() == 0 || mesh.F().rows() == 0)
return {};
Eigen::VectorXd dists;
Eigen::VectorXi I;
PointSet C;
std::vector<unsigned> range = pt_indices;
if(range.empty()) {
range.resize(size_t(points.rows()), 0);
std::iota(range.begin(), range.end(), 0);
}
igl::point_mesh_squared_distance( points, mesh.V(), mesh.F(), dists, I, C);
PointSet ret(range.size(), 3);
PointSet ret(I.rows(), 3);
for(int i = 0; i < I.rows(); i++) {
throw_on_cancel();
auto idx = I(i);
auto trindex = mesh.F().row(idx);
tbb::parallel_for(size_t(0), range.size(),
[&ret, &range, &mesh, &points, thr, eps](size_t ridx)
{
thr();
auto eidx = Eigen::Index(range[ridx]);
int faceid = 0;
Vec3d p;
mesh.squared_distance(points.row(eidx), faceid, p);
auto trindex = mesh.F().row(faceid);
const Vec3d& p1 = mesh.V().row(trindex(0));
const Vec3d& p2 = mesh.V().row(trindex(1));
@ -237,8 +263,6 @@ PointSet normals(const PointSet& points,
// of its triangle. The procedure is the same, get the neighbor
// triangles and calculate an average normal.
const Vec3d& p = C.row(i);
// mark the vertex indices of the edge. ia and ib marks and edge ic
// will mark a single vertex.
int ia = -1, ib = -1, ic = -1;
@ -266,7 +290,7 @@ PointSet normals(const PointSet& points,
std::vector<Vec3i> neigh;
if(ic >= 0) { // The point is right on a vertex of the triangle
for(int n = 0; n < mesh.F().rows(); ++n) {
throw_on_cancel();
thr();
Vec3i ni = mesh.F().row(n);
if((ni(X) == ic || ni(Y) == ic || ni(Z) == ic))
neigh.emplace_back(ni);
@ -275,7 +299,7 @@ PointSet normals(const PointSet& points,
else if(ia >= 0 && ib >= 0) { // the point is on and edge
// now get all the neigboring triangles
for(int n = 0; n < mesh.F().rows(); ++n) {
throw_on_cancel();
thr();
Vec3i ni = mesh.F().row(n);
if((ni(X) == ia || ni(Y) == ia || ni(Z) == ia) &&
(ni(X) == ib || ni(Y) == ib || ni(Z) == ib))
@ -316,52 +340,32 @@ PointSet normals(const PointSet& points,
Vec3d sumnorm(0, 0, 0);
sumnorm = std::accumulate(neighnorms.begin(), lend, sumnorm);
sumnorm.normalize();
ret.row(i) = sumnorm;
ret.row(long(ridx)) = sumnorm;
}
else { // point lies safely within its triangle
Eigen::Vector3d U = p2 - p1;
Eigen::Vector3d V = p3 - p1;
ret.row(i) = U.cross(V).normalized();
ret.row(long(ridx)) = U.cross(V).normalized();
}
}
});
return ret;
}
namespace bgi = boost::geometry::index;
using Index3D = bgi::rtree< SpatElement, bgi::rstar<16, 4> /* ? */ >;
// Clustering a set of points by the given criteria
ClusteredPoints cluster(
const sla::PointSet& points,
std::function<bool(const SpatElement&, const SpatElement&)> pred,
unsigned max_points = 0)
ClusteredPoints cluster(Index3D& sindex, unsigned max_points,
std::function<std::vector<SpatElement>(const Index3D&, const SpatElement&)> qfn)
{
namespace bgi = boost::geometry::index;
using Index3D = bgi::rtree< SpatElement, bgi::rstar<16, 4> /* ? */ >;
// A spatial index for querying the nearest points
Index3D sindex;
// Build the index
for(unsigned idx = 0; idx < points.rows(); idx++)
sindex.insert( std::make_pair(points.row(idx), idx));
using Elems = std::vector<SpatElement>;
// Recursive function for visiting all the points in a given distance to
// each other
std::function<void(Elems&, Elems&)> group =
[&sindex, &group, pred, max_points](Elems& pts, Elems& cluster)
[&sindex, &group, max_points, qfn](Elems& pts, Elems& cluster)
{
for(auto& p : pts) {
std::vector<SpatElement> tmp;
sindex.query(
bgi::satisfies([p, pred](const SpatElement& se) {
return pred(p, se);
}),
std::back_inserter(tmp)
);
std::vector<SpatElement> tmp = qfn(sindex, p);
auto cmp = [](const SpatElement& e1, const SpatElement& e2){
return e1.second < e2.second;
};
@ -405,5 +409,84 @@ ClusteredPoints cluster(
return result;
}
namespace {
std::vector<SpatElement> distance_queryfn(const Index3D& sindex,
const SpatElement& p,
double dist,
unsigned max_points)
{
std::vector<SpatElement> tmp; tmp.reserve(max_points);
sindex.query(
bgi::nearest(p.first, max_points),
std::back_inserter(tmp)
);
for(auto it = tmp.begin(); it < tmp.end(); ++it)
if(distance(p.first, it->first) > dist) it = tmp.erase(it);
return tmp;
}
}
// Clustering a set of points by the given criteria
ClusteredPoints cluster(
const std::vector<unsigned>& indices,
std::function<Vec3d(unsigned)> pointfn,
double dist,
unsigned max_points)
{
// A spatial index for querying the nearest points
Index3D sindex;
// Build the index
for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx));
return cluster(sindex, max_points,
[dist, max_points](const Index3D& sidx, const SpatElement& p)
{
return distance_queryfn(sidx, p, dist, max_points);
});
}
// Clustering a set of points by the given criteria
ClusteredPoints cluster(
const std::vector<unsigned>& indices,
std::function<Vec3d(unsigned)> pointfn,
std::function<bool(const SpatElement&, const SpatElement&)> predicate,
unsigned max_points)
{
// A spatial index for querying the nearest points
Index3D sindex;
// Build the index
for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx));
return cluster(sindex, max_points,
[max_points, predicate](const Index3D& sidx, const SpatElement& p)
{
std::vector<SpatElement> tmp; tmp.reserve(max_points);
sidx.query(bgi::satisfies([p, predicate](const SpatElement& e){
return predicate(p, e);
}), std::back_inserter(tmp));
return tmp;
});
}
ClusteredPoints cluster(const PointSet& pts, double dist, unsigned max_points)
{
// A spatial index for querying the nearest points
Index3D sindex;
// Build the index
for(Eigen::Index i = 0; i < pts.rows(); i++)
sindex.insert(std::make_pair(Vec3d(pts.row(i)), unsigned(i)));
return cluster(sindex, max_points,
[dist, max_points](const Index3D& sidx, const SpatElement& p)
{
return distance_queryfn(sidx, p, dist, max_points);
});
}
}
}

View File

@ -118,6 +118,9 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
// Make a copy of the config, normalize it.
DynamicPrintConfig config(config_in);
config.option("sla_print_settings_id", true);
config.option("sla_material_settings_id", true);
config.option("printer_settings_id", true);
config.normalize();
// Collect changes to print config.
t_config_option_keys print_diff = m_print_config.diff(config);
@ -151,9 +154,9 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
PlaceholderParser &pp = this->placeholder_parser();
pp.apply_config(config);
// Set the profile aliases for the PrintBase::output_filename()
pp.set("print_preset", config_in.option("sla_print_settings_id")->clone());
pp.set("material_preset", config_in.option("sla_material_settings_id")->clone());
pp.set("printer_preset", config_in.option("printer_settings_id")->clone());
pp.set("print_preset", config.option("sla_print_settings_id")->clone());
pp.set("material_preset", config.option("sla_material_settings_id")->clone());
pp.set("printer_preset", config.option("printer_settings_id")->clone());
}
// It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
@ -300,81 +303,82 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted);
if (it_status->status == ModelObjectStatus::New)
// PrintObject instances will be added in the next loop.
continue;
// Update the ModelObject instance, possibly invalidate the linked PrintObjects.
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
const ModelObject &model_object_new = *model.objects[idx_model_object];
auto it_print_object_status = print_object_status.lower_bound(PrintObjectStatus(model_object.id()));
if (it_print_object_status != print_object_status.end() && it_print_object_status->id != model_object.id())
it_print_object_status = print_object_status.end();
// Check whether a model part volume was added or removed, their transformations or order changed.
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
bool sla_trafo_differs = model_object.instances.empty() != model_object_new.instances.empty() ||
(! model_object.instances.empty() && ! sla_trafo(model_object).isApprox(sla_trafo(model_object_new)));
if (model_parts_differ || sla_trafo_differs) {
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
if (it_print_object_status != print_object_status.end()) {
update_apply_status(it_print_object_status->print_object->invalidate_all_steps());
const_cast<PrintObjectStatus&>(*it_print_object_status).status = PrintObjectStatus::Deleted;
}
// Copy content of the ModelObject including its ID, do not change the parent.
model_object.assign_copy(model_object_new);
} else {
// Synchronize Object's config.
bool object_config_changed = model_object.config != model_object_new.config;
if (object_config_changed)
model_object.config = model_object_new.config;
if (! object_diff.empty() || object_config_changed) {
SLAPrintObjectConfig new_config = m_default_object_config;
normalize_and_apply_config(new_config, model_object.config);
if (it_print_object_status != print_object_status.end()) {
t_config_option_keys diff = it_print_object_status->print_object->config().diff(new_config);
if (! diff.empty()) {
update_apply_status(it_print_object_status->print_object->invalidate_state_by_config_options(diff));
it_print_object_status->print_object->config_apply_only(new_config, diff, true);
}
}
}
/*if (model_object.sla_support_points != model_object_new.sla_support_points) {
model_object.sla_support_points = model_object_new.sla_support_points;
if (it_print_object_status != print_object_status.end())
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
}
if (model_object.sla_points_status != model_object_new.sla_points_status) {
// Change of this status should invalidate support points. The points themselves are not enough, there are none
// in case that nothing was generated OR that points were autogenerated already and not copied to the front-end.
// These cases can only be differentiated by checking the status change. However, changing from 'Generating' should NOT
// invalidate - that would keep stopping the background processing without a reason.
if (model_object.sla_points_status != sla::PointsStatus::Generating)
if (it_print_object_status != print_object_status.end())
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
model_object.sla_points_status = model_object_new.sla_points_status;
}*/
// PrintObject for this ModelObject, if it exists.
auto it_print_object_status = print_object_status.end();
if (it_status->status != ModelObjectStatus::New) {
// Update the ModelObject instance, possibly invalidate the linked PrintObjects.
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
const ModelObject &model_object_new = *model.objects[idx_model_object];
it_print_object_status = print_object_status.lower_bound(PrintObjectStatus(model_object.id()));
if (it_print_object_status != print_object_status.end() && it_print_object_status->id != model_object.id())
it_print_object_status = print_object_status.end();
// Check whether a model part volume was added or removed, their transformations or order changed.
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
bool sla_trafo_differs = model_object.instances.empty() != model_object_new.instances.empty() ||
(! model_object.instances.empty() && ! sla_trafo(model_object).isApprox(sla_trafo(model_object_new)));
if (model_parts_differ || sla_trafo_differs) {
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
if (it_print_object_status != print_object_status.end()) {
update_apply_status(it_print_object_status->print_object->invalidate_all_steps());
const_cast<PrintObjectStatus&>(*it_print_object_status).status = PrintObjectStatus::Deleted;
}
// Copy content of the ModelObject including its ID, do not change the parent.
model_object.assign_copy(model_object_new);
} else {
// Synchronize Object's config.
bool object_config_changed = model_object.config != model_object_new.config;
if (object_config_changed)
model_object.config = model_object_new.config;
if (! object_diff.empty() || object_config_changed) {
SLAPrintObjectConfig new_config = m_default_object_config;
normalize_and_apply_config(new_config, model_object.config);
if (it_print_object_status != print_object_status.end()) {
t_config_option_keys diff = it_print_object_status->print_object->config().diff(new_config);
if (! diff.empty()) {
update_apply_status(it_print_object_status->print_object->invalidate_state_by_config_options(diff));
it_print_object_status->print_object->config_apply_only(new_config, diff, true);
}
}
}
/*if (model_object.sla_support_points != model_object_new.sla_support_points) {
model_object.sla_support_points = model_object_new.sla_support_points;
if (it_print_object_status != print_object_status.end())
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
}
if (model_object.sla_points_status != model_object_new.sla_points_status) {
// Change of this status should invalidate support points. The points themselves are not enough, there are none
// in case that nothing was generated OR that points were autogenerated already and not copied to the front-end.
// These cases can only be differentiated by checking the status change. However, changing from 'Generating' should NOT
// invalidate - that would keep stopping the background processing without a reason.
if (model_object.sla_points_status != sla::PointsStatus::Generating)
if (it_print_object_status != print_object_status.end())
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
model_object.sla_points_status = model_object_new.sla_points_status;
}*/
bool old_user_modified = model_object.sla_points_status == sla::PointsStatus::UserModified;
bool new_user_modified = model_object_new.sla_points_status == sla::PointsStatus::UserModified;
if ((old_user_modified && ! new_user_modified) || // switching to automatic supports from manual supports
(! old_user_modified && new_user_modified) || // switching to manual supports from automatic supports
(new_user_modified && model_object.sla_support_points != model_object_new.sla_support_points)) {
if (it_print_object_status != print_object_status.end())
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
bool old_user_modified = model_object.sla_points_status == sla::PointsStatus::UserModified;
bool new_user_modified = model_object_new.sla_points_status == sla::PointsStatus::UserModified;
if ((old_user_modified && ! new_user_modified) || // switching to automatic supports from manual supports
(! old_user_modified && new_user_modified) || // switching to manual supports from automatic supports
(new_user_modified && model_object.sla_support_points != model_object_new.sla_support_points)) {
if (it_print_object_status != print_object_status.end())
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
model_object.sla_points_status = model_object_new.sla_points_status;
model_object.sla_support_points = model_object_new.sla_support_points;
}
model_object.sla_points_status = model_object_new.sla_points_status;
model_object.sla_support_points = model_object_new.sla_support_points;
}
// Copy the ModelObject name, input_file and instances. The instances will compared against PrintObject instances in the next step.
model_object.name = model_object_new.name;
model_object.input_file = model_object_new.input_file;
model_object.clear_instances();
model_object.instances.reserve(model_object_new.instances.size());
for (const ModelInstance *model_instance : model_object_new.instances) {
model_object.instances.emplace_back(new ModelInstance(*model_instance));
model_object.instances.back()->set_model_object(&model_object);
}
}
// Copy the ModelObject name, input_file and instances. The instances will compared against PrintObject instances in the next step.
model_object.name = model_object_new.name;
model_object.input_file = model_object_new.input_file;
model_object.clear_instances();
model_object.instances.reserve(model_object_new.instances.size());
for (const ModelInstance *model_instance : model_object_new.instances) {
model_object.instances.emplace_back(new ModelInstance(*model_instance));
model_object.instances.back()->set_model_object(&model_object);
}
}
}
std::vector<SLAPrintObject::Instance> new_instances = sla_instances(model_object);
if (it_print_object_status != print_object_status.end() && it_print_object_status->status != PrintObjectStatus::Deleted) {
@ -544,9 +548,9 @@ sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) {
scfg.head_penetration_mm = c.support_head_penetration.getFloat();
scfg.head_width_mm = c.support_head_width.getFloat();
scfg.object_elevation_mm = c.support_object_elevation.getFloat();
scfg.tilt = c.support_critical_angle.getFloat() * PI / 180.0 ;
scfg.bridge_slope = c.support_critical_angle.getFloat() * PI / 180.0 ;
scfg.max_bridge_length_mm = c.support_max_bridge_length.getFloat();
scfg.headless_pillar_radius_mm = 0.375*c.support_pillar_diameter.getFloat();
scfg.max_pillar_link_distance_mm = c.support_max_pillar_link_distance.getFloat();
switch(c.support_pillar_connection_mode.getInt()) {
case slapcmZigZag:
scfg.pillar_connection_mode = sla::PillarConnectionMode::zigzag; break;
@ -567,7 +571,24 @@ void swapXY(ExPolygon& expoly) {
for(auto& p : expoly.contour.points) std::swap(p(X), p(Y));
for(auto& h : expoly.holes) for(auto& p : h.points) std::swap(p(X), p(Y));
}
}
std::string SLAPrint::validate() const
{
for(SLAPrintObject * po : m_objects) {
sla::SupportConfig cfg = make_support_cfg(po->config());
double pinhead_width =
2 * cfg.head_front_radius_mm +
cfg.head_width_mm +
2 * cfg.head_back_radius_mm -
cfg.head_penetration_mm;
if(pinhead_width > cfg.object_elevation_mm)
return L("Elevetion is too low for object.");
}
return "";
}
std::vector<float> SLAPrint::calculate_heights(const BoundingBoxf3& bb3d,
@ -673,6 +694,7 @@ void SLAPrint::process()
// the density config value is in percents:
config.density_relative = float(cfg.support_points_density_relative / 100.f);
config.minimal_distance = float(cfg.support_points_minimal_distance);
config.head_diameter = float(cfg.support_head_front_diameter);
// Construction of this object does the calculation.
this->throw_if_canceled();
@ -710,54 +732,52 @@ void SLAPrint::process()
return;
}
try {
sla::SupportConfig scfg = make_support_cfg(po.m_config);
sla::Controller ctl;
sla::SupportConfig scfg = make_support_cfg(po.m_config);
sla::Controller ctl;
// some magic to scale the status values coming from the support
// tree creation into the whole print process
auto stfirst = OBJ_STEP_LEVELS.begin();
auto stthis = stfirst + slaposSupportTree;
// we need to add up the status portions until this operation
int init = std::accumulate(stfirst, stthis, 0);
init = int(init * ostepd); // scale the init portion
// some magic to scale the status values coming from the support
// tree creation into the whole print process
auto stfirst = OBJ_STEP_LEVELS.begin();
auto stthis = stfirst + slaposSupportTree;
// we need to add up the status portions until this operation
int init = std::accumulate(stfirst, stthis, 0);
init = int(init * ostepd); // scale the init portion
// scaling for the sub operations
double d = *stthis / (objcount * 100.0);
// scaling for the sub operations
double d = *stthis / (objcount * 100.0);
ctl.statuscb = [this, init, d](unsigned st, const std::string& msg)
{
//FIXME this status line scaling does not seem to be correct.
// How does it account for an increasing object index?
report_status(*this, int(init + st*d), msg);
};
ctl.statuscb = [this, init, d](unsigned st, const std::string& msg)
{
//FIXME this status line scaling does not seem to be correct.
// How does it account for an increasing object index?
report_status(*this, int(init + st*d), msg);
};
ctl.stopcondition = [this](){ return canceled(); };
ctl.cancelfn = [this]() { throw_if_canceled(); };
ctl.stopcondition = [this](){ return canceled(); };
ctl.cancelfn = [this]() { throw_if_canceled(); };
po.m_supportdata->support_tree_ptr.reset(
new SLASupportTree(sla::to_point_set(po.m_supportdata->support_points),
po.m_supportdata->emesh, scfg, ctl));
po.m_supportdata->support_tree_ptr.reset(
new SLASupportTree(po.m_supportdata->support_points,
po.m_supportdata->emesh, scfg, ctl));
// Create the unified mesh
auto rc = SlicingStatus::RELOAD_SCENE;
throw_if_canceled();
// This is to prevent "Done." being displayed during merged_mesh()
report_status(*this, -1, L("Visualizing supports"));
po.m_supportdata->support_tree_ptr->merged_mesh();
// Create the unified mesh
auto rc = SlicingStatus::RELOAD_SCENE;
BOOST_LOG_TRIVIAL(debug) << "Processed support point count "
<< po.m_supportdata->support_points.size();
// This is to prevent "Done." being displayed during merged_mesh()
report_status(*this, -1, L("Visualizing supports"));
po.m_supportdata->support_tree_ptr->merged_mesh();
// Check the mesh for later troubleshooting.
if(po.support_mesh().empty())
BOOST_LOG_TRIVIAL(warning) << "Support mesh is empty";
BOOST_LOG_TRIVIAL(debug) << "Processed support point count "
<< po.m_supportdata->support_points.size();
// Check the mesh for later troubleshooting.
if(po.support_mesh().empty())
BOOST_LOG_TRIVIAL(warning) << "Support mesh is empty";
report_status(*this, -1, L("Visualizing supports"), rc);
report_status(*this, -1, L("Visualizing supports"), rc);
} catch(sla::SLASupportsStoppedException&) {
// no need to rethrow
// throw_if_canceled();
}
};
// This step generates the sla base pad
@ -821,7 +841,7 @@ void SLAPrint::process()
// We have the layer polygon collection but we need to unite them into
// an index where the key is the height level in discrete levels (clipper)
auto index_slices = [ilhd](SLAPrintObject& po) {
auto index_slices = [this, ilhd](SLAPrintObject& po) {
po.m_slice_index.clear();
auto sih = LevelID(scale_(ilhd));
@ -890,6 +910,9 @@ void SLAPrint::process()
sr.support_slices_idx = SLAPrintObject::SliceRecord::Idx(i);
}
}
// Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update status to the 3D preview to load the SLA slices.
report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW);
};
// Rasterizing the model objects, and their supports
@ -1389,6 +1412,7 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector<t_conf
|| opt_key == "support_base_height"
|| opt_key == "support_critical_angle"
|| opt_key == "support_max_bridge_length"
|| opt_key == "support_max_pillar_link_distance"
|| opt_key == "support_object_elevation") {
steps.emplace_back(slaposSupportTree);
} else if (

View File

@ -240,6 +240,8 @@ public:
const SLAPrintStatistics& print_statistics() const { return m_print_statistics; }
std::string validate() const override;
private:
using SLAPrinter = FilePrinter<FilePrinterFormat::SLA_PNGZIP>;
using SLAPrinterPtr = std::unique_ptr<SLAPrinter>;
@ -247,6 +249,11 @@ private:
// Invalidate steps based on a set of parameters changed.
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
std::vector<float> calculate_heights(const BoundingBoxf3& bb,
float elevation,
float initial_layer_height,
float layer_height) const;
void fill_statistics();
SLAPrintConfig m_print_config;
@ -270,8 +277,6 @@ private:
lref(std::cref(lyr)), copies(std::cref(cp)) {}
};
std::vector<float> calculate_heights(const BoundingBoxf3& bb, float elevation, float initial_layer_height, float layer_height) const;
// One level may contain multiple slices from multiple objects and their
// supports
using LayerRefs = std::vector<LayerRef>;

View File

@ -2850,7 +2850,7 @@ void modulate_extrusion_by_overlapping_layers(
if (end_and_dist2.first == nullptr) {
// New fragment connecting to pt_current was not found.
// Verify that the last point found is close to the original end point of the unfragmented path.
//const double d2 = (pt_end - pt_current).squaredNorm();
//const double d2 = (pt_end - pt_current).cast<double>.squaredNorm();
//assert(d2 < coordf_t(search_radius * search_radius));
// End of the path.
break;

View File

@ -20,9 +20,8 @@
// Disable synchronization of unselected instances
#define DISABLE_INSTANCES_SYNCH (0 && ENABLE_1_42_0_ALPHA1)
// Scene's GUI made using imgui library
#define ENABLE_IMGUI (1 && ENABLE_1_42_0_ALPHA1)
#define DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI (1 && ENABLE_IMGUI)
// Disable imgui dialog for move, rotate and scale gizmos
#define DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI (1 && ENABLE_1_42_0_ALPHA1)
// Use wxDataViewRender instead of wxDataViewCustomRenderer
#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0_ALPHA1)

View File

@ -247,7 +247,7 @@ bool TriangleMesh::needed_repair() const
|| this->stl.stats.backwards_edges > 0;
}
void TriangleMesh::WriteOBJFile(char* output_file)
void TriangleMesh::WriteOBJFile(const char* output_file)
{
stl_generate_shared_vertices(&stl);
stl_write_obj(&stl, output_file);
@ -620,8 +620,9 @@ void TriangleMesh::require_shared_vertices()
for (int nbr_idx = 0; nbr_idx < 3; ++nbr_idx) {
int nbr_face = this->stl.neighbors_start[facet_idx].neighbor[nbr_idx];
if (nbr_face != -1) {
assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == vertices[(nbr_idx + 1) % 3]);
assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == vertices[nbr_idx]);
assert(
(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == vertices[(nbr_idx + 1) % 3] && stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == vertices[nbr_idx]) ||
(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == vertices[(nbr_idx + 1) % 3] && stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == vertices[nbr_idx]));
}
}
}
@ -1498,9 +1499,16 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
// Try to close gaps.
// Do it in two rounds, first try to connect in the same direction only,
// then try to connect the open polylines in reversed order as well.
#if 0
for (double max_gap : { EPSILON, 0.001, 0.1, 1., 2. }) {
chain_open_polylines_close_gaps(open_polylines, *loops, max_gap, false);
chain_open_polylines_close_gaps(open_polylines, *loops, max_gap, true);
}
#else
const double max_gap = 2.; //mm
chain_open_polylines_close_gaps(open_polylines, *loops, max_gap, false);
chain_open_polylines_close_gaps(open_polylines, *loops, max_gap, true);
#endif
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{

View File

@ -36,7 +36,7 @@ public:
float volume();
void check_topology();
bool is_manifold() const { return this->stl.stats.connected_facets_3_edge == (int)this->stl.stats.number_of_facets; }
void WriteOBJFile(char* output_file);
void WriteOBJFile(const char* output_file);
void scale(float factor);
void scale(const Vec3d &versor);
void translate(float x, float y, float z);

View File

@ -31,22 +31,456 @@
#include "libslic3r/Print.hpp"
#include "libslic3r/SLAPrint.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/Format/AMF.hpp"
#include "libslic3r/Format/3mf.hpp"
#include "libslic3r/Format/STL.hpp"
#include "libslic3r/Format/OBJ.hpp"
#include "libslic3r/Utils.hpp"
#include "slic3r.hpp"
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/GUI_App.hpp"
using namespace Slic3r;
/// utility function for displaying CLI usage
void printUsage();
PrinterTechnology get_printer_technology(const DynamicConfig &config)
{
const ConfigOptionEnum<PrinterTechnology> *opt = config.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology");
return (opt == nullptr) ? ptUnknown : opt->value;
}
#ifdef _MSC_VER
int slic3r_main_(int argc, char **argv)
int CLI::run(int argc, char **argv)
{
if (! this->setup(argc, argv))
return 1;
m_extra_config.apply(m_config, true);
m_extra_config.normalize();
bool start_gui = m_actions.empty() &&
// cutting transformations are setting an "export" action.
std::find(m_transforms.begin(), m_transforms.end(), "cut") == m_transforms.end() &&
std::find(m_transforms.begin(), m_transforms.end(), "cut_x") == m_transforms.end() &&
std::find(m_transforms.begin(), m_transforms.end(), "cut_y") == m_transforms.end();
PrinterTechnology printer_technology = get_printer_technology(m_extra_config);
const std::vector<std::string> &load_configs = m_config.option<ConfigOptionStrings>("load", true)->values;
// load config files supplied via --load
for (auto const &file : load_configs) {
if (! boost::filesystem::exists(file)) {
if (m_config.opt_bool("ignore_nonexistent_config")) {
continue;
} else {
boost::nowide::cerr << "No such file: " << file << std::endl;
return 1;
}
}
DynamicPrintConfig config;
try {
config.load(file);
} catch (std::exception &ex) {
boost::nowide::cerr << "Error while reading config file: " << ex.what() << std::endl;
return 1;
}
config.normalize();
PrinterTechnology other_printer_technology = get_printer_technology(config);
if (printer_technology == ptUnknown) {
printer_technology = other_printer_technology;
} else if (printer_technology != other_printer_technology) {
boost::nowide::cerr << "Mixing configurations for FFF and SLA technologies" << std::endl;
return 1;
}
m_print_config.apply(config);
}
// Read input file(s) if any.
for (const std::string &file : m_input_files) {
if (! boost::filesystem::exists(file)) {
boost::nowide::cerr << "No such file: " << file << std::endl;
exit(1);
}
Model model;
try {
// When loading an AMF or 3MF, config is imported as well, including the printer technology.
model = Model::read_from_file(file, &m_print_config, true);
PrinterTechnology other_printer_technology = get_printer_technology(m_print_config);
if (printer_technology == ptUnknown) {
printer_technology = other_printer_technology;
} else if (printer_technology != other_printer_technology) {
boost::nowide::cerr << "Mixing configurations for FFF and SLA technologies" << std::endl;
return 1;
}
} catch (std::exception &e) {
boost::nowide::cerr << file << ": " << e.what() << std::endl;
return 1;
}
if (model.objects.empty()) {
boost::nowide::cerr << "Error: file is empty: " << file << std::endl;
continue;
}
m_models.push_back(model);
}
// Apply command line options to a more specific DynamicPrintConfig which provides normalize()
// (command line options override --load files)
m_print_config.apply(m_extra_config, true);
// Normalizing after importing the 3MFs / AMFs
m_print_config.normalize();
if (printer_technology == ptUnknown)
printer_technology = std::find(m_actions.begin(), m_actions.end(), "export_sla") == m_actions.end() ? ptFFF : ptSLA;
// Initialize full print configs for both the FFF and SLA technologies.
FullPrintConfig fff_print_config;
// SLAFullPrintConfig sla_print_config;
fff_print_config.apply(m_print_config, true);
// sla_print_config.apply(m_print_config, true);
// Loop through transform options.
for (auto const &opt_key : m_transforms) {
if (opt_key == "merge") {
Model m;
for (auto &model : m_models)
for (ModelObject *o : model.objects)
m.add_object(*o);
// Rearrange instances unless --dont-arrange is supplied
if (! m_config.opt_bool("dont_arrange")) {
m.add_default_instances();
const BoundingBoxf &bb = fff_print_config.bed_shape.values;
m.arrange_objects(
fff_print_config.min_object_distance(),
// If we are going to use the merged model for printing, honor
// the configured print bed for arranging, otherwise do it freely.
this->has_print_action() ? &bb : nullptr
);
}
m_models.clear();
m_models.emplace_back(std::move(m));
} else if (opt_key == "duplicate") {
const BoundingBoxf &bb = fff_print_config.bed_shape.values;
for (auto &model : m_models) {
const bool all_objects_have_instances = std::none_of(
model.objects.begin(), model.objects.end(),
[](ModelObject* o){ return o->instances.empty(); }
);
if (all_objects_have_instances) {
// if all input objects have defined position(s) apply duplication to the whole model
model.duplicate(m_config.opt_int("duplicate"), fff_print_config.min_object_distance(), &bb);
} else {
model.add_default_instances();
model.duplicate_objects(m_config.opt_int("duplicate"), fff_print_config.min_object_distance(), &bb);
}
}
} else if (opt_key == "duplicate_grid") {
std::vector<int> &ints = m_config.option<ConfigOptionInts>("duplicate_grid")->values;
const int x = ints.size() > 0 ? ints.at(0) : 1;
const int y = ints.size() > 1 ? ints.at(1) : 1;
const double distance = fff_print_config.duplicate_distance.value;
for (auto &model : m_models)
model.duplicate_objects_grid(x, y, (distance > 0) ? distance : 6); // TODO: this is not the right place for setting a default
} else if (opt_key == "center") {
for (auto &model : m_models) {
model.add_default_instances();
// this affects instances:
model.center_instances_around_point(m_config.option<ConfigOptionPoint>("center")->value);
// this affects volumes:
//FIXME Vojtech: Who knows why the complete model should be aligned with Z as a single rigid body?
//model.align_to_ground();
BoundingBoxf3 bbox;
for (ModelObject *model_object : model.objects)
// We are interested into the Z span only, therefore it is sufficient to measure the bounding box of the 1st instance only.
bbox.merge(model_object->instance_bounding_box(0, false));
for (ModelObject *model_object : model.objects)
for (ModelInstance *model_instance : model_object->instances)
model_instance->set_offset(Z, model_instance->get_offset(Z) - bbox.min.z());
}
} else if (opt_key == "align_xy") {
const Vec2d &p = m_config.option<ConfigOptionPoint>("align_xy")->value;
for (auto &model : m_models) {
BoundingBoxf3 bb = model.bounding_box();
// this affects volumes:
model.translate(-(bb.min.x() - p.x()), -(bb.min.y() - p.y()), -bb.min.z());
}
} else if (opt_key == "dont_arrange") {
// do nothing - this option alters other transform options
} else if (opt_key == "rotate") {
for (auto &model : m_models)
for (auto &o : model.objects)
// this affects volumes:
o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), Z);
} else if (opt_key == "rotate_x") {
for (auto &model : m_models)
for (auto &o : model.objects)
// this affects volumes:
o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), X);
} else if (opt_key == "rotate_y") {
for (auto &model : m_models)
for (auto &o : model.objects)
// this affects volumes:
o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), Y);
} else if (opt_key == "scale") {
for (auto &model : m_models)
for (auto &o : model.objects)
// this affects volumes:
o->scale(m_config.get_abs_value(opt_key, 1));
} else if (opt_key == "scale_to_fit") {
const Vec3d &opt = m_config.opt<ConfigOptionPoint3>(opt_key)->value;
if (opt.x() <= 0 || opt.y() <= 0 || opt.z() <= 0) {
boost::nowide::cerr << "--scale-to-fit requires a positive volume" << std::endl;
return 1;
}
for (auto &model : m_models)
for (auto &o : model.objects)
// this affects volumes:
o->scale_to_fit(opt);
} else if (opt_key == "cut" || opt_key == "cut_x" || opt_key == "cut_y") {
std::vector<Model> new_models;
for (auto &model : m_models) {
model.repair();
model.translate(0, 0, -model.bounding_box().min.z()); // align to z = 0
size_t num_objects = model.objects.size();
for (size_t i = 0; i < num_objects; ++ i) {
#if 0
if (opt_key == "cut_x") {
o->cut(X, m_config.opt_float("cut_x"), &out);
} else if (opt_key == "cut_y") {
o->cut(Y, m_config.opt_float("cut_y"), &out);
} else if (opt_key == "cut") {
o->cut(Z, m_config.opt_float("cut"), &out);
}
#else
int main(int argc, char **argv)
model.objects.front()->cut(0, m_config.opt_float("cut"), true, true, true);
#endif
model.delete_object(size_t(0));
}
}
// TODO: copy less stuff around using pointers
m_models = new_models;
if (m_actions.empty())
m_actions.push_back("export_stl");
}
#if 0
else if (opt_key == "cut_grid") {
std::vector<Model> new_models;
for (auto &model : m_models) {
TriangleMesh mesh = model.mesh();
mesh.repair();
TriangleMeshPtrs meshes = mesh.cut_by_grid(m_config.option<ConfigOptionPoint>("cut_grid")->value);
size_t i = 0;
for (TriangleMesh* m : meshes) {
Model out;
auto o = out.add_object();
o->add_volume(*m);
o->input_file += "_" + std::to_string(i++);
delete m;
}
}
// TODO: copy less stuff around using pointers
m_models = new_models;
if (m_actions.empty())
m_actions.push_back("export_stl");
}
#endif
else if (opt_key == "split") {
for (Model &model : m_models) {
size_t num_objects = model.objects.size();
for (size_t i = 0; i < num_objects; ++ i) {
model.objects.front()->split(nullptr);
model.delete_object(size_t(0));
}
}
} else if (opt_key == "repair") {
for (auto &model : m_models)
model.repair();
} else {
boost::nowide::cerr << "error: option not implemented yet: " << opt_key << std::endl;
return 1;
}
}
// loop through action options
for (auto const &opt_key : m_actions) {
if (opt_key == "help") {
this->print_help();
} else if (opt_key == "help_fff") {
this->print_help(true, ptFFF);
} else if (opt_key == "help_sla") {
this->print_help(true, ptSLA);
} else if (opt_key == "save") {
//FIXME check for mixing the FFF / SLA parameters.
// or better save fff_print_config vs. sla_print_config
m_print_config.save(m_config.opt_string("save"));
} else if (opt_key == "info") {
// --info works on unrepaired model
for (Model &model : m_models) {
model.add_default_instances();
model.print_info();
}
} else if (opt_key == "export_stl") {
for (auto &model : m_models)
model.add_default_instances();
if (! this->export_models(IO::STL))
return 1;
} else if (opt_key == "export_obj") {
for (auto &model : m_models)
model.add_default_instances();
if (! this->export_models(IO::OBJ))
return 1;
} else if (opt_key == "export_amf") {
if (! this->export_models(IO::AMF))
return 1;
} else if (opt_key == "export_3mf") {
if (! this->export_models(IO::TMF))
return 1;
} else if (opt_key == "export_gcode" || opt_key == "export_sla" || opt_key == "slice") {
if (opt_key == "export_gcode" && printer_technology == ptSLA) {
boost::nowide::cerr << "error: cannot export G-code for an FFF configuration" << std::endl;
return 1;
} else if (opt_key == "export_sla" && printer_technology == ptFFF) {
boost::nowide::cerr << "error: cannot export SLA slices for a SLA configuration" << std::endl;
return 1;
}
// Make a copy of the model if the current action is not the last action, as the model may be
// modified by the centering and such.
Model model_copy;
bool make_copy = &opt_key != &m_actions.back();
for (Model &model_in : m_models) {
if (make_copy)
model_copy = model_in;
Model &model = make_copy ? model_copy : model_in;
// If all objects have defined instances, their relative positions will be
// honored when printing (they will be only centered, unless --dont-arrange
// is supplied); if any object has no instances, it will get a default one
// and all instances will be rearranged (unless --dont-arrange is supplied).
std::string outfile = m_config.opt_string("output");
Print fff_print;
SLAPrint sla_print;
PrintBase *print = (printer_technology == ptFFF) ? static_cast<PrintBase*>(&fff_print) : static_cast<PrintBase*>(&sla_print);
if (! m_config.opt_bool("dont_arrange")) {
//FIXME make the min_object_distance configurable.
model.arrange_objects(fff_print.config().min_object_distance());
model.center_instances_around_point(m_config.option<ConfigOptionPoint>("center")->value);
}
if (printer_technology == ptFFF) {
for (auto* mo : model.objects)
fff_print.auto_assign_extruders(mo);
}
print->apply(model, m_print_config);
std::string err = print->validate();
if (! err.empty()) {
boost::nowide::cerr << err << std::endl;
return 1;
}
if (print->empty())
boost::nowide::cout << "Nothing to print for " << outfile << " . Either the print is empty or no object is fully inside the print volume." << std::endl;
else
try {
std::string outfile_final;
print->process();
if (printer_technology == ptFFF) {
// The outfile is processed by a PlaceholderParser.
outfile = fff_print.export_gcode(outfile, nullptr);
outfile_final = fff_print.print_statistics().finalize_output_path(outfile);
} else {
outfile = sla_print.output_filepath(outfile);
//FIXME Tamas, please port it to miniz
// sla_print.export_raster<SLAZipFmt>(outfile);
outfile_final = sla_print.print_statistics().finalize_output_path(outfile);
}
if (outfile != outfile_final && Slic3r::rename_file(outfile, outfile_final) != 0) {
boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl;
return 1;
}
boost::nowide::cout << "Slicing result exported to " << outfile << std::endl;
} catch (const std::exception &ex) {
boost::nowide::cerr << ex.what() << std::endl;
return 1;
}
/*
print.center = ! m_config.has("center")
&& ! m_config.has("align_xy")
&& ! m_config.opt_bool("dont_arrange");
print.set_model(model);
// start chronometer
typedef std::chrono::high_resolution_clock clock_;
typedef std::chrono::duration<double, std::ratio<1> > second_;
std::chrono::time_point<clock_> t0{ clock_::now() };
const std::string outfile = this->output_filepath(model, IO::Gcode);
try {
print.export_gcode(outfile);
} catch (std::runtime_error &e) {
boost::nowide::cerr << e.what() << std::endl;
return 1;
}
boost::nowide::cout << "G-code exported to " << outfile << std::endl;
// output some statistics
double duration { std::chrono::duration_cast<second_>(clock_::now() - t0).count() };
boost::nowide::cout << std::fixed << std::setprecision(0)
<< "Done. Process took " << (duration/60) << " minutes and "
<< std::setprecision(3)
<< std::fmod(duration, 60.0) << " seconds." << std::endl
<< std::setprecision(2)
<< "Filament required: " << print.total_used_filament() << "mm"
<< " (" << print.total_extruded_volume()/1000 << "cm3)" << std::endl;
*/
}
} else {
boost::nowide::cerr << "error: option not supported yet: " << opt_key << std::endl;
return 1;
}
}
if (start_gui) {
#if 1
// #ifdef USE_WX
GUI::GUI_App *gui = new GUI::GUI_App();
// gui->autosave = m_config.opt_string("autosave");
GUI::GUI_App::SetInstance(gui);
gui->CallAfter([gui, this, &load_configs] {
if (!gui->initialized()) {
return;
}
#if 0
// Load the cummulative config over the currently active profiles.
//FIXME if multiple configs are loaded, only the last one will have an effect.
// We need to decide what to do about loading of separate presets (just print preset, just filament preset etc).
// As of now only the full configs are supported here.
if (!m_print_config.empty())
gui->mainframe->load_config(m_print_config);
#endif
if (! load_configs.empty())
// Load the last config to give it a name at the UI. The name of the preset may be later
// changed by loading an AMF or 3MF.
//FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config.
gui->mainframe->load_config_file(load_configs.back());
// If loading a 3MF file, the config is loaded from the last one.
if (! m_input_files.empty())
gui->plater()->load_files(m_input_files, true, true);
if (! m_extra_config.empty())
gui->mainframe->load_config(m_extra_config);
});
return wxEntry(argc, argv);
#else
// No GUI support. Just print out a help.
this->print_help(false);
// If started without a parameter, consider it to be OK, otherwise report an error code (no action etc).
return (argc == 0) ? 0 : 1;
#endif
}
return 0;
}
bool CLI::setup(int argc, char **argv)
{
{
const char *loglevel = boost::nowide::getenv("SLIC3R_LOGLEVEL");
@ -58,15 +492,6 @@ int main(int argc, char **argv)
}
}
// parse all command line options into a DynamicConfig
DynamicPrintAndCLIConfig all_config;
t_config_option_keys input_files;
// if any option is unsupported, print usage and abort immediately
if (! all_config.read_cli(argc, argv, &input_files)) {
printUsage();
return 0;
}
boost::filesystem::path path_to_binary = boost::filesystem::system_complete(argv[0]);
// Path from the Slic3r binary to its resources.
@ -94,207 +519,114 @@ int main(int argc, char **argv)
set_var_dir((path_resources / "icons").string());
set_local_dir((path_resources / "localization").string());
// apply command line options to a more handy CLIConfig
CLIConfig cli_config;
#ifdef __APPLE__
// Enable the GUI mode by default, to support drag & drop.
cli_config.gui.value = true;
#endif /* __APPLE__ */
// Parse all command line options into a DynamicConfig.
// If any option is unsupported, print usage and abort immediately.
t_config_option_keys opt_order;
if (! m_config.read_cli(argc, argv, &m_input_files, &opt_order)) {
// Separate error message reported by the CLI parser from the help.
boost::nowide::cerr << std::endl;
this->print_help();
return false;
}
// Parse actions and transform options.
for (auto const &opt_key : opt_order) {
if (cli_actions_config_def.has(opt_key))
m_actions.emplace_back(opt_key);
else if (cli_transform_config_def.has(opt_key))
m_transforms.emplace_back(opt_key);
}
cli_config.apply(all_config, true);
set_data_dir(cli_config.datadir.value);
// Load the extra config values.
DynamicPrintConfig extra_config;
extra_config.apply(all_config, true);
// load config files supplied via --load
DynamicPrintConfig print_config;
for (const std::string &file : cli_config.load.values) {
if (! boost::filesystem::exists(file)) {
boost::nowide::cout << "No such file: " << file << std::endl;
exit(1);
}
DynamicPrintConfig c;
try {
c.load(file);
} catch (std::exception &e) {
boost::nowide::cout << "Error while reading config file: " << e.what() << std::endl;
exit(1);
}
c.normalize();
print_config.apply(c);
{
const ConfigOptionInt *opt_loglevel = m_config.opt<ConfigOptionInt>("loglevel");
if (opt_loglevel != 0)
set_logging_level(opt_loglevel->value);
}
if ((input_files.empty() || cli_config.gui.value) && ! cli_config.no_gui.value && ! cli_config.help.value && cli_config.save.value.empty()) {
#if 1
GUI::GUI_App *gui = new GUI::GUI_App();
GUI::GUI_App::SetInstance(gui);
gui->CallAfter([gui, &input_files, &cli_config, &extra_config, &print_config] {
if (! gui->initialized()) {
return;
}
#if 0
// Load the cummulative config over the currently active profiles.
//FIXME if multiple configs are loaded, only the last one will have an effect.
// We need to decide what to do about loading of separate presets (just print preset, just filament preset etc).
// As of now only the full configs are supported here.
if (! print_config.empty())
gui->mainframe->load_config(print_config);
#endif
if (! cli_config.load.values.empty())
// Load the last config to give it a name at the UI. The name of the preset may be later
// changed by loading an AMF or 3MF.
//FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config.
gui->mainframe->load_config_file(cli_config.load.values.back());
// If loading a 3MF file, the config is loaded from the last one.
gui->plater()->load_files(input_files, true, true);
if (! extra_config.empty())
gui->mainframe->load_config(extra_config);
});
return wxEntry(argc, argv);
#else
std::cout << "GUI support has not been built." << "\n";
return -1;
#endif
}
// Initialize with defaults.
for (const t_optiondef_map *options : { &cli_actions_config_def.options, &cli_transform_config_def.options, &cli_misc_config_def.options })
for (const std::pair<t_config_option_key, ConfigOptionDef> &optdef : *options)
m_config.optptr(optdef.first, true);
// apply command line options to a more specific DynamicPrintConfig which provides normalize()
// (command line options override --load files)
print_config.apply(extra_config, true);
// write config if requested
if (! cli_config.save.value.empty()) {
print_config.normalize();
print_config.save(cli_config.save.value);
}
set_data_dir(m_config.opt_string("datadir"));
if (cli_config.help) {
printUsage();
return 0;
}
// read input file(s) if any
std::vector<Model> models;
for (const t_config_option_key &file : input_files) {
if (! boost::filesystem::exists(file)) {
boost::nowide::cerr << "No such file: " << file << std::endl;
exit(1);
}
Model model;
try {
model = Model::read_from_file(file, &print_config, true);
} catch (std::exception &e) {
boost::nowide::cerr << file << ": " << e.what() << std::endl;
exit(1);
}
if (model.objects.empty()) {
boost::nowide::cerr << "Error: file is empty: " << file << std::endl;
continue;
}
model.add_default_instances();
// apply command line transform options
for (ModelObject* o : model.objects) {
/*
if (cli_config.scale_to_fit.is_positive_volume())
o->scale_to_fit(cli_config.scale_to_fit.value);
*/
// TODO: honor option order?
o->scale(cli_config.scale.value);
o->rotate(Geometry::deg2rad(cli_config.rotate_x.value), X);
o->rotate(Geometry::deg2rad(cli_config.rotate_y.value), Y);
o->rotate(Geometry::deg2rad(cli_config.rotate.value), Z);
}
// TODO: handle --merge
models.push_back(model);
}
for (Model &model : models) {
if (cli_config.info) {
// --info works on unrepaired model
model.print_info();
} else if (cli_config.export_3mf) {
std::string outfile = cli_config.output.value;
if (outfile.empty()) outfile = model.objects.front()->input_file;
// Check if the file is already a 3mf.
if(outfile.substr(outfile.find_last_of('.'), outfile.length()) == ".3mf")
outfile = outfile.substr(0, outfile.find_last_of('.')) + "_2" + ".3mf";
else
// Remove the previous extension and add .3mf extention.
outfile = outfile.substr(0, outfile.find_last_of('.')) + ".3mf";
store_3mf(outfile.c_str(), &model, nullptr);
boost::nowide::cout << "File file exported to " << outfile << std::endl;
} else if (cli_config.cut > 0) {
model.repair();
model.translate(0, 0, - model.bounding_box().min(2));
if (! model.objects.empty()) {
// XXX
// Model out;
// model.objects.front()->cut(cli_config.cut, &out);
// ModelObject &upper = *out.objects[0];
// ModelObject &lower = *out.objects[1];
// // Use the input name and trim off the extension.
// std::string outfile = cli_config.output.value;
// if (outfile.empty())
// outfile = model.objects.front()->input_file;
// outfile = outfile.substr(0, outfile.find_last_of('.'));
// std::cerr << outfile << "\n";
// if (upper.facets_count() > 0)
// upper.mesh().write_binary((outfile + "_upper.stl").c_str());
// if (lower.facets_count() > 0)
// lower.mesh().write_binary((outfile + "_lower.stl").c_str());
}
} else if (cli_config.slice) {
PrinterTechnology printer_technology = print_config.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology", true)->value;
std::string outfile = cli_config.output.value;
Print fff_print;
SLAPrint sla_print;
PrintBase *print = (printer_technology == ptFFF) ? static_cast<PrintBase*>(&fff_print) : static_cast<PrintBase*>(&sla_print);
if (! cli_config.dont_arrange) {
//FIXME make the min_object_distance configurable.
model.arrange_objects(fff_print.config().min_object_distance());
model.center_instances_around_point(cli_config.print_center);
}
if (printer_technology == ptFFF) {
for (auto* mo : model.objects)
fff_print.auto_assign_extruders(mo);
}
print_config.normalize();
print->apply(model, print_config);
std::string err = print->validate();
if (err.empty()) {
if (printer_technology == ptFFF) {
// The outfile is processed by a PlaceholderParser.
fff_print.export_gcode(outfile, nullptr);
} else {
assert(printer_technology == ptSLA);
//FIXME add the output here
}
} else
std::cerr << err << "\n";
} else {
boost::nowide::cerr << "error: command not supported" << std::endl;
return 1;
}
}
return 0;
return true;
}
void printUsage()
void CLI::print_help(bool include_print_options, PrinterTechnology printer_technology) const
{
std::cout << "Slic3r " << SLIC3R_VERSION << " is a STL-to-GCODE translator for RepRap 3D printers" << "\n"
<< "written by Alessandro Ranellucci <aar@cpan.org> - http://slic3r.org/ - https://github.com/slic3r/Slic3r" << "\n"
// << "Git Version " << BUILD_COMMIT << "\n\n"
<< "Usage: ./slic3r [ OPTIONS ] [ file.stl ] [ file2.stl ] ..." << "\n";
// CLI Options
std::cout << "** CLI OPTIONS **\n";
print_cli_options(boost::nowide::cout);
std::cout << "****\n";
// Print options
std::cout << "** PRINT OPTIONS **\n";
print_print_options(boost::nowide::cout);
std::cout << "****\n";
boost::nowide::cout
<< "Slic3r Prusa Edition " << SLIC3R_BUILD << std::endl
<< "https://github.com/prusa3d/Slic3r" << std::endl << std::endl
<< "Usage: slic3r [ ACTIONS ] [ TRANSFORM ] [ OPTIONS ] [ file.stl ... ]" << std::endl
<< std::endl
<< "Actions:" << std::endl;
cli_actions_config_def.print_cli_help(boost::nowide::cout, false);
boost::nowide::cout
<< std::endl
<< "Transform options:" << std::endl;
cli_transform_config_def.print_cli_help(boost::nowide::cout, false);
boost::nowide::cout
<< std::endl
<< "Other options:" << std::endl;
cli_misc_config_def.print_cli_help(boost::nowide::cout, false);
if (include_print_options) {
boost::nowide::cout << std::endl;
print_config_def.print_cli_help(boost::nowide::cout, true, [printer_technology](const ConfigOptionDef &def)
{ return printer_technology == ptAny || def.printer_technology == ptAny || printer_technology == def.printer_technology; });
} else {
boost::nowide::cout
<< std::endl
<< "Run --help-fff / --help-sla to see the full listing of print options." << std::endl;
}
}
bool CLI::export_models(IO::ExportFormat format)
{
for (Model &model : m_models) {
const std::string path = this->output_filepath(model, format);
bool success = false;
switch (format) {
case IO::AMF: success = Slic3r::store_amf(path.c_str(), &model, nullptr); break;
case IO::OBJ: success = Slic3r::store_obj(path.c_str(), &model); break;
case IO::STL: success = Slic3r::store_stl(path.c_str(), &model, true); break;
case IO::TMF: success = Slic3r::store_3mf(path.c_str(), &model, nullptr); break;
default: assert(false); break;
}
if (success)
std::cout << "File exported to " << path << std::endl;
else {
std::cerr << "File export to " << path << " failed" << std::endl;
return false;
}
}
return true;
}
std::string CLI::output_filepath(const Model &model, IO::ExportFormat format) const
{
std::string ext;
switch (format) {
case IO::AMF: ext = ".amf"; break;
case IO::OBJ: ext = ".obj"; break;
case IO::STL: ext = ".stl"; break;
case IO::TMF: ext = ".3mf"; break;
default: assert(false); break;
};
auto proposed_path = boost::filesystem::path(model.propose_export_file_name_and_path(ext));
// use --output when available
std::string cmdline_param = m_config.opt_string("output");
if (! cmdline_param.empty()) {
// if we were supplied a directory, use it and append our automatically generated filename
boost::filesystem::path cmdline_path(cmdline_param);
if (boost::filesystem::is_directory(cmdline_path))
proposed_path = cmdline_path / proposed_path.filename();
else
proposed_path = cmdline_path;
}
return proposed_path.string();
}
#ifdef _MSC_VER
@ -309,7 +641,12 @@ extern "C" {
for (size_t i = 0; i < argc; ++ i)
argv_ptrs[i] = const_cast<char*>(argv_narrow[i].data());
// Call the UTF8 main.
return slic3r_main_(argc, argv_ptrs.data());
return CLI().run(argc, argv_ptrs.data());
}
}
#else /* _MSC_VER */
int main(int argc, char **argv)
{
return CLI().run(argc, argv);
}
#endif /* _MSC_VER */

48
src/slic3r.hpp Normal file
View File

@ -0,0 +1,48 @@
#ifndef SLIC3R_HPP
#define SLIC3R_HPP
#include "libslic3r/Config.hpp"
#include "libslic3r/Model.hpp"
namespace Slic3r {
namespace IO {
enum ExportFormat : int {
AMF,
OBJ,
STL,
// SVG,
TMF,
Gcode
};
}
class CLI {
public:
int run(int argc, char **argv);
private:
DynamicPrintAndCLIConfig m_config;
DynamicPrintConfig m_print_config;
DynamicPrintConfig m_extra_config;
std::vector<std::string> m_input_files;
std::vector<std::string> m_actions;
std::vector<std::string> m_transforms;
std::vector<Model> m_models;
bool setup(int argc, char **argv);
/// Prints usage of the CLI.
void print_help(bool include_print_options = false, PrinterTechnology printer_technology = ptAny) const;
/// Exports loaded models to a file of the specified format, according to the options affecting output filename.
bool export_models(IO::ExportFormat format);
bool has_print_action() const { return m_config.opt_bool("export_gcode") || m_config.opt_bool("export_sla"); }
std::string output_filepath(const Model &model, IO::ExportFormat format) const;
};
}
#endif

View File

@ -28,8 +28,20 @@ set(SLIC3R_GUI_SOURCES
GUI/GLCanvas3D.cpp
GUI/GLCanvas3DManager.hpp
GUI/GLCanvas3DManager.cpp
GUI/GLGizmo.hpp
GUI/GLGizmo.cpp
GUI/Gizmos/GLGizmoBase.cpp
GUI/Gizmos/GLGizmoBase.hpp
GUI/Gizmos/GLGizmoMove.cpp
GUI/Gizmos/GLGizmoMove.hpp
GUI/Gizmos/GLGizmoRotate.cpp
GUI/Gizmos/GLGizmoRotate.hpp
GUI/Gizmos/GLGizmoScale.cpp
GUI/Gizmos/GLGizmoScale.hpp
GUI/Gizmos/GLGizmoSlaSupports.cpp
GUI/Gizmos/GLGizmoSlaSupports.hpp
GUI/Gizmos/GLGizmoFlatten.cpp
GUI/Gizmos/GLGizmoFlatten.hpp
GUI/Gizmos/GLGizmoCut.cpp
GUI/Gizmos/GLGizmoCut.hpp
GUI/GLTexture.hpp
GUI/GLTexture.cpp
GUI/GLToolbar.hpp
@ -76,6 +88,8 @@ set(SLIC3R_GUI_SOURCES
GUI/2DBed.hpp
GUI/3DBed.cpp
GUI/3DBed.hpp
GUI/Camera.cpp
GUI/Camera.hpp
GUI/wxExtensions.cpp
GUI/wxExtensions.hpp
GUI/WipeTowerDialog.cpp

View File

@ -834,6 +834,8 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
ModelInstance::EPrintVolumeState state = ModelInstance::PVS_Inside;
bool all_contained = true;
bool contained_min_one = false;
for (GLVolume* volume : this->volumes)
{
if ((volume == nullptr) || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || ((volume->composite_id.volume_id < 0) && !volume->shader_outside_printer_detection_enabled))
@ -843,6 +845,9 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
bool contained = print_volume.contains(bb);
all_contained &= contained;
if (contained)
contained_min_one = true;
volume->is_outside = !contained;
if ((state == ModelInstance::PVS_Inside) && volume->is_outside)
@ -855,7 +860,7 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
if (out_state != nullptr)
*out_state = state;
return all_contained;
return /*all_contained*/ contained_min_one; // #ys_FIXME_delete_after_testing
}
void GLVolumeCollection::reset_outside_state()
@ -2002,9 +2007,9 @@ std::string _3DScene::get_gl_info(bool format_as_html, bool extensions)
return s_canvas_mgr.get_gl_info(format_as_html, extensions);
}
bool _3DScene::add_canvas(wxGLCanvas* canvas)
bool _3DScene::add_canvas(wxGLCanvas* canvas, GUI::Bed3D& bed, GUI::Camera& camera, GUI::GLToolbar& view_toolbar)
{
return s_canvas_mgr.add(canvas);
return s_canvas_mgr.add(canvas, bed, camera, view_toolbar);
}
bool _3DScene::remove_canvas(wxGLCanvas* canvas)

View File

@ -25,6 +25,11 @@ inline void glAssertRecentCall() { }
#endif
namespace Slic3r {
namespace GUI {
class Bed3D;
struct Camera;
class GLToolbar;
} // namespace GUI
class Print;
class PrintObject;
@ -359,7 +364,7 @@ public:
void set_volume_rotation(const Vec3d& rotation) { m_volume_transformation.set_rotation(rotation); set_bounding_boxes_as_dirty(); }
void set_volume_rotation(Axis axis, double rotation) { m_volume_transformation.set_rotation(axis, rotation); set_bounding_boxes_as_dirty(); }
Vec3d get_volume_scaling_factor() const { return m_volume_transformation.get_scaling_factor(); }
const Vec3d& get_volume_scaling_factor() const { return m_volume_transformation.get_scaling_factor(); }
double get_volume_scaling_factor(Axis axis) const { return m_volume_transformation.get_scaling_factor(axis); }
void set_volume_scaling_factor(const Vec3d& scaling_factor) { m_volume_transformation.set_scaling_factor(scaling_factor); set_bounding_boxes_as_dirty(); }
@ -563,7 +568,7 @@ class _3DScene
public:
static std::string get_gl_info(bool format_as_html, bool extensions);
static bool add_canvas(wxGLCanvas* canvas);
static bool add_canvas(wxGLCanvas* canvas, GUI::Bed3D& bed, GUI::Camera& camera, GUI::GLToolbar& view_toolbar);
static bool remove_canvas(wxGLCanvas* canvas);
static void remove_all_canvases();

View File

@ -123,7 +123,7 @@ public:
// This "finished" flag does not account for the final export of the output file (.gcode or zipped PNGs),
// and it does not account for the OctoPrint scheduling.
bool finished() const { return m_print->finished(); }
private:
void thread_proc();
void thread_proc_safe();

View File

@ -10,8 +10,10 @@
#include <wx/listctrl.h>
#include <wx/stattext.h>
#include <wx/timer.h>
#include <wx/wupdlock.h>
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/Utils/Bonjour.hpp"
@ -49,31 +51,36 @@ struct LifetimeGuard
LifetimeGuard(BonjourDialog *dialog) : dialog(dialog) {}
};
BonjourDialog::BonjourDialog(wxWindow *parent) :
wxDialog(parent, wxID_ANY, _(L("Network lookup"))),
list(new wxListView(this, wxID_ANY, wxDefaultPosition, wxSize(800, 300))),
replies(new ReplySet),
label(new wxStaticText(this, wxID_ANY, "")),
timer(new wxTimer()),
timer_state(0)
BonjourDialog::BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology tech)
: wxDialog(parent, wxID_ANY, _(L("Network lookup")), wxDefaultPosition, wxDefaultSize, wxRESIZE_BORDER)
, list(new wxListView(this, wxID_ANY))
, replies(new ReplySet)
, label(new wxStaticText(this, wxID_ANY, ""))
, timer(new wxTimer())
, timer_state(0)
, tech(tech)
{
const int em = GUI::wxGetApp().em_unit();
list->SetMinSize(wxSize(80 * em, 30 * em));
wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);
vsizer->Add(label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10);
vsizer->Add(label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, em);
list->SetSingleStyle(wxLC_SINGLE_SEL);
list->SetSingleStyle(wxLC_SORT_DESCENDING);
list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 50);
list->AppendColumn(_(L("Hostname")), wxLIST_FORMAT_LEFT, 100);
list->AppendColumn(_(L("Service name")), wxLIST_FORMAT_LEFT, 200);
list->AppendColumn(_(L("OctoPrint version")), wxLIST_FORMAT_LEFT, 50);
list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 5 * em);
list->AppendColumn(_(L("Hostname")), wxLIST_FORMAT_LEFT, 10 * em);
list->AppendColumn(_(L("Service name")), wxLIST_FORMAT_LEFT, 20 * em);
if (tech == ptFFF) {
list->AppendColumn(_(L("OctoPrint version")), wxLIST_FORMAT_LEFT, 5 * em);
}
vsizer->Add(list, 1, wxEXPAND | wxALL, 10);
vsizer->Add(list, 1, wxEXPAND | wxALL, em);
wxBoxSizer *button_sizer = new wxBoxSizer(wxHORIZONTAL);
button_sizer->Add(new wxButton(this, wxID_OK, "OK"), 0, wxALL, 10);
button_sizer->Add(new wxButton(this, wxID_CANCEL, "Cancel"), 0, wxALL, 10);
button_sizer->Add(new wxButton(this, wxID_OK, "OK"), 0, wxALL, em);
button_sizer->Add(new wxButton(this, wxID_CANCEL, "Cancel"), 0, wxALL, em);
// ^ Note: The Ok/Cancel labels are translated by wxWidgets
vsizer->Add(button_sizer, 0, wxALIGN_CENTER);
@ -110,7 +117,11 @@ bool BonjourDialog::show_and_lookup()
// so that both threads can access it safely.
auto dguard = std::make_shared<LifetimeGuard>(this);
// Note: More can be done here when we support discovery of hosts other than Octoprint and SL1
Bonjour::TxtKeys txt_keys { "version", "model" };
bonjour = std::move(Bonjour("octoprint")
.set_txt_keys(std::move(txt_keys))
.set_retries(3)
.set_timeout(4)
.on_reply([dguard](BonjourReply &&reply) {
@ -157,9 +168,20 @@ void BonjourDialog::on_reply(BonjourReplyEvent &e)
return;
}
// Filter replies based on selected technology
const auto model = e.reply.txt_data.find("model");
const bool sl1 = model != e.reply.txt_data.end() && model->second == "SL1";
if (tech == ptFFF && sl1 || tech == ptSLA && !sl1) {
return;
}
replies->insert(std::move(e.reply));
auto selected = get_selected();
wxWindowUpdateLocker freeze_guard(this);
(void)freeze_guard;
list->DeleteAllItems();
// The whole list is recreated so that we benefit from it already being sorted in the set.
@ -168,12 +190,20 @@ void BonjourDialog::on_reply(BonjourReplyEvent &e)
auto item = list->InsertItem(0, reply.full_address);
list->SetItem(item, 1, reply.hostname);
list->SetItem(item, 2, reply.service_name);
list->SetItem(item, 3, reply.version);
if (tech == ptFFF) {
const auto it = reply.txt_data.find("version");
if (it != reply.txt_data.end()) {
list->SetItem(item, 3, GUI::from_u8(it->second));
}
}
}
for (int i = 0; i < 4; i++) {
this->list->SetColumnWidth(i, wxLIST_AUTOSIZE);
if (this->list->GetColumnWidth(i) < 100) { this->list->SetColumnWidth(i, 100); }
const int em = GUI::wxGetApp().em_unit();
for (int i = 0; i < list->GetColumnCount(); i++) {
list->SetColumnWidth(i, wxLIST_AUTOSIZE);
if (list->GetColumnWidth(i) < 10 * em) { list->SetColumnWidth(i, 10 * em); }
}
if (!selected.IsEmpty()) {

View File

@ -5,6 +5,8 @@
#include <wx/dialog.h>
#include "libslic3r/PrintConfig.hpp"
class wxListView;
class wxStaticText;
class wxTimer;
@ -21,7 +23,7 @@ class ReplySet;
class BonjourDialog: public wxDialog
{
public:
BonjourDialog(wxWindow *parent);
BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology);
BonjourDialog(BonjourDialog &&) = delete;
BonjourDialog(const BonjourDialog &) = delete;
BonjourDialog &operator=(BonjourDialog &&) = delete;
@ -37,6 +39,7 @@ private:
std::shared_ptr<Bonjour> bonjour;
std::unique_ptr<wxTimer> timer;
unsigned timer_state;
Slic3r::PrinterTechnology tech;
void on_reply(BonjourReplyEvent &);
void on_timer(wxTimerEvent &);

62
src/slic3r/GUI/Camera.cpp Normal file
View File

@ -0,0 +1,62 @@
#include "libslic3r/libslic3r.h"
#include "Camera.hpp"
static const float GIMBALL_LOCK_THETA_MAX = 180.0f;
namespace Slic3r {
namespace GUI {
Camera::Camera()
: type(Ortho)
, zoom(1.0f)
, phi(45.0f)
// , distance(0.0f)
, requires_zoom_to_bed(false)
, m_theta(45.0f)
, m_target(Vec3d::Zero())
{
}
std::string Camera::get_type_as_string() const
{
switch (type)
{
default:
case Unknown:
return "unknown";
// case Perspective:
// return "perspective";
case Ortho:
return "ortho";
};
}
void Camera::set_target(const Vec3d& target)
{
m_target = target;
m_target(0) = clamp(m_scene_box.min(0), m_scene_box.max(0), m_target(0));
m_target(1) = clamp(m_scene_box.min(1), m_scene_box.max(1), m_target(1));
m_target(2) = clamp(m_scene_box.min(2), m_scene_box.max(2), m_target(2));
}
void Camera::set_theta(float theta, bool apply_limit)
{
if (apply_limit)
m_theta = clamp(0.0f, GIMBALL_LOCK_THETA_MAX, theta);
else
{
m_theta = fmod(theta, 360.0f);
if (m_theta < 0.0f)
m_theta += 360.0f;
}
}
void Camera::set_scene_box(const BoundingBoxf3& box)
{
m_scene_box = box;
}
} // GUI
} // Slic3r

50
src/slic3r/GUI/Camera.hpp Normal file
View File

@ -0,0 +1,50 @@
#ifndef slic3r_Camera_hpp_
#define slic3r_Camera_hpp_
#include "libslic3r/BoundingBox.hpp"
namespace Slic3r {
namespace GUI {
struct Camera
{
enum EType : unsigned char
{
Unknown,
// Perspective,
Ortho,
Num_types
};
EType type;
float zoom;
float phi;
// float distance;
bool requires_zoom_to_bed;
private:
Vec3d m_target;
float m_theta;
BoundingBoxf3 m_scene_box;
public:
Camera();
std::string get_type_as_string() const;
const Vec3d& get_target() const { return m_target; }
void set_target(const Vec3d& target);
float get_theta() const { return m_theta; }
void set_theta(float theta, bool apply_limit);
const BoundingBoxf3& get_scene_box() const { return m_scene_box; }
void set_scene_box(const BoundingBoxf3& box);
};
} // GUI
} // Slic3r
#endif // slic3r_Camera_hpp_

View File

@ -38,7 +38,7 @@ static wxString generate_html_row(const Config::Snapshot &snapshot, bool row_eve
text += wxString("<font size=\"5\"><b>") + (snapshot_active ? _(L("Active: ")) : "") +
Utils::format_local_date_time(snapshot.time_captured) + ": " + format_reason(snapshot.reason);
if (! snapshot.comment.empty())
text += " (" + snapshot.comment + ")";
text += " (" + wxString::FromUTF8(snapshot.comment.data()) + ")";
text += "</b></font><br>";
// End of row header.
text += _(L("slic3r version")) + ": " + snapshot.slic3r_version_captured.to_string() + "<br>";

View File

@ -37,7 +37,9 @@ void Field::PostInitialize()
m_Undo_to_sys_btn = new MyButton(m_parent, wxID_ANY, "", wxDefaultPosition,wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER);
if (wxMSW) {
m_Undo_btn->SetBackgroundColour(color);
m_Undo_btn->SetBackgroundStyle(wxBG_STYLE_PAINT);
m_Undo_to_sys_btn->SetBackgroundColour(color);
m_Undo_to_sys_btn->SetBackgroundStyle(wxBG_STYLE_PAINT);
}
m_Undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) { on_back_to_initial_value(); }));
m_Undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) { on_back_to_sys_value(); }));
@ -258,6 +260,12 @@ void TextCtrl::BUILD() {
const long style = m_opt.multiline ? wxTE_MULTILINE : wxTE_PROCESS_ENTER/*0*/;
auto temp = new wxTextCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, style);
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
if (! m_opt.multiline)
// Only disable background refresh for single line input fields, as they are completely painted over by the edit control.
// This does not apply to the multi-line edit field, where the last line and a narrow frame around the text is not cleared.
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
#ifdef __WXOSX__
temp->OSXDisableAllSmartSubstitutions();
#endif // __WXOSX__
@ -373,6 +381,8 @@ void CheckBox::BUILD() {
false;
auto temp = new wxCheckBox(m_parent, wxID_ANY, wxString(""), wxDefaultPosition, size);
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
temp->SetValue(check_value);
if (m_opt.readonly) temp->Disable();
@ -430,6 +440,8 @@ void SpinCtrl::BUILD() {
auto temp = new wxSpinCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size,
0|wxTE_PROCESS_ENTER, min_val, max_val, default_value);
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
#ifndef __WXOSX__
// #ys_FIXME_KILL_FOCUS
@ -505,6 +517,8 @@ void Choice::BUILD() {
}
else
temp = new wxBitmapComboBox(m_parent, wxID_ANY, wxString(""), wxDefaultPosition, size, 0, nullptr, wxCB_READONLY);
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
// recast as a wxWindow to fit the calling convention
window = dynamic_cast<wxWindow*>(temp);
@ -784,6 +798,7 @@ void ColourPicker::BUILD()
}
auto temp = new wxColourPickerCtrl(m_parent, wxID_ANY, clr, wxDefaultPosition, size);
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
// // recast as a wxWindow to fit the calling convention
window = dynamic_cast<wxWindow*>(temp);
@ -818,10 +833,21 @@ void PointCtrl::BUILD()
x_textctrl = new wxTextCtrl(m_parent, wxID_ANY, X, wxDefaultPosition, field_size, wxTE_PROCESS_ENTER);
y_textctrl = new wxTextCtrl(m_parent, wxID_ANY, Y, wxDefaultPosition, field_size, wxTE_PROCESS_ENTER);
x_textctrl->SetFont(Slic3r::GUI::wxGetApp().normal_font());
x_textctrl->SetBackgroundStyle(wxBG_STYLE_PAINT);
y_textctrl->SetFont(Slic3r::GUI::wxGetApp().normal_font());
y_textctrl->SetBackgroundStyle(wxBG_STYLE_PAINT);
temp->Add(new wxStaticText(m_parent, wxID_ANY, "x : "), 0, wxALIGN_CENTER_VERTICAL, 0);
auto static_text_x = new wxStaticText(m_parent, wxID_ANY, "x : ");
auto static_text_y = new wxStaticText(m_parent, wxID_ANY, " y : ");
static_text_x->SetFont(Slic3r::GUI::wxGetApp().normal_font());
static_text_x->SetBackgroundStyle(wxBG_STYLE_PAINT);
static_text_y->SetFont(Slic3r::GUI::wxGetApp().normal_font());
static_text_y->SetBackgroundStyle(wxBG_STYLE_PAINT);
temp->Add(static_text_x, 0, wxALIGN_CENTER_VERTICAL, 0);
temp->Add(x_textctrl);
temp->Add(new wxStaticText(m_parent, wxID_ANY, " y : "), 0, wxALIGN_CENTER_VERTICAL, 0);
temp->Add(static_text_y, 0, wxALIGN_CENTER_VERTICAL, 0);
temp->Add(y_textctrl);
// x_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), x_textctrl->GetId());
@ -890,6 +916,8 @@ void StaticText::BUILD()
const wxString legend(static_cast<const ConfigOptionString*>(m_opt.default_value)->value);
auto temp = new wxStaticText(m_parent, wxID_ANY, legend, wxDefaultPosition, size, wxST_ELLIPSIZE_MIDDLE);
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
temp->SetFont(wxGetApp().bold_font());
// // recast as a wxWindow to fit the calling convention
@ -913,10 +941,14 @@ void SliderCtrl::BUILD()
m_slider = new wxSlider(m_parent, wxID_ANY, def_val * m_scale,
min * m_scale, max * m_scale,
wxDefaultPosition, size);
m_slider->SetFont(Slic3r::GUI::wxGetApp().normal_font());
m_slider->SetBackgroundStyle(wxBG_STYLE_PAINT);
wxSize field_size(40, -1);
m_textctrl = new wxTextCtrl(m_parent, wxID_ANY, wxString::Format("%d", m_slider->GetValue()/m_scale),
wxDefaultPosition, field_size);
m_textctrl->SetFont(Slic3r::GUI::wxGetApp().normal_font());
m_textctrl->SetBackgroundStyle(wxBG_STYLE_PAINT);
temp->Add(m_slider, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL, 0);
temp->Add(m_textctrl, 0, wxALIGN_CENTER_VERTICAL, 0);

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,7 @@
#include "GLToolbar.hpp"
#include "Event.hpp"
#include "3DBed.hpp"
#include "Camera.hpp"
#include <float.h>
@ -100,7 +101,6 @@ template <size_t N> using Vec3dsEvent = ArrayEvent<Vec3d, N>;
wxDECLARE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_VIEWPORT_CHANGED, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent);
@ -162,41 +162,6 @@ class GLCanvas3D
void reset() { first_volumes.clear(); }
};
struct Camera
{
enum EType : unsigned char
{
Unknown,
// Perspective,
Ortho,
Num_types
};
EType type;
float zoom;
float phi;
// float distance;
private:
Vec3d m_target;
BoundingBoxf3 m_scene_box;
float m_theta;
public:
Camera();
std::string get_type_as_string() const;
float get_theta() const { return m_theta; }
void set_theta(float theta, bool apply_limit);
const Vec3d& get_target() const { return m_target; }
void set_target(const Vec3d& target, GLCanvas3D& canvas);
const BoundingBoxf3& get_scene_box() const { return m_scene_box; }
void set_scene_box(const BoundingBoxf3& box, GLCanvas3D& canvas);
};
#if !ENABLE_TEXTURES_FROM_SVG
class Shader
{
@ -785,10 +750,6 @@ private:
void render_overlay(const GLCanvas3D& canvas, const Selection& selection) const;
#if !ENABLE_IMGUI
void create_external_gizmo_widgets(wxWindow *parent);
#endif // not ENABLE_IMGUI
private:
void reset();
@ -829,7 +790,8 @@ private:
enum Warning {
ObjectOutside,
ToolpathOutside,
SomethingNotShown
SomethingNotShown,
ObjectClashed
};
// Sets a warning of the given type to be active/inactive. If several warnings are active simultaneously,
@ -848,7 +810,7 @@ private:
std::vector<Warning> m_warnings;
// Generates the texture with given text.
bool _generate(const std::string& msg, const GLCanvas3D& canvas);
bool _generate(const std::string& msg, const GLCanvas3D& canvas, const bool red_colored = false);
};
class LegendTexture : public GUI::GLTexture
@ -885,14 +847,14 @@ private:
LegendTexture m_legend_texture;
WarningTexture m_warning_texture;
wxTimer m_timer;
Camera m_camera;
Bed3D* m_bed;
Bed3D& m_bed;
Camera& m_camera;
GLToolbar& m_view_toolbar;
LayersEditing m_layers_editing;
Shader m_shader;
Mouse m_mouse;
mutable Gizmos m_gizmos;
mutable GLToolbar m_toolbar;
GLToolbar* m_view_toolbar;
ClippingPlane m_clipping_planes[2];
bool m_use_clipping_planes;
mutable SlaCap m_sla_caps[2];
@ -908,7 +870,6 @@ private:
bool m_dirty;
bool m_initialized;
bool m_use_VBOs;
bool m_requires_zoom_to_bed;
bool m_apply_zoom_to_volumes_filter;
mutable int m_hover_volume_id;
bool m_toolbar_action_running;
@ -929,12 +890,8 @@ private:
GCodePreviewVolumeIndex m_gcode_preview_volume_index;
#if !ENABLE_IMGUI
wxWindow *m_external_gizmo_widgets_parent;
#endif // not ENABLE_IMGUI
public:
GLCanvas3D(wxGLCanvas* canvas);
GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar);
~GLCanvas3D();
void set_context(wxGLContext* context) { m_context = context; }
@ -942,10 +899,6 @@ public:
wxGLCanvas* get_wxglcanvas() { return m_canvas; }
const wxGLCanvas* get_wxglcanvas() const { return m_canvas; }
void set_bed(Bed3D* bed) { m_bed = bed; }
void set_view_toolbar(GLToolbar* toolbar) { m_view_toolbar = toolbar; }
bool init(bool useVBOs, bool use_legacy_opengl);
void post_event(wxEvent &&event);
@ -1005,17 +958,11 @@ public:
void zoom_to_volumes();
void zoom_to_selection();
void select_view(const std::string& direction);
void set_viewport_from_scene(const GLCanvas3D& other);
void update_volumes_colors_by_extruder();
void update_toolbar_items_visibility();
#if !ENABLE_IMGUI
Rect get_gizmo_reset_rect(const GLCanvas3D& canvas, bool viewport) const;
bool gizmo_reset_rect_contains(const GLCanvas3D& canvas, float x, float y) const;
#endif // not ENABLE_IMGUI
bool is_dragging() const { return m_gizmos.is_dragging() || m_moving; }
void render();
@ -1056,10 +1003,6 @@ public:
void set_tooltip(const std::string& tooltip) const;
#if !ENABLE_IMGUI
void set_external_gizmo_widgets_parent(wxWindow *parent);
#endif // not ENABLE_IMGUI
void do_move();
void do_rotate();
void do_scale();
@ -1070,8 +1013,6 @@ public:
void update_gizmos_on_off_state();
void viewport_changed();
void handle_sidebar_focus_event(const std::string& opt_key, bool focus_on);
void update_ui_from_settings();

View File

@ -129,7 +129,7 @@ GLCanvas3DManager::~GLCanvas3DManager()
}
}
bool GLCanvas3DManager::add(wxGLCanvas* canvas)
bool GLCanvas3DManager::add(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar)
{
if (canvas == nullptr)
return false;
@ -137,7 +137,7 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas)
if (_get_canvas(canvas) != m_canvases.end())
return false;
GLCanvas3D* canvas3D = new GLCanvas3D(canvas);
GLCanvas3D* canvas3D = new GLCanvas3D(canvas, bed, camera, view_toolbar);
if (canvas3D == nullptr)
return false;

View File

@ -23,6 +23,9 @@ class PrintObject;
namespace GUI {
class GLCanvas3D;
class Bed3D;
class GLToolbar;
struct Camera;
class GLCanvas3DManager
{
@ -62,7 +65,7 @@ public:
GLCanvas3DManager();
~GLCanvas3DManager();
bool add(wxGLCanvas* canvas);
bool add(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar);
bool remove(wxGLCanvas* canvas);
void remove_all();

File diff suppressed because it is too large Load Diff

View File

@ -1,629 +0,0 @@
#ifndef slic3r_GLGizmo_hpp_
#define slic3r_GLGizmo_hpp_
#include <igl/AABB.h>
#include "../../slic3r/GUI/GLTexture.hpp"
#include "../../slic3r/GUI/GLCanvas3D.hpp"
#include "libslic3r/Point.hpp"
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/SLA/SLAAutoSupports.hpp"
#include <array>
#include <vector>
#include <memory>
class wxWindow;
class GLUquadric;
typedef class GLUquadric GLUquadricObj;
namespace Slic3r {
class BoundingBoxf3;
class Linef3;
class ModelObject;
namespace GUI {
class GLCanvas3D;
#if ENABLE_IMGUI
class ImGuiWrapper;
#endif // ENABLE_IMGUI
class GLGizmoBase
{
protected:
struct Grabber
{
static const float SizeFactor;
static const float MinHalfSize;
static const float DraggingScaleFactor;
Vec3d center;
Vec3d angles;
float color[3];
bool enabled;
bool dragging;
Grabber();
void render(bool hover, float size) const;
void render_for_picking(float size) const { render(size, color, false); }
float get_half_size(float size) const;
float get_dragging_half_size(float size) const;
private:
void render(float size, const float* render_color, bool use_lighting) const;
void render_face(float half_size) const;
};
public:
enum EState
{
Off,
Hover,
On,
Num_States
};
struct UpdateData
{
const Linef3 mouse_ray;
const Point* mouse_pos;
bool shift_down;
UpdateData(const Linef3& mouse_ray, const Point* mouse_pos = nullptr, bool shift_down = false)
: mouse_ray(mouse_ray), mouse_pos(mouse_pos), shift_down(shift_down)
{}
};
protected:
GLCanvas3D& m_parent;
int m_group_id;
EState m_state;
int m_shortcut_key;
#if ENABLE_SVG_ICONS
std::string m_icon_filename;
#endif // ENABLE_SVG_ICONS
unsigned int m_sprite_id;
int m_hover_id;
bool m_dragging;
float m_base_color[3];
float m_drag_color[3];
float m_highlight_color[3];
mutable std::vector<Grabber> m_grabbers;
#if ENABLE_IMGUI
ImGuiWrapper* m_imgui;
#endif // ENABLE_IMGUI
public:
#if ENABLE_SVG_ICONS
GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoBase(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
virtual ~GLGizmoBase() {}
bool init() { return on_init(); }
std::string get_name() const { return on_get_name(); }
int get_group_id() const { return m_group_id; }
void set_group_id(int id) { m_group_id = id; }
EState get_state() const { return m_state; }
void set_state(EState state) { m_state = state; on_set_state(); }
int get_shortcut_key() const { return m_shortcut_key; }
void set_shortcut_key(int key) { m_shortcut_key = key; }
#if ENABLE_SVG_ICONS
const std::string& get_icon_filename() const { return m_icon_filename; }
#endif // ENABLE_SVG_ICONS
bool is_activable(const GLCanvas3D::Selection& selection) const { return on_is_activable(selection); }
bool is_selectable() const { return on_is_selectable(); }
unsigned int get_sprite_id() const { return m_sprite_id; }
int get_hover_id() const { return m_hover_id; }
void set_hover_id(int id);
void set_highlight_color(const float* color);
void enable_grabber(unsigned int id);
void disable_grabber(unsigned int id);
void start_dragging(const GLCanvas3D::Selection& selection);
void stop_dragging();
bool is_dragging() const { return m_dragging; }
void update(const UpdateData& data, const GLCanvas3D::Selection& selection);
void render(const GLCanvas3D::Selection& selection) const { on_render(selection); }
void render_for_picking(const GLCanvas3D::Selection& selection) const { on_render_for_picking(selection); }
#if !ENABLE_IMGUI
virtual void create_external_gizmo_widgets(wxWindow *parent);
#endif // not ENABLE_IMGUI
#if ENABLE_IMGUI
void render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection) { on_render_input_window(x, y, bottom_limit, selection); }
#endif // ENABLE_IMGUI
protected:
virtual bool on_init() = 0;
virtual std::string on_get_name() const = 0;
virtual void on_set_state() {}
virtual void on_set_hover_id() {}
virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return true; }
virtual bool on_is_selectable() const { return true; }
virtual void on_enable_grabber(unsigned int id) {}
virtual void on_disable_grabber(unsigned int id) {}
virtual void on_start_dragging(const GLCanvas3D::Selection& selection) {}
virtual void on_stop_dragging() {}
virtual void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection) = 0;
virtual void on_render(const GLCanvas3D::Selection& selection) const = 0;
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const = 0;
#if ENABLE_IMGUI
virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection) {}
#endif // ENABLE_IMGUI
float picking_color_component(unsigned int id) const;
void render_grabbers(const BoundingBoxf3& box) const;
void render_grabbers(float size) const;
void render_grabbers_for_picking(const BoundingBoxf3& box) const;
void set_tooltip(const std::string& tooltip) const;
std::string format(float value, unsigned int decimals) const;
};
class GLGizmoRotate : public GLGizmoBase
{
static const float Offset;
static const unsigned int CircleResolution;
static const unsigned int AngleResolution;
static const unsigned int ScaleStepsCount;
static const float ScaleStepRad;
static const unsigned int ScaleLongEvery;
static const float ScaleLongTooth;
static const unsigned int SnapRegionsCount;
static const float GrabberOffset;
public:
enum Axis : unsigned char
{
X,
Y,
Z
};
private:
Axis m_axis;
double m_angle;
GLUquadricObj* m_quadric;
mutable Vec3d m_center;
mutable float m_radius;
mutable float m_snap_coarse_in_radius;
mutable float m_snap_coarse_out_radius;
mutable float m_snap_fine_in_radius;
mutable float m_snap_fine_out_radius;
public:
GLGizmoRotate(GLCanvas3D& parent, Axis axis);
GLGizmoRotate(const GLGizmoRotate& other);
virtual ~GLGizmoRotate();
double get_angle() const { return m_angle; }
void set_angle(double angle);
protected:
virtual bool on_init();
virtual std::string on_get_name() const { return ""; }
virtual void on_start_dragging(const GLCanvas3D::Selection& selection);
virtual void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection);
virtual void on_render(const GLCanvas3D::Selection& selection) const;
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const;
private:
void render_circle() const;
void render_scale() const;
void render_snap_radii() const;
void render_reference_radius() const;
void render_angle() const;
void render_grabber(const BoundingBoxf3& box) const;
void render_grabber_extension(const BoundingBoxf3& box, bool picking) const;
void transform_to_local(const GLCanvas3D::Selection& selection) const;
// returns the intersection of the mouse ray with the plane perpendicular to the gizmo axis, in local coordinate
Vec3d mouse_position_in_local_plane(const Linef3& mouse_ray, const GLCanvas3D::Selection& selection) const;
};
class GLGizmoRotate3D : public GLGizmoBase
{
std::vector<GLGizmoRotate> m_gizmos;
public:
#if ENABLE_SVG_ICONS
GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoRotate3D(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); }
void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); }
protected:
virtual bool on_init();
virtual std::string on_get_name() const;
virtual void on_set_state()
{
for (GLGizmoRotate& g : m_gizmos)
{
g.set_state(m_state);
}
}
virtual void on_set_hover_id()
{
for (unsigned int i = 0; i < 3; ++i)
{
m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1);
}
}
virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return !selection.is_wipe_tower(); }
virtual void on_enable_grabber(unsigned int id)
{
if ((0 <= id) && (id < 3))
m_gizmos[id].enable_grabber(0);
}
virtual void on_disable_grabber(unsigned int id)
{
if ((0 <= id) && (id < 3))
m_gizmos[id].disable_grabber(0);
}
virtual void on_start_dragging(const GLCanvas3D::Selection& selection);
virtual void on_stop_dragging();
virtual void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection)
{
for (GLGizmoRotate& g : m_gizmos)
{
g.update(data, selection);
}
}
virtual void on_render(const GLCanvas3D::Selection& selection) const;
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const
{
for (const GLGizmoRotate& g : m_gizmos)
{
g.render_for_picking(selection);
}
}
#if ENABLE_IMGUI
virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection);
#endif // ENABLE_IMGUI
};
class GLGizmoScale3D : public GLGizmoBase
{
static const float Offset;
mutable BoundingBoxf3 m_box;
Vec3d m_scale;
double m_snap_step;
Vec3d m_starting_scale;
Vec3d m_starting_drag_position;
BoundingBoxf3 m_starting_box;
public:
#if ENABLE_SVG_ICONS
GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoScale3D(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
double get_snap_step(double step) const { return m_snap_step; }
void set_snap_step(double step) { m_snap_step = step; }
const Vec3d& get_scale() const { return m_scale; }
void set_scale(const Vec3d& scale) { m_starting_scale = scale; m_scale = scale; }
protected:
virtual bool on_init();
virtual std::string on_get_name() const;
virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return !selection.is_wipe_tower(); }
virtual void on_start_dragging(const GLCanvas3D::Selection& selection);
virtual void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection);
virtual void on_render(const GLCanvas3D::Selection& selection) const;
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const;
#if ENABLE_IMGUI
virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection);
#endif // ENABLE_IMGUI
private:
void render_grabbers_connection(unsigned int id_1, unsigned int id_2) const;
void do_scale_x(const UpdateData& data);
void do_scale_y(const UpdateData& data);
void do_scale_z(const UpdateData& data);
void do_scale_uniform(const UpdateData& data);
double calc_ratio(const UpdateData& data) const;
};
class GLGizmoMove3D : public GLGizmoBase
{
static const double Offset;
Vec3d m_displacement;
double m_snap_step;
Vec3d m_starting_drag_position;
Vec3d m_starting_box_center;
Vec3d m_starting_box_bottom_center;
GLUquadricObj* m_quadric;
public:
#if ENABLE_SVG_ICONS
GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoMove3D(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
virtual ~GLGizmoMove3D();
double get_snap_step(double step) const { return m_snap_step; }
void set_snap_step(double step) { m_snap_step = step; }
const Vec3d& get_displacement() const { return m_displacement; }
protected:
virtual bool on_init();
virtual std::string on_get_name() const;
virtual void on_start_dragging(const GLCanvas3D::Selection& selection);
virtual void on_stop_dragging();
virtual void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection);
virtual void on_render(const GLCanvas3D::Selection& selection) const;
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const;
#if ENABLE_IMGUI
virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection);
#endif // ENABLE_IMGUI
private:
double calc_projection(const UpdateData& data) const;
void render_grabber_extension(Axis axis, const BoundingBoxf3& box, bool picking) const;
};
class GLGizmoFlatten : public GLGizmoBase
{
// This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself.
private:
mutable Vec3d m_normal;
struct PlaneData {
std::vector<Vec3d> vertices;
Vec3d normal;
float area;
};
// This holds information to decide whether recalculation is necessary:
std::vector<Transform3d> m_volumes_matrices;
std::vector<ModelVolumeType> m_volumes_types;
Vec3d m_first_instance_scale;
Vec3d m_first_instance_mirror;
std::vector<PlaneData> m_planes;
bool m_planes_valid = false;
mutable Vec3d m_starting_center;
const ModelObject* m_model_object = nullptr;
std::vector<const Transform3d*> instances_matrices;
void update_planes();
bool is_plane_update_necessary() const;
public:
#if ENABLE_SVG_ICONS
GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoFlatten(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
void set_flattening_data(const ModelObject* model_object);
Vec3d get_flattening_normal() const;
protected:
virtual bool on_init();
virtual std::string on_get_name() const;
virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const;
virtual void on_start_dragging(const GLCanvas3D::Selection& selection);
virtual void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection) {}
virtual void on_render(const GLCanvas3D::Selection& selection) const;
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const;
virtual void on_set_state()
{
if (m_state == On && is_plane_update_necessary())
update_planes();
}
};
#define SLAGIZMO_IMGUI_MODAL 0
class GLGizmoSlaSupports : public GLGizmoBase
{
private:
ModelObject* m_model_object = nullptr;
ModelObject* m_old_model_object = nullptr;
int m_active_instance = -1;
int m_old_instance_id = -1;
Vec3f unproject_on_mesh(const Vec2d& mouse_pos);
const float RenderPointScale = 1.f;
GLUquadricObj* m_quadric;
Eigen::MatrixXf m_V; // vertices
Eigen::MatrixXi m_F; // facets indices
igl::AABB<Eigen::MatrixXf,3> m_AABB;
struct SourceDataSummary {
Geometry::Transformation transformation;
};
// This holds information to decide whether recalculation is necessary:
SourceDataSummary m_source_data;
mutable Vec3d m_starting_center;
public:
#if ENABLE_SVG_ICONS
GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
virtual ~GLGizmoSlaSupports();
void set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection);
bool mouse_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down);
void delete_selected_points(bool force = false);
private:
bool on_init();
void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection);
virtual void on_render(const GLCanvas3D::Selection& selection) const;
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const;
void render_selection_rectangle() const;
void render_points(const GLCanvas3D::Selection& selection, bool picking = false) const;
bool is_mesh_update_necessary() const;
void update_mesh();
#if !ENABLE_IMGUI
void render_tooltip_texture() const;
mutable GLTexture m_tooltip_texture;
mutable GLTexture m_reset_texture;
#endif // not ENABLE_IMGUI
bool m_lock_unique_islands = false;
bool m_editing_mode = false; // Is editing mode active?
bool m_old_editing_state = false; // To keep track of whether the user toggled between the modes (needed for imgui refreshes).
float m_new_point_head_diameter = 0.4f; // Size of a new point.
float m_minimal_point_distance = 20.f;
float m_density = 100.f;
std::vector<std::pair<sla::SupportPoint, bool>> m_editing_mode_cache; // a support point and whether it is currently selected
bool m_selection_rectangle_active = false;
Vec2d m_selection_rectangle_start_corner;
Vec2d m_selection_rectangle_end_corner;
bool m_ignore_up_event = false;
bool m_combo_box_open = false; // To ensure proper rendering of the imgui combobox.
bool m_unsaved_changes = false; // Are there unsaved changes in manual mode?
bool m_selection_empty = true;
EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state)
int m_canvas_width;
int m_canvas_height;
std::vector<ConfigOption*> get_config_options(const std::vector<std::string>& keys) const;
// Methods that do the model_object and editing cache synchronization,
// editing mode selection, etc:
enum {
AllPoints = -2,
NoPoints,
};
void select_point(int i);
void editing_mode_apply_changes();
void editing_mode_discard_changes();
void editing_mode_reload_cache();
void get_data_from_backend();
void auto_generate();
void switch_to_editing_mode();
protected:
void on_set_state() override;
void on_start_dragging(const GLCanvas3D::Selection& selection) override;
#if ENABLE_IMGUI
virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection) override;
#endif // ENABLE_IMGUI
virtual std::string on_get_name() const;
virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const;
virtual bool on_is_selectable() const;
};
#if !ENABLE_IMGUI
class GLGizmoCutPanel;
#endif // not ENABLE_IMGUI
class GLGizmoCut : public GLGizmoBase
{
static const double Offset;
static const double Margin;
static const std::array<float, 3> GrabberColor;
mutable double m_cut_z;
double m_start_z;
mutable double m_max_z;
Vec3d m_drag_pos;
Vec3d m_drag_center;
bool m_keep_upper;
bool m_keep_lower;
bool m_rotate_lower;
#if !ENABLE_IMGUI
GLGizmoCutPanel *m_panel;
#endif // not ENABLE_IMGUI
public:
#if ENABLE_SVG_ICONS
GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoCut(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
#if !ENABLE_IMGUI
virtual void create_external_gizmo_widgets(wxWindow *parent);
#endif // not ENABLE_IMGUI
#if !ENABLE_IMGUI
#endif // not ENABLE_IMGUI
protected:
virtual bool on_init();
virtual std::string on_get_name() const;
virtual void on_set_state();
virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const;
virtual void on_start_dragging(const GLCanvas3D::Selection& selection);
virtual void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection);
virtual void on_render(const GLCanvas3D::Selection& selection) const;
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const;
#if ENABLE_IMGUI
virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection);
#endif // ENABLE_IMGUI
private:
void update_max_z(const GLCanvas3D::Selection& selection) const;
void set_cut_z(double cut_z) const;
void perform_cut(const GLCanvas3D::Selection& selection);
double calc_projection(const Linef3& mouse_ray) const;
};
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GLGizmo_hpp_

View File

@ -2,6 +2,7 @@
#define slic3r_GLTexture_hpp_
#include <string>
#include <vector>
class wxImage;

View File

@ -148,9 +148,6 @@ void config_wizard(int reason)
_(L("Please check and fix your object list.")),
_(L("Attention!")));
}
// Load the currently selected preset into the GUI, update the preset selection box.
// wxGetApp().load_current_presets(); // #ys_FIXME_to_delete presets are loaded now in select_preset function
}
// opt_index = 0, by the reason of zero-index in ConfigOptionVector by default (in case only one element)

View File

@ -17,6 +17,7 @@
#include <wx/dir.h>
#include <wx/wupdlock.h>
#include <wx/filefn.h>
#include <wx/sysopt.h>
#include "libslic3r/Utils.hpp"
#include "libslic3r/Model.hpp"
@ -79,9 +80,7 @@ IMPLEMENT_APP(GUI_App)
GUI_App::GUI_App()
: wxApp()
, m_em_unit(10)
#if ENABLE_IMGUI
, m_imgui(new ImGuiWrapper())
#endif // ENABLE_IMGUI
{}
bool GUI_App::OnInit()
@ -90,14 +89,17 @@ bool GUI_App::OnInit()
const wxString resources_dir = from_u8(Slic3r::resources_dir());
wxCHECK_MSG(wxDirExists(resources_dir), false,
wxString::Format("Resources path does not exist or is not a directory: %s", resources_dir));
#if ENABLE_IMGUI
wxCHECK_MSG(m_imgui->init(), false, "Failed to initialize ImGui");
#endif // ENABLE_IMGUI
SetAppName("Slic3rPE-alpha");
SetAppName("Slic3rPE-beta");
SetAppDisplayName("Slic3r Prusa Edition");
// Enable this to get the default Win32 COMCTRL32 behavior of static boxes.
// wxSystemOptions::SetOption("msw.staticbox.optimized-paint", 0);
// Enable this to disable Windows Vista themes for all wxNotebooks. The themes seem to lead to terrible
// performance when working on high resolution multi-display setups.
// wxSystemOptions::SetOption("msw.notebook.themed-background", 0);
// Slic3r::debugf "wxWidgets version %s, Wx version %s\n", wxVERSION_STRING, wxVERSION;
// Set the Slic3r data directory at the Slic3r XS module.
@ -161,22 +163,13 @@ bool GUI_App::OnInit()
Bind(wxEVT_IDLE, [this](wxIdleEvent& event)
{
if (! plater_)
return;
if (app_config->dirty() && app_config->get("autosave") == "1")
app_config->save();
// ! Temporary workaround for the correct behavior of the Scrolled sidebar panel
// Do this "manipulations" only once ( after (re)create of the application )
if (plater_ && sidebar().obj_list()->GetMinHeight() > 15 * wxGetApp().em_unit())
{
wxWindowUpdateLocker noUpdates_sidebar(&sidebar());
sidebar().obj_list()->SetMinSize(wxSize(-1, 15 * wxGetApp().em_unit()));
// !!! to correct later layouts
update_mode(); // update view mode after fix of the object_list size
}
if (this->plater() != nullptr)
this->obj_manipul()->update_if_dirty();
this->obj_manipul()->update_if_dirty();
// Preset updating & Configwizard are done after the above initializations,
// and after MainFrame is created & shown.
@ -203,12 +196,13 @@ bool GUI_App::OnInit()
}
preset_updater->sync(preset_bundle);
});
load_current_presets();
}
});
load_current_presets();
mainframe->Show(true);
update_mode(); // update view mode after fix of the object_list size
m_initialized = true;
return true;
}
@ -259,6 +253,7 @@ void GUI_App::init_fonts()
{
m_small_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
m_bold_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold();
m_normal_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
#ifdef __WXMAC__
m_small_font.SetPointSize(11);
@ -532,20 +527,7 @@ void GUI_App::save_mode(const /*ConfigOptionMode*/int mode)
// Update view mode according to selected menu
void GUI_App::update_mode()
{
wxWindowUpdateLocker noUpdates(&sidebar());
const ConfigOptionMode mode = wxGetApp().get_mode();
obj_list()->get_sizer()->Show(mode > comSimple);
sidebar().set_mode_value(mode);
// sidebar().show_buttons(mode == comExpert);
obj_list()->unselect_objects();
obj_list()->update_selections();
obj_list()->update_object_menu();
sidebar().update_mode_sizer(mode);
sidebar().Layout();
sidebar().update_mode();
for (auto tab : tabs_list)
tab->update_visibility();

View File

@ -5,9 +5,7 @@
#include <string>
#include "libslic3r/PrintConfig.hpp"
#include "MainFrame.hpp"
#if ENABLE_IMGUI
#include "ImGuiWrapper.hpp"
#endif // ENABLE_IMGUI
#include <wx/app.h>
#include <wx/colour.h>
@ -80,16 +78,14 @@ class GUI_App : public wxApp
wxFont m_small_font;
wxFont m_bold_font;
wxFont m_normal_font;
size_t m_em_unit; // width of a "m"-symbol in pixels for current system font
// Note: for 100% Scale m_em_unit = 10 -> it's a good enough coefficient for a size setting of controls
wxLocale* m_wxLocale{ nullptr };
#if ENABLE_IMGUI
std::unique_ptr<ImGuiWrapper> m_imgui;
#endif // ENABLE_IMGUI
std::unique_ptr<PrintHostJobQueue> m_printhost_job_queue;
public:
@ -111,6 +107,7 @@ public:
const wxFont& small_font() { return m_small_font; }
const wxFont& bold_font() { return m_bold_font; }
const wxFont& normal_font() { return m_normal_font; }
size_t em_unit() const { return m_em_unit; }
void set_em_unit(const size_t em_unit) { m_em_unit = em_unit; }
@ -166,9 +163,7 @@ public:
std::vector<Tab *> tabs_list;
#if ENABLE_IMGUI
ImGuiWrapper* imgui() { return m_imgui.get(); }
#endif // ENABLE_IMGUI
PrintHostJobQueue& printhost_job_queue() { return *m_printhost_job_queue.get(); }

View File

@ -43,6 +43,18 @@ static PrinterTechnology printer_technology()
return wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology();
}
// Config from current edited printer preset
static DynamicPrintConfig& printer_config()
{
return wxGetApp().preset_bundle->printers.get_edited_preset().config;
}
static int extruders_count()
{
return printer_technology() == ptSLA ? 1 :
printer_config().option<ConfigOptionFloats>("nozzle_diameter")->values.size();
}
ObjectList::ObjectList(wxWindow* parent) :
wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDV_MULTIPLE),
m_parent(parent)
@ -115,10 +127,7 @@ ObjectList::~ObjectList()
void ObjectList::create_objects_ctrl()
{
// temporary workaround for the correct behavior of the Scrolled sidebar panel:
// 1. set a height of the list to some big value
// 2. change it to the normal min value (200) after first whole App updating/layouting
SetMinSize(wxSize(-1, 3000)); // #ys_FIXME
SetMinSize(wxSize(-1, 15 * wxGetApp().em_unit()));
m_sizer = new wxBoxSizer(wxVERTICAL);
m_sizer->Add(this, 1, wxGROW);
@ -430,11 +439,12 @@ void ObjectList::OnContextMenu(wxDataViewEvent&)
else if (title == _("Name") && pt.x >15 &&
m_objects_model->GetBitmap(item).GetRefData() == m_bmp_manifold_warning.GetRefData())
{
if (is_windows10()) {
const auto obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item));
wxGetApp().plater()->fix_through_netfabb(obj_idx);
}
if (is_windows10())
fix_through_netfabb();
}
else if (title == _("Extruder"))
show_extruder_selection_menu();
#ifndef __WXMSW__
GetMainWindow()->SetToolTip(""); // hide tooltip
#endif //__WXMSW__
@ -442,9 +452,13 @@ void ObjectList::OnContextMenu(wxDataViewEvent&)
void ObjectList::show_context_menu()
{
if (multiple_selection() && selected_instances_of_same_object())
if (multiple_selection())
{
wxGetApp().plater()->PopupMenu(&m_menu_instance);
if (selected_instances_of_same_object())
wxGetApp().plater()->PopupMenu(&m_menu_instance);
else
show_extruder_selection_menu();
return;
}
@ -649,8 +663,7 @@ void ObjectList::get_options_menu(settings_menu_hierarchy& settings_menu, const
{
auto options = get_options(is_part);
auto extruders_cnt = printer_technology() == ptSLA ? 1 :
wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
const int extruders_cnt = extruders_count();
DynamicPrintConfig config;
for (auto& option : options)
@ -660,9 +673,7 @@ void ObjectList::get_options_menu(settings_menu_hierarchy& settings_menu, const
if (category.empty() ||
(category == "Extruders" && extruders_cnt == 1)) continue;
const std::string& label = opt->label.empty() ? opt->full_label :
opt->full_label.empty() ? opt->label :
opt->full_label + " " + opt->label;;
const std::string& label = !opt->full_label.empty() ? opt->full_label : opt->label;
std::pair<std::string, std::string> option_label(option, label);
std::vector< std::pair<std::string, std::string> > new_category;
auto& cat_opt_label = settings_menu.find(category) == settings_menu.end() ? new_category : settings_menu.at(category);
@ -1086,8 +1097,7 @@ void ObjectList::create_freq_settings_popupmenu(wxMenu *menu)
const FreqSettingsBundle& bundle = printer_technology() == ptFFF ?
FREQ_SETTINGS_BUNDLE_FFF : FREQ_SETTINGS_BUNDLE_SLA;
auto extruders_cnt = printer_technology() == ptSLA ? 1 :
wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
const int extruders_cnt = extruders_count();
for (auto& it : bundle) {
if (it.first.empty() || it.first == "Extruders" && extruders_cnt == 1)
@ -1284,7 +1294,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
const wxString name = _(L("Generic")) + "-" + _(type_name);
TriangleMesh mesh;
auto& bed_shape = wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionPoints>("bed_shape")->values;
auto& bed_shape = printer_config().option<ConfigOptionPoints>("bed_shape")->values;
const auto& sz = BoundingBoxf(bed_shape).size();
const auto side = 0.1 * std::max(sz(0), sz(1));
@ -1412,12 +1422,14 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con
// Cannot delete a wipe tower.
return false;
ModelObject* object = (*m_objects)[obj_idx];
if (type == itVolume) {
const auto volume = (*m_objects)[obj_idx]->volumes[idx];
const auto volume = object->volumes[idx];
// if user is deleting the last solid part, throw error
int solid_cnt = 0;
for (auto vol : (*m_objects)[obj_idx]->volumes)
for (auto vol : object->volumes)
if (vol->is_model_part())
++solid_cnt;
if (volume->is_model_part() && solid_cnt == 1) {
@ -1425,14 +1437,23 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con
return false;
}
(*m_objects)[obj_idx]->delete_volume(idx);
object->delete_volume(idx);
if (object->volumes.size() == 1)
{
const auto last_volume = object->volumes[0];
if (!last_volume->config.empty()) {
object->config.apply(last_volume->config);
last_volume->config.clear();
}
}
}
else if (type == itInstance) {
if ((*m_objects)[obj_idx]->instances.size() == 1) {
if (object->instances.size() == 1) {
Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last intance from object.")));
return false;
}
(*m_objects)[obj_idx]->delete_instance(idx);
object->delete_instance(idx);
}
else
return false;
@ -1452,7 +1473,7 @@ void ObjectList::split()
ModelVolume* volume;
if (!get_volume_by_item(item, volume)) return;
DynamicPrintConfig& config = wxGetApp().preset_bundle->printers.get_edited_preset().config;
DynamicPrintConfig& config = printer_config();
const ConfigOption *nozzle_dmtrs_opt = config.option("nozzle_diameter", false);
const auto nozzle_dmrs_cnt = (nozzle_dmtrs_opt == nullptr) ? size_t(1) : dynamic_cast<const ConfigOptionFloats*>(nozzle_dmtrs_opt)->values.size();
if (volume->split(nozzle_dmrs_cnt) == 1) {
@ -1522,12 +1543,7 @@ bool ObjectList::is_splittable()
if (!get_volume_by_item(item, volume) || !volume)
return false;
int splittable = volume->is_splittable();
if (splittable == -1) {
splittable = (int)volume->mesh.has_multiple_patches();
volume->set_splittable(splittable);
}
return splittable != 0;
return volume->is_splittable();
}
bool ObjectList::selected_instances_of_same_object()
@ -1762,6 +1778,12 @@ void ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& it
if (item->type&itVolume)
{
m_objects_model->Delete(m_objects_model->GetItemByVolumeId(item->obj_idx, item->sub_obj_idx));
if ((*m_objects)[item->obj_idx]->volumes.size() == 1 &&
(*m_objects)[item->obj_idx]->config.has("extruder"))
{
const wxString extruder = wxString::Format("%d", (*m_objects)[item->obj_idx]->config.option<ConfigOptionInt>("extruder")->value);
m_objects_model->SetValue(extruder, m_objects_model->GetItemById(item->obj_idx), 1);
}
wxGetApp().plater()->canvas3D()->ensure_on_bed(item->obj_idx);
}
else
@ -2000,6 +2022,8 @@ void ObjectList::update_selections_on_canvas()
void ObjectList::select_item(const wxDataViewItem& item)
{
if (! item.IsOk()) { return; }
m_prevent_list_events = true;
UnselectAll();
@ -2280,13 +2304,37 @@ void ObjectList::fix_through_netfabb() const
if (!item)
return;
ItemType type = m_objects_model->GetItemType(item);
const ItemType type = m_objects_model->GetItemType(item);
const int obj_idx = type & itObject ? m_objects_model->GetIdByItem(item) :
type & itVolume ? m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)) : -1;
const int vol_idx = type & itVolume ? m_objects_model->GetVolumeIdByItem(item) : -1;
wxGetApp().plater()->fix_through_netfabb(obj_idx, vol_idx);
if (type & itObject)
wxGetApp().plater()->fix_through_netfabb(m_objects_model->GetIdByItem(item));
else if (type & itVolume)
wxGetApp().plater()->fix_through_netfabb(m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)),
m_objects_model->GetVolumeIdByItem(item));
update_item_error_icon(obj_idx, vol_idx);
}
void ObjectList::update_item_error_icon(const int obj_idx, const int vol_idx) const
{
const wxDataViewItem item = vol_idx <0 ? m_objects_model->GetItemById(obj_idx) :
m_objects_model->GetItemByVolumeId(obj_idx, vol_idx);
if (!item)
return;
auto model_object = (*m_objects)[obj_idx];
const stl_stats& stats = model_object->volumes[vol_idx<0 ? 0 : vol_idx]->mesh.stl.stats;
const int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
stats.facets_added + stats.facets_reversed + stats.backwards_edges;
if (errors == 0) {
// delete Error_icon if all errors are fixed
wxVariant variant;
variant << PrusaDataViewBitmapText(from_u8(model_object->name), wxNullBitmap);
m_objects_model->SetValue(variant, item, 0);
}
}
void ObjectList::ItemValueChanged(wxDataViewEvent &event)
@ -2309,5 +2357,86 @@ void ObjectList::OnEditingDone(wxDataViewEvent &event)
_(L("the following characters are not allowed:")) + " <>:/\\|?*\"");
}
void ObjectList::show_extruder_selection_menu()
{
wxDataViewItemArray sels;
GetSelections(sels);
for (const wxDataViewItem& item : sels)
if (!(m_objects_model->GetItemType(item) & (itVolume | itObject)))
// show this menu only for Object(s)/Volume(s) selection
return;
wxMenu* menu = new wxMenu();
append_menu_item(menu, wxID_ANY, _(L("Set extruder for selected items")),
_(L("Select extruder number for selected objects and/or parts")),
[this](wxCommandEvent&) { extruder_selection(); }, "", menu);
PopupMenu(menu);
}
void ObjectList::extruder_selection()
{
wxArrayString choices;
choices.Add("default");
for (int i = 1; i <= extruders_count(); ++i)
choices.Add(wxString::Format("%d", i));
const wxString& selected_extruder = wxGetSingleChoice(_(L("Select extruder number:")),
_(L("This extruder will be set for selected items")),
choices, 0, this);
if (selected_extruder.IsEmpty())
return;
const int extruder_num = selected_extruder == "default" ? 0 : atoi(selected_extruder.c_str());
// /* Another variant for an extruder selection */
// extruder_num = wxGetNumberFromUser(_(L("Attention!!! \n"
// "It's a possibile to set an extruder number \n"
// "for whole Object(s) and/or object Part(s), \n"
// "not for an Instance. ")),
// _(L("Enter extruder number:")),
// _(L("This extruder will be set for selected items")),
// 1, 1, 5, this);
set_extruder_for_selected_items(extruder_num);
}
void ObjectList::set_extruder_for_selected_items(const int extruder) const
{
wxDataViewItemArray sels;
GetSelections(sels);
for (const wxDataViewItem& item : sels)
{
const ItemType type = m_objects_model->GetItemType(item);
const int obj_idx = type & itObject ? m_objects_model->GetIdByItem(item) :
m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item));
const int vol_idx = type & itVolume ? m_objects_model->GetVolumeIdByItem(item) : -1;
DynamicPrintConfig& config = type & itObject ? (*m_objects)[obj_idx]->config :
(*m_objects)[obj_idx]->volumes[vol_idx]->config;
if (config.has("extruder")) {
if (extruder == 0)
config.erase("extruder");
else
config.option<ConfigOptionInt>("extruder")->value = extruder;
}
else if (extruder > 0)
config.set_key_value("extruder", new ConfigOptionInt(extruder));
const wxString extruder_str = extruder == 0 ? wxString ("default") :
wxString::Format("%d", config.option<ConfigOptionInt>("extruder")->value);
m_objects_model->SetValue(extruder_str, item, 1);
wxGetApp().plater()->canvas3D()->ensure_on_bed(obj_idx);
}
// update scene
wxGetApp().plater()->update();
}
} //namespace GUI
} //namespace Slic3r

View File

@ -270,6 +270,7 @@ public:
void split_instances();
void rename_item();
void fix_through_netfabb() const;
void update_item_error_icon(const int obj_idx, int vol_idx) const ;
private:
void OnChar(wxKeyEvent& event);
void OnContextMenu(wxDataViewEvent &event);
@ -282,6 +283,10 @@ private:
void ItemValueChanged(wxDataViewEvent &event);
void OnEditingDone(wxDataViewEvent &event);
void show_extruder_selection_menu();
void extruder_selection();
void set_extruder_for_selected_items(const int extruder) const ;
std::vector<std::string> get_options(const bool is_part);
const std::vector<std::string>& get_options_for_bundle(const wxString& bundle_name);
void get_options_menu(settings_menu_hierarchy& settings_menu, const bool is_part);

View File

@ -27,17 +27,11 @@
namespace Slic3r {
namespace GUI {
View3D::View3D(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
View3D::View3D(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
: m_canvas_widget(nullptr)
, m_canvas(nullptr)
#if !ENABLE_IMGUI
, m_gizmo_widget(nullptr)
#endif // !ENABLE_IMGUI
, m_model(nullptr)
, m_config(nullptr)
, m_process(nullptr)
{
init(parent, model, config, process);
init(parent, bed, camera, view_toolbar, model, config, process);
}
View3D::~View3D()
@ -50,13 +44,13 @@ View3D::~View3D()
}
}
bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
bool View3D::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
{
if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* disable wxTAB_TRAVERSAL */))
return false;
m_canvas_widget = GLCanvas3DManager::create_wxglcanvas(this);
_3DScene::add_canvas(m_canvas_widget);
_3DScene::add_canvas(m_canvas_widget, bed, camera, view_toolbar);
m_canvas = _3DScene::get_canvas(this->m_canvas_widget);
m_canvas->allow_multisample(GLCanvas3DManager::can_multisample());
@ -70,17 +64,8 @@ bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, Ba
m_canvas->enable_gizmos(true);
m_canvas->enable_toolbar(true);
#if !ENABLE_IMGUI
m_gizmo_widget = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize);
m_gizmo_widget->SetSizer(new wxBoxSizer(wxVERTICAL));
m_canvas->set_external_gizmo_widgets_parent(m_gizmo_widget);
#endif // !ENABLE_IMGUI
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
main_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0);
#if !ENABLE_IMGUI
main_sizer->Add(m_gizmo_widget, 0, wxALL | wxEXPAND, 0);
#endif // !ENABLE_IMGUI
SetSizer(main_sizer);
SetMinSize(GetSize());
@ -89,18 +74,6 @@ bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, Ba
return true;
}
void View3D::set_bed(Bed3D* bed)
{
if (m_canvas != nullptr)
m_canvas->set_bed(bed);
}
void View3D::set_view_toolbar(GLToolbar* toolbar)
{
if (m_canvas != nullptr)
m_canvas->set_view_toolbar(toolbar);
}
void View3D::set_as_dirty()
{
if (m_canvas != nullptr)
@ -193,7 +166,7 @@ void View3D::render()
m_canvas->set_as_dirty();
}
Preview::Preview(wxWindow* parent, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function<void()> schedule_background_process_func)
Preview::Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function<void()> schedule_background_process_func)
: m_canvas_widget(nullptr)
, m_canvas(nullptr)
, m_double_slider_sizer(nullptr)
@ -213,28 +186,26 @@ Preview::Preview(wxWindow* parent, DynamicPrintConfig* config, BackgroundSlicing
, m_loaded(false)
, m_enabled(false)
, m_schedule_background_process(schedule_background_process_func)
, m_volumes_cleanup_required(false)
{
if (init(parent, config, process, gcode_preview_data))
if (init(parent, bed, camera, view_toolbar))
{
show_hide_ui_elements("none");
load_print();
}
}
bool Preview::init(wxWindow* parent, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data)
bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar)
{
if ((config == nullptr) || (process == nullptr) || (gcode_preview_data == nullptr))
return false;
if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* disable wxTAB_TRAVERSAL */))
return false;
m_canvas_widget = GLCanvas3DManager::create_wxglcanvas(this);
_3DScene::add_canvas(m_canvas_widget);
m_canvas = _3DScene::get_canvas(this->m_canvas_widget);
_3DScene::add_canvas(m_canvas_widget, bed, camera, view_toolbar);
m_canvas = _3DScene::get_canvas(this->m_canvas_widget);
m_canvas->allow_multisample(GLCanvas3DManager::can_multisample());
m_canvas->set_config(m_config);
m_canvas->set_process(process);
m_canvas->set_process(m_process);
m_canvas->enable_legend_texture(true);
m_canvas->enable_dynamic_background(true);
@ -342,18 +313,6 @@ Preview::~Preview()
}
}
void Preview::set_bed(Bed3D* bed)
{
if (m_canvas != nullptr)
m_canvas->set_bed(bed);
}
void Preview::set_view_toolbar(GLToolbar* toolbar)
{
if (m_canvas != nullptr)
m_canvas->set_view_toolbar(toolbar);
}
void Preview::set_number_extruders(unsigned int number_extruders)
{
if (m_number_extruders != number_extruders)
@ -390,18 +349,6 @@ void Preview::select_view(const std::string& direction)
m_canvas->select_view(direction);
}
void Preview::set_viewport_from_scene(GLCanvas3D* canvas)
{
if (canvas != nullptr)
m_canvas->set_viewport_from_scene(*canvas);
}
void Preview::set_viewport_into_scene(GLCanvas3D* canvas)
{
if (canvas != nullptr)
canvas->set_viewport_from_scene(*m_canvas);
}
void Preview::set_drop_target(wxDropTarget* target)
{
if (target != nullptr)
@ -417,18 +364,22 @@ void Preview::load_print()
load_print_as_sla();
}
void Preview::reload_print(bool force, bool keep_volumes)
void Preview::reload_print(bool keep_volumes)
{
if (!keep_volumes)
if (!IsShown())
{
m_volumes_cleanup_required = !keep_volumes;
return;
}
if (m_volumes_cleanup_required || !keep_volumes)
{
m_canvas->reset_volumes();
m_canvas->reset_legend_texture();
m_loaded = false;
m_volumes_cleanup_required = false;
}
if (!IsShown() && !force)
return;
load_print();
}
@ -608,15 +559,14 @@ static int find_close_layer_idx(const std::vector<double>& zs, double &z, double
return -1;
}
void Preview::update_double_slider(const std::vector<double>& layers_z, bool force_sliders_full_range)
void Preview::update_double_slider(const std::vector<double>& layers_z)
{
// Save the initial slider span.
double z_low = m_slider->GetLowerValueD();
double z_high = m_slider->GetHigherValueD();
bool was_empty = m_slider->GetMaxValue() == 0;
bool span_changed = layers_z.empty() || std::abs(layers_z.back() - m_slider->GetMaxValueD()) > 1e-6;
force_sliders_full_range |= was_empty | span_changed;
bool snap_to_min = force_sliders_full_range || m_slider->is_lower_at_min();
bool force_sliders_full_range = was_empty;
bool snap_to_min = force_sliders_full_range || m_slider->is_lower_at_min();
bool snap_to_max = force_sliders_full_range || m_slider->is_higher_at_max();
std::vector<std::pair<int, double>> values;
@ -788,10 +738,11 @@ void Preview::load_print_as_fff()
if (IsShown())
{
if (gcode_preview_data_valid)
if (gcode_preview_data_valid) {
// Load the real G-code preview.
m_canvas->load_gcode_preview(*m_gcode_preview_data, colors);
else
m_loaded = true;
} else
// Load the initial preview based on slices, not the final G-code.
m_canvas->load_preview(colors, color_print_values);
show_hide_ui_elements(gcode_preview_data_valid ? "full" : "simple");
@ -803,7 +754,6 @@ void Preview::load_print_as_fff()
m_canvas_widget->Refresh();
} else
update_sliders(zs);
m_loaded = true;
}
}

View File

@ -28,30 +28,20 @@ namespace GUI {
class GLCanvas3D;
class GLToolbar;
class Bed3D;
struct Camera;
class View3D : public wxPanel
{
wxGLCanvas* m_canvas_widget;
GLCanvas3D* m_canvas;
#if !ENABLE_IMGUI
wxPanel* m_gizmo_widget;
#endif // !ENABLE_IMGUI
Model* m_model;
DynamicPrintConfig* m_config;
BackgroundSlicingProcess* m_process;
public:
View3D(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process);
View3D(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process);
virtual ~View3D();
wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; }
GLCanvas3D* get_canvas3d() { return m_canvas; }
void set_bed(Bed3D* bed);
void set_view_toolbar(GLToolbar* toolbar);
void set_as_dirty();
void bed_shape_changed();
@ -75,7 +65,7 @@ public:
void render();
private:
bool init(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process);
bool init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process);
};
class Preview : public wxPanel
@ -96,6 +86,8 @@ class Preview : public wxPanel
BackgroundSlicingProcess* m_process;
GCodePreviewData* m_gcode_preview_data;
bool m_volumes_cleanup_required;
// Calling this function object forces Plater::schedule_background_process.
std::function<void()> m_schedule_background_process;
@ -108,30 +100,25 @@ class Preview : public wxPanel
PrusaDoubleSlider* m_slider {nullptr};
public:
Preview(wxWindow* parent, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function<void()> schedule_background_process = [](){});
Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function<void()> schedule_background_process = [](){});
virtual ~Preview();
wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; }
GLCanvas3D* get_canvas3d() { return m_canvas; }
void set_bed(Bed3D* bed);
void set_view_toolbar(GLToolbar* toolbar);
void set_number_extruders(unsigned int number_extruders);
void set_canvas_as_dirty();
void set_enabled(bool enabled);
void bed_shape_changed();
void select_view(const std::string& direction);
void set_viewport_from_scene(GLCanvas3D* canvas);
void set_viewport_into_scene(GLCanvas3D* canvas);
void set_drop_target(wxDropTarget* target);
void load_print();
void reload_print(bool force = false, bool keep_volumes = false);
void reload_print(bool keep_volumes = false);
void refresh_print();
private:
bool init(wxWindow* parent, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data);
bool init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar);
void bind_event_handlers();
void unbind_event_handlers();
@ -151,7 +138,7 @@ private:
// Create/Update/Reset double slider on 3dPreview
void create_double_slider();
void update_double_slider(const std::vector<double>& layers_z, bool force_sliders_full_range = false);
void update_double_slider(const std::vector<double>& layers_z);
void fill_slider_values(std::vector<std::pair<int, double>> &values,
const std::vector<double> &layers_z);
void reset_double_slider();

View File

@ -0,0 +1,281 @@
#include "GLGizmoBase.hpp"
#include <GL/glew.h>
#include "slic3r/GUI/GUI_App.hpp"
// TODO: Display tooltips quicker on Linux
namespace Slic3r {
namespace GUI {
const float GLGizmoBase::Grabber::SizeFactor = 0.025f;
const float GLGizmoBase::Grabber::MinHalfSize = 1.5f;
const float GLGizmoBase::Grabber::DraggingScaleFactor = 1.25f;
GLGizmoBase::Grabber::Grabber()
: center(Vec3d::Zero())
, angles(Vec3d::Zero())
, dragging(false)
, enabled(true)
{
color[0] = 1.0f;
color[1] = 1.0f;
color[2] = 1.0f;
}
void GLGizmoBase::Grabber::render(bool hover, float size) const
{
float render_color[3];
if (hover)
{
render_color[0] = 1.0f - color[0];
render_color[1] = 1.0f - color[1];
render_color[2] = 1.0f - color[2];
}
else
::memcpy((void*)render_color, (const void*)color, 3 * sizeof(float));
render(size, render_color, true);
}
float GLGizmoBase::Grabber::get_half_size(float size) const
{
return std::max(size * SizeFactor, MinHalfSize);
}
float GLGizmoBase::Grabber::get_dragging_half_size(float size) const
{
return std::max(size * SizeFactor * DraggingScaleFactor, MinHalfSize);
}
void GLGizmoBase::Grabber::render(float size, const float* render_color, bool use_lighting) const
{
float half_size = dragging ? get_dragging_half_size(size) : get_half_size(size);
if (use_lighting)
::glEnable(GL_LIGHTING);
::glColor3fv(render_color);
::glPushMatrix();
::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);
// face min x
::glPushMatrix();
::glTranslatef(-(GLfloat)half_size, 0.0f, 0.0f);
::glRotatef(-90.0f, 0.0f, 1.0f, 0.0f);
render_face(half_size);
::glPopMatrix();
// face max x
::glPushMatrix();
::glTranslatef((GLfloat)half_size, 0.0f, 0.0f);
::glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
render_face(half_size);
::glPopMatrix();
// face min y
::glPushMatrix();
::glTranslatef(0.0f, -(GLfloat)half_size, 0.0f);
::glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
render_face(half_size);
::glPopMatrix();
// face max y
::glPushMatrix();
::glTranslatef(0.0f, (GLfloat)half_size, 0.0f);
::glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
render_face(half_size);
::glPopMatrix();
// face min z
::glPushMatrix();
::glTranslatef(0.0f, 0.0f, -(GLfloat)half_size);
::glRotatef(180.0f, 1.0f, 0.0f, 0.0f);
render_face(half_size);
::glPopMatrix();
// face max z
::glPushMatrix();
::glTranslatef(0.0f, 0.0f, (GLfloat)half_size);
render_face(half_size);
::glPopMatrix();
::glPopMatrix();
if (use_lighting)
::glDisable(GL_LIGHTING);
}
void GLGizmoBase::Grabber::render_face(float half_size) const
{
::glBegin(GL_TRIANGLES);
::glNormal3f(0.0f, 0.0f, 1.0f);
::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);
::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();
}
#if ENABLE_SVG_ICONS
GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
#else
GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, unsigned int sprite_id)
#endif // ENABLE_SVG_ICONS
: m_parent(parent)
, m_group_id(-1)
, m_state(Off)
, m_shortcut_key(0)
#if ENABLE_SVG_ICONS
, m_icon_filename(icon_filename)
#endif // ENABLE_SVG_ICONS
, m_sprite_id(sprite_id)
, m_hover_id(-1)
, m_dragging(false)
, m_imgui(wxGetApp().imgui())
{
::memcpy((void*)m_base_color, (const void*)DEFAULT_BASE_COLOR, 3 * sizeof(float));
::memcpy((void*)m_drag_color, (const void*)DEFAULT_DRAG_COLOR, 3 * sizeof(float));
::memcpy((void*)m_highlight_color, (const void*)DEFAULT_HIGHLIGHT_COLOR, 3 * sizeof(float));
}
void GLGizmoBase::set_hover_id(int id)
{
if (m_grabbers.empty() || (id < (int)m_grabbers.size()))
{
m_hover_id = id;
on_set_hover_id();
}
}
void GLGizmoBase::set_highlight_color(const float* color)
{
if (color != nullptr)
::memcpy((void*)m_highlight_color, (const void*)color, 3 * sizeof(float));
}
void GLGizmoBase::enable_grabber(unsigned int id)
{
if ((0 <= id) && (id < (unsigned int)m_grabbers.size()))
m_grabbers[id].enabled = true;
on_enable_grabber(id);
}
void GLGizmoBase::disable_grabber(unsigned int id)
{
if ((0 <= id) && (id < (unsigned int)m_grabbers.size()))
m_grabbers[id].enabled = false;
on_disable_grabber(id);
}
void GLGizmoBase::start_dragging(const GLCanvas3D::Selection& selection)
{
m_dragging = true;
for (int i = 0; i < (int)m_grabbers.size(); ++i)
{
m_grabbers[i].dragging = (m_hover_id == i);
}
on_start_dragging(selection);
}
void GLGizmoBase::stop_dragging()
{
m_dragging = false;
for (int i = 0; i < (int)m_grabbers.size(); ++i)
{
m_grabbers[i].dragging = false;
}
on_stop_dragging();
}
void GLGizmoBase::update(const UpdateData& data, const GLCanvas3D::Selection& selection)
{
if (m_hover_id != -1)
on_update(data, selection);
}
std::array<float, 3> GLGizmoBase::picking_color_component(unsigned int id) const
{
static const float INV_255 = 1.0f / 255.0f;
id = BASE_ID - id;
if (m_group_id > -1)
id -= m_group_id;
// color components are encoded to match the calculation of volume_id made into GLCanvas3D::_picking_pass()
return std::array<float, 3> { (float)((id >> 0) & 0xff) * INV_255, // red
(float)((id >> 8) & 0xff) * INV_255, // green
(float)((id >> 16) & 0xff) * INV_255 }; // blue
}
void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const
{
float size = (float)box.max_size();
for (int i = 0; i < (int)m_grabbers.size(); ++i)
{
if (m_grabbers[i].enabled)
m_grabbers[i].render((m_hover_id == i), size);
}
}
void GLGizmoBase::render_grabbers(float size) const
{
for (int i = 0; i < (int)m_grabbers.size(); ++i)
{
if (m_grabbers[i].enabled)
m_grabbers[i].render((m_hover_id == i), size);
}
}
void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const
{
float size = (float)box.max_size();
for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i)
{
if (m_grabbers[i].enabled)
{
std::array<float, 3> color = picking_color_component(i);
m_grabbers[i].color[0] = color[0];
m_grabbers[i].color[1] = color[1];
m_grabbers[i].color[2] = color[2];
m_grabbers[i].render_for_picking(size);
}
}
}
void GLGizmoBase::set_tooltip(const std::string& tooltip) const
{
m_parent.set_tooltip(tooltip);
}
std::string GLGizmoBase::format(float value, unsigned int decimals) const
{
return Slic3r::string_printf("%.*f", decimals, value);
}
} // namespace GUI
} // namespace Slic3r

View File

@ -0,0 +1,182 @@
#ifndef slic3r_GLGizmoBase_hpp_
#define slic3r_GLGizmoBase_hpp_
#include "libslic3r/Point.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/I18N.hpp"
class wxWindow;
class GLUquadric;
typedef class GLUquadric GLUquadricObj;
namespace Slic3r {
class BoundingBoxf3;
class Linef3;
class ModelObject;
namespace GUI {
static const float DEFAULT_BASE_COLOR[3] = { 0.625f, 0.625f, 0.625f };
static const float DEFAULT_DRAG_COLOR[3] = { 1.0f, 1.0f, 1.0f };
static const float DEFAULT_HIGHLIGHT_COLOR[3] = { 1.0f, 0.38f, 0.0f };
static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } };
class ImGuiWrapper;
class GLGizmoBase
{
public:
// Starting value for ids to avoid clashing with ids used by GLVolumes
// (254 is choosen to leave some space for forward compatibility)
static const unsigned int BASE_ID = 255 * 255 * 254;
protected:
struct Grabber
{
static const float SizeFactor;
static const float MinHalfSize;
static const float DraggingScaleFactor;
Vec3d center;
Vec3d angles;
float color[3];
bool enabled;
bool dragging;
Grabber();
void render(bool hover, float size) const;
void render_for_picking(float size) const { render(size, color, false); }
float get_half_size(float size) const;
float get_dragging_half_size(float size) const;
private:
void render(float size, const float* render_color, bool use_lighting) const;
void render_face(float half_size) const;
};
public:
enum EState
{
Off,
Hover,
On,
Num_States
};
struct UpdateData
{
const Linef3 mouse_ray;
const Point* mouse_pos;
bool shift_down;
UpdateData(const Linef3& mouse_ray, const Point* mouse_pos = nullptr, bool shift_down = false)
: mouse_ray(mouse_ray), mouse_pos(mouse_pos), shift_down(shift_down)
{}
};
protected:
GLCanvas3D& m_parent;
int m_group_id;
EState m_state;
int m_shortcut_key;
#if ENABLE_SVG_ICONS
std::string m_icon_filename;
#endif // ENABLE_SVG_ICONS
unsigned int m_sprite_id;
int m_hover_id;
bool m_dragging;
float m_base_color[3];
float m_drag_color[3];
float m_highlight_color[3];
mutable std::vector<Grabber> m_grabbers;
ImGuiWrapper* m_imgui;
public:
#if ENABLE_SVG_ICONS
GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoBase(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
virtual ~GLGizmoBase() {}
bool init() { return on_init(); }
std::string get_name() const { return on_get_name(); }
int get_group_id() const { return m_group_id; }
void set_group_id(int id) { m_group_id = id; }
EState get_state() const { return m_state; }
void set_state(EState state) { m_state = state; on_set_state(); }
int get_shortcut_key() const { return m_shortcut_key; }
void set_shortcut_key(int key) { m_shortcut_key = key; }
#if ENABLE_SVG_ICONS
const std::string& get_icon_filename() const { return m_icon_filename; }
#endif // ENABLE_SVG_ICONS
bool is_activable(const GLCanvas3D::Selection& selection) const { return on_is_activable(selection); }
bool is_selectable() const { return on_is_selectable(); }
unsigned int get_sprite_id() const { return m_sprite_id; }
int get_hover_id() const { return m_hover_id; }
void set_hover_id(int id);
void set_highlight_color(const float* color);
void enable_grabber(unsigned int id);
void disable_grabber(unsigned int id);
void start_dragging(const GLCanvas3D::Selection& selection);
void stop_dragging();
bool is_dragging() const { return m_dragging; }
void update(const UpdateData& data, const GLCanvas3D::Selection& selection);
void render(const GLCanvas3D::Selection& selection) const { on_render(selection); }
void render_for_picking(const GLCanvas3D::Selection& selection) const { on_render_for_picking(selection); }
void render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection) { on_render_input_window(x, y, bottom_limit, selection); }
protected:
virtual bool on_init() = 0;
virtual std::string on_get_name() const = 0;
virtual void on_set_state() {}
virtual void on_set_hover_id() {}
virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return true; }
virtual bool on_is_selectable() const { return true; }
virtual void on_enable_grabber(unsigned int id) {}
virtual void on_disable_grabber(unsigned int id) {}
virtual void on_start_dragging(const GLCanvas3D::Selection& selection) {}
virtual void on_stop_dragging() {}
virtual void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection) = 0;
virtual void on_render(const GLCanvas3D::Selection& selection) const = 0;
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const = 0;
virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection) {}
// Returns the picking color for the given id, based on the BASE_ID constant
// No check is made for clashing with other picking color (i.e. GLVolumes)
std::array<float, 3> picking_color_component(unsigned int id) const;
void render_grabbers(const BoundingBoxf3& box) const;
void render_grabbers(float size) const;
void render_grabbers_for_picking(const BoundingBoxf3& box) const;
void set_tooltip(const std::string& tooltip) const;
std::string format(float value, unsigned int decimals) const;
};
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GLGizmoBase_hpp_

View File

@ -0,0 +1,257 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoCut.hpp"
#include <GL/glew.h>
#include <wx/button.h>
#include <wx/checkbox.h>
#include <wx/stattext.h>
#include <wx/sizer.h>
#include "slic3r/GUI/GUI_App.hpp"
namespace Slic3r {
namespace GUI {
// GLGizmoCut
class GLGizmoCutPanel : public wxPanel
{
public:
GLGizmoCutPanel(wxWindow *parent);
void display(bool display);
private:
bool m_active;
wxCheckBox *m_cb_rotate;
wxButton *m_btn_cut;
wxButton *m_btn_cancel;
};
GLGizmoCutPanel::GLGizmoCutPanel(wxWindow *parent)
: wxPanel(parent)
, m_active(false)
, m_cb_rotate(new wxCheckBox(this, wxID_ANY, _(L("Rotate lower part upwards"))))
, m_btn_cut(new wxButton(this, wxID_OK, _(L("Perform cut"))))
, m_btn_cancel(new wxButton(this, wxID_CANCEL, _(L("Cancel"))))
{
enum { MARGIN = 5 };
auto *sizer = new wxBoxSizer(wxHORIZONTAL);
auto *label = new wxStaticText(this, wxID_ANY, _(L("Cut object:")));
sizer->Add(label, 0, wxALL | wxALIGN_CENTER, MARGIN);
sizer->Add(m_cb_rotate, 0, wxALL | wxALIGN_CENTER, MARGIN);
sizer->AddStretchSpacer();
sizer->Add(m_btn_cut, 0, wxALL | wxALIGN_CENTER, MARGIN);
sizer->Add(m_btn_cancel, 0, wxALL | wxALIGN_CENTER, MARGIN);
SetSizer(sizer);
}
void GLGizmoCutPanel::display(bool display)
{
Show(display);
GetParent()->Layout();
}
const double GLGizmoCut::Offset = 10.0;
const double GLGizmoCut::Margin = 20.0;
const std::array<float, 3> GLGizmoCut::GrabberColor = { 1.0, 0.5, 0.0 };
#if ENABLE_SVG_ICONS
GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
, m_cut_z(0.0)
, m_max_z(0.0)
, m_keep_upper(true)
, m_keep_lower(true)
, m_rotate_lower(false)
{}
bool GLGizmoCut::on_init()
{
m_grabbers.emplace_back();
m_shortcut_key = WXK_CONTROL_C;
return true;
}
std::string GLGizmoCut::on_get_name() const
{
return L("Cut [C]");
}
void GLGizmoCut::on_set_state()
{
// Reset m_cut_z on gizmo activation
if (get_state() == On) {
m_cut_z = m_parent.get_selection().get_bounding_box().size()(2) / 2.0;
}
}
bool GLGizmoCut::on_is_activable(const GLCanvas3D::Selection& selection) const
{
return selection.is_single_full_instance() && !selection.is_wipe_tower();
}
void GLGizmoCut::on_start_dragging(const GLCanvas3D::Selection& selection)
{
if (m_hover_id == -1) { return; }
const BoundingBoxf3& box = selection.get_bounding_box();
m_start_z = m_cut_z;
update_max_z(selection);
m_drag_pos = m_grabbers[m_hover_id].center;
m_drag_center = box.center();
m_drag_center(2) = m_cut_z;
}
void GLGizmoCut::on_update(const UpdateData& data, const GLCanvas3D::Selection& selection)
{
if (m_hover_id != -1) {
set_cut_z(m_start_z + calc_projection(data.mouse_ray));
}
}
void GLGizmoCut::on_render(const GLCanvas3D::Selection& selection) const
{
if (m_grabbers[0].dragging) {
set_tooltip("Z: " + format(m_cut_z, 2));
}
update_max_z(selection);
const BoundingBoxf3& box = selection.get_bounding_box();
Vec3d plane_center = box.center();
plane_center(2) = m_cut_z;
const float min_x = box.min(0) - Margin;
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);
// Draw the cutting plane
::glBegin(GL_QUADS);
::glColor4f(0.8f, 0.8f, 0.8f, 0.5f);
::glVertex3f(min_x, min_y, plane_center(2));
::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();
::glEnable(GL_CULL_FACE);
::glDisable(GL_BLEND);
// TODO: draw cut part contour?
// Draw the grabber and the connecting line
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);
::glBegin(GL_LINES);
::glVertex3dv(plane_center.data());
::glVertex3dv(m_grabbers[0].center.data());
::glEnd();
std::copy(std::begin(GrabberColor), std::end(GrabberColor), m_grabbers[0].color);
m_grabbers[0].render(m_hover_id == 0, box.max_size());
}
void GLGizmoCut::on_render_for_picking(const GLCanvas3D::Selection& selection) const
{
::glDisable(GL_DEPTH_TEST);
render_grabbers_for_picking(selection.get_bounding_box());
}
void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection)
{
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
m_imgui->set_next_window_bg_alpha(0.5f);
m_imgui->begin(_(L("Cut")), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
ImGui::PushItemWidth(100.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);
m_imgui->checkbox(_(L("Keep lower part")), m_keep_lower);
m_imgui->checkbox(_(L("Rotate lower part upwards")), m_rotate_lower);
m_imgui->disabled_begin(!m_keep_upper && !m_keep_lower);
const bool cut_clicked = m_imgui->button(_(L("Perform cut")));
m_imgui->disabled_end();
m_imgui->end();
if (cut_clicked && (m_keep_upper || m_keep_lower)) {
perform_cut(selection);
}
}
void GLGizmoCut::update_max_z(const GLCanvas3D::Selection& selection) const
{
m_max_z = selection.get_bounding_box().size()(2);
set_cut_z(m_cut_z);
}
void GLGizmoCut::set_cut_z(double cut_z) const
{
// Clamp the plane to the object's bounding box
m_cut_z = std::max(0.0, std::min(m_max_z, cut_z));
}
void GLGizmoCut::perform_cut(const GLCanvas3D::Selection& selection)
{
const auto instance_idx = selection.get_instance_idx();
const auto object_idx = selection.get_object_idx();
wxCHECK_RET(instance_idx >= 0 && object_idx >= 0, "GLGizmoCut: Invalid object selection");
wxGetApp().plater()->cut(object_idx, instance_idx, m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower);
}
double GLGizmoCut::calc_projection(const Linef3& mouse_ray) const
{
double projection = 0.0;
const Vec3d starting_vec = m_drag_pos - m_drag_center;
const double len_starting_vec = starting_vec.norm();
if (len_starting_vec != 0.0)
{
Vec3d mouse_dir = mouse_ray.unit_vector();
// finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
// in our case plane normal and ray direction are the same (orthogonal view)
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
Vec3d inters = mouse_ray.a + (m_drag_pos - mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir;
// vector from the starting position to the found intersection
Vec3d inters_vec = inters - m_drag_pos;
// finds projection of the vector along the staring direction
projection = inters_vec.dot(starting_vec.normalized());
}
return projection;
}
} // namespace GUI
} // namespace Slic3r

View File

@ -0,0 +1,53 @@
#ifndef slic3r_GLGizmoCut_hpp_
#define slic3r_GLGizmoCut_hpp_
#include "GLGizmoBase.hpp"
namespace Slic3r {
namespace GUI {
class GLGizmoCut : public GLGizmoBase
{
static const double Offset;
static const double Margin;
static const std::array<float, 3> GrabberColor;
mutable double m_cut_z;
double m_start_z;
mutable double m_max_z;
Vec3d m_drag_pos;
Vec3d m_drag_center;
bool m_keep_upper;
bool m_keep_lower;
bool m_rotate_lower;
public:
#if ENABLE_SVG_ICONS
GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoCut(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
protected:
virtual bool on_init();
virtual std::string on_get_name() const;
virtual void on_set_state();
virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const;
virtual void on_start_dragging(const GLCanvas3D::Selection& selection);
virtual void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection);
virtual void on_render(const GLCanvas3D::Selection& selection) const;
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const;
virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection);
private:
void update_max_z(const GLCanvas3D::Selection& selection) const;
void set_cut_z(double cut_z) const;
void perform_cut(const GLCanvas3D::Selection& selection);
double calc_projection(const Linef3& mouse_ray) const;
};
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GLGizmoCut_hpp_

View File

@ -0,0 +1,357 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoFlatten.hpp"
#include <numeric>
#include <GL/glew.h>
namespace Slic3r {
namespace GUI {
#if ENABLE_SVG_ICONS
GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
, m_normal(Vec3d::Zero())
, m_starting_center(Vec3d::Zero())
{
}
bool GLGizmoFlatten::on_init()
{
m_shortcut_key = WXK_CONTROL_F;
return true;
}
std::string GLGizmoFlatten::on_get_name() const
{
return L("Place on face [F]");
}
bool GLGizmoFlatten::on_is_activable(const GLCanvas3D::Selection& selection) const
{
return selection.is_single_full_instance();
}
void GLGizmoFlatten::on_start_dragging(const GLCanvas3D::Selection& selection)
{
if (m_hover_id != -1)
{
assert(m_planes_valid);
m_normal = m_planes[m_hover_id].normal;
m_starting_center = selection.get_bounding_box().center();
}
}
void GLGizmoFlatten::on_render(const GLCanvas3D::Selection& selection) const
{
::glClear(GL_DEPTH_BUFFER_BIT);
::glEnable(GL_DEPTH_TEST);
::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());
if (this->is_plane_update_necessary())
const_cast<GLGizmoFlatten*>(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);
else
::glColor4f(0.9f, 0.9f, 0.9f, 0.5f);
::glBegin(GL_POLYGON);
for (const Vec3d& vertex : m_planes[i].vertices)
{
::glVertex3dv(vertex.data());
}
::glEnd();
}
::glPopMatrix();
}
::glEnable(GL_CULL_FACE);
::glDisable(GL_BLEND);
}
void GLGizmoFlatten::on_render_for_picking(const GLCanvas3D::Selection& selection) const
{
::glDisable(GL_DEPTH_TEST);
::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());
if (this->is_plane_update_necessary())
const_cast<GLGizmoFlatten*>(this)->update_planes();
for (int i = 0; i < (int)m_planes.size(); ++i)
{
::glColor3fv(picking_color_component(i).data());
::glBegin(GL_POLYGON);
for (const Vec3d& vertex : m_planes[i].vertices)
{
::glVertex3dv(vertex.data());
}
::glEnd();
}
::glPopMatrix();
}
::glEnable(GL_CULL_FACE);
}
void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object)
{
m_starting_center = Vec3d::Zero();
if (m_model_object != model_object) {
m_planes.clear();
m_planes_valid = false;
}
m_model_object = model_object;
}
void GLGizmoFlatten::update_planes()
{
TriangleMesh ch;
for (const ModelVolume* vol : m_model_object->volumes)
{
if (vol->type() != ModelVolumeType::MODEL_PART)
continue;
TriangleMesh vol_ch = vol->get_convex_hull();
vol_ch.transform(vol->get_matrix());
ch.merge(vol_ch);
}
ch = ch.convex_hull_3d();
m_planes.clear();
const Transform3d& inst_matrix = m_model_object->instances.front()->get_matrix(true);
// Following constants are used for discarding too small polygons.
const float minimal_area = 5.f; // in square mm (world coordinates)
const float minimal_side = 1.f; // mm
// Now we'll go through all the facets and append Points of facets sharing the same normal.
// This part is still performed in mesh coordinate system.
const int num_of_facets = ch.stl.stats.number_of_facets;
std::vector<int> facet_queue(num_of_facets, 0);
std::vector<bool> facet_visited(num_of_facets, false);
int facet_queue_cnt = 0;
const stl_normal* normal_ptr = nullptr;
while (1) {
// Find next unvisited triangle:
int facet_idx = 0;
for (; facet_idx < num_of_facets; ++ facet_idx)
if (!facet_visited[facet_idx]) {
facet_queue[facet_queue_cnt ++] = facet_idx;
facet_visited[facet_idx] = true;
normal_ptr = &ch.stl.facet_start[facet_idx].normal;
m_planes.emplace_back();
break;
}
if (facet_idx == num_of_facets)
break; // Everything was visited already
while (facet_queue_cnt > 0) {
int facet_idx = facet_queue[-- facet_queue_cnt];
const stl_normal& this_normal = ch.stl.facet_start[facet_idx].normal;
if (std::abs(this_normal(0) - (*normal_ptr)(0)) < 0.001 && std::abs(this_normal(1) - (*normal_ptr)(1)) < 0.001 && std::abs(this_normal(2) - (*normal_ptr)(2)) < 0.001) {
stl_vertex* first_vertex = ch.stl.facet_start[facet_idx].vertex;
for (int j=0; j<3; ++j)
m_planes.back().vertices.emplace_back((double)first_vertex[j](0), (double)first_vertex[j](1), (double)first_vertex[j](2));
facet_visited[facet_idx] = true;
for (int j = 0; j < 3; ++ j) {
int neighbor_idx = ch.stl.neighbors_start[facet_idx].neighbor[j];
if (! facet_visited[neighbor_idx])
facet_queue[facet_queue_cnt ++] = neighbor_idx;
}
}
}
m_planes.back().normal = normal_ptr->cast<double>();
// Now we'll transform all the points into world coordinates, so that the areas, angles and distances
// make real sense.
m_planes.back().vertices = transform(m_planes.back().vertices, inst_matrix);
// if this is a just a very small triangle, remove it to speed up further calculations (it would be rejected later anyway):
if (m_planes.back().vertices.size() == 3 &&
((m_planes.back().vertices[0] - m_planes.back().vertices[1]).norm() < minimal_side
|| (m_planes.back().vertices[0] - m_planes.back().vertices[2]).norm() < minimal_side
|| (m_planes.back().vertices[1] - m_planes.back().vertices[2]).norm() < minimal_side))
m_planes.pop_back();
}
// Let's prepare transformation of the normal vector from mesh to instance coordinates.
Geometry::Transformation t(inst_matrix);
Vec3d scaling = t.get_scaling_factor();
t.set_scaling_factor(Vec3d(1./scaling(0), 1./scaling(1), 1./scaling(2)));
// Now we'll go through all the polygons, transform the points into xy plane to process them:
for (unsigned int polygon_id=0; polygon_id < m_planes.size(); ++polygon_id) {
Pointf3s& polygon = m_planes[polygon_id].vertices;
const Vec3d& normal = m_planes[polygon_id].normal;
// transform the normal according to the instance matrix:
Vec3d normal_transformed = t.get_matrix() * normal;
// We are going to rotate about z and y to flatten the plane
Eigen::Quaterniond q;
Transform3d m = Transform3d::Identity();
m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(normal_transformed, Vec3d::UnitZ()).toRotationMatrix();
polygon = transform(polygon, m);
// Now to remove the inner points. We'll misuse Geometry::convex_hull for that, but since
// it works in fixed point representation, we will rescale the polygon to avoid overflows.
// And yes, it is a nasty thing to do. Whoever has time is free to refactor.
Vec3d bb_size = BoundingBoxf3(polygon).size();
float sf = std::min(1./bb_size(0), 1./bb_size(1));
Transform3d tr = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(sf, sf, 1.f));
polygon = transform(polygon, tr);
polygon = Slic3r::Geometry::convex_hull(polygon);
polygon = transform(polygon, tr.inverse());
// Calculate area of the polygons and discard ones that are too small
float& area = m_planes[polygon_id].area;
area = 0.f;
for (unsigned int i = 0; i < polygon.size(); i++) // Shoelace formula
area += polygon[i](0)*polygon[i + 1 < polygon.size() ? i + 1 : 0](1) - polygon[i + 1 < polygon.size() ? i + 1 : 0](0)*polygon[i](1);
area = 0.5f * std::abs(area);
bool discard = false;
if (area < minimal_area)
discard = true;
else {
// We also check the inner angles and discard polygons with angles smaller than the following threshold
const double angle_threshold = ::cos(10.0 * (double)PI / 180.0);
for (unsigned int i = 0; i < polygon.size(); ++i) {
const Vec3d& prec = polygon[(i == 0) ? polygon.size() - 1 : i - 1];
const Vec3d& curr = polygon[i];
const Vec3d& next = polygon[(i == polygon.size() - 1) ? 0 : i + 1];
if ((prec - curr).normalized().dot((next - curr).normalized()) > angle_threshold) {
discard = true;
break;
}
}
}
if (discard) {
m_planes.erase(m_planes.begin() + (polygon_id--));
continue;
}
// We will shrink the polygon a little bit so it does not touch the object edges:
Vec3d centroid = std::accumulate(polygon.begin(), polygon.end(), Vec3d(0.0, 0.0, 0.0));
centroid /= (double)polygon.size();
for (auto& vertex : polygon)
vertex = 0.9f*vertex + 0.1f*centroid;
// Polygon is now simple and convex, we'll round the corners to make them look nicer.
// The algorithm takes a vertex, calculates middles of respective sides and moves the vertex
// towards their average (controlled by 'aggressivity'). This is repeated k times.
// In next iterations, the neighbours are not always taken at the middle (to increase the
// rounding effect at the corners, where we need it most).
const unsigned int k = 10; // number of iterations
const float aggressivity = 0.2f; // agressivity
const unsigned int N = polygon.size();
std::vector<std::pair<unsigned int, unsigned int>> neighbours;
if (k != 0) {
Pointf3s points_out(2*k*N); // vector long enough to store the future vertices
for (unsigned int j=0; j<N; ++j) {
points_out[j*2*k] = polygon[j];
neighbours.push_back(std::make_pair((int)(j*2*k-k) < 0 ? (N-1)*2*k+k : j*2*k-k, j*2*k+k));
}
for (unsigned int i=0; i<k; ++i) {
// Calculate middle of each edge so that neighbours points to something useful:
for (unsigned int j=0; j<N; ++j)
if (i==0)
points_out[j*2*k+k] = 0.5f * (points_out[j*2*k] + points_out[j==N-1 ? 0 : (j+1)*2*k]);
else {
float r = 0.2+0.3/(k-1)*i; // the neighbours are not always taken in the middle
points_out[neighbours[j].first] = r*points_out[j*2*k] + (1-r) * points_out[neighbours[j].first-1];
points_out[neighbours[j].second] = r*points_out[j*2*k] + (1-r) * points_out[neighbours[j].second+1];
}
// Now we have a triangle and valid neighbours, we can do an iteration:
for (unsigned int j=0; j<N; ++j)
points_out[2*k*j] = (1-aggressivity) * points_out[2*k*j] +
aggressivity*0.5f*(points_out[neighbours[j].first] + points_out[neighbours[j].second]);
for (auto& n : neighbours) {
++n.first;
--n.second;
}
}
polygon = points_out; // replace the coarse polygon with the smooth one that we just created
}
// Raise a bit above the object surface to avoid flickering:
for (auto& b : polygon)
b(2) += 0.1f;
// Transform back to 3D (and also back to mesh coordinates)
polygon = transform(polygon, inst_matrix.inverse() * m.inverse());
}
// We'll sort the planes by area and only keep the 254 largest ones (because of the picking pass limitations):
std::sort(m_planes.rbegin(), m_planes.rend(), [](const PlaneData& a, const PlaneData& b) { return a.area < b.area; });
m_planes.resize(std::min((int)m_planes.size(), 254));
// Planes are finished - let's save what we calculated it from:
m_volumes_matrices.clear();
m_volumes_types.clear();
for (const ModelVolume* vol : m_model_object->volumes) {
m_volumes_matrices.push_back(vol->get_matrix());
m_volumes_types.push_back(vol->type());
}
m_first_instance_scale = m_model_object->instances.front()->get_scaling_factor();
m_first_instance_mirror = m_model_object->instances.front()->get_mirror();
m_planes_valid = true;
}
bool GLGizmoFlatten::is_plane_update_necessary() const
{
if (m_state != On || !m_model_object || m_model_object->instances.empty())
return false;
if (! m_planes_valid || m_model_object->volumes.size() != m_volumes_matrices.size())
return true;
// We want to recalculate when the scale changes - some planes could (dis)appear.
if (! m_model_object->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale)
|| ! m_model_object->instances.front()->get_mirror().isApprox(m_first_instance_mirror))
return true;
for (unsigned int i=0; i < m_model_object->volumes.size(); ++i)
if (! m_model_object->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i])
|| m_model_object->volumes[i]->type() != m_volumes_types[i])
return true;
return false;
}
Vec3d GLGizmoFlatten::get_flattening_normal() const
{
Vec3d out = m_normal;
m_normal = Vec3d::Zero();
m_starting_center = Vec3d::Zero();
return out;
}
} // namespace GUI
} // namespace Slic3r

View File

@ -0,0 +1,67 @@
#ifndef slic3r_GLGizmoFlatten_hpp_
#define slic3r_GLGizmoFlatten_hpp_
#include "GLGizmoBase.hpp"
namespace Slic3r {
namespace GUI {
class GLGizmoFlatten : public GLGizmoBase
{
// This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself.
private:
mutable Vec3d m_normal;
struct PlaneData {
std::vector<Vec3d> vertices;
Vec3d normal;
float area;
};
// This holds information to decide whether recalculation is necessary:
std::vector<Transform3d> m_volumes_matrices;
std::vector<ModelVolumeType> m_volumes_types;
Vec3d m_first_instance_scale;
Vec3d m_first_instance_mirror;
std::vector<PlaneData> m_planes;
bool m_planes_valid = false;
mutable Vec3d m_starting_center;
const ModelObject* m_model_object = nullptr;
std::vector<const Transform3d*> instances_matrices;
void update_planes();
bool is_plane_update_necessary() const;
public:
#if ENABLE_SVG_ICONS
GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoFlatten(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
void set_flattening_data(const ModelObject* model_object);
Vec3d get_flattening_normal() const;
protected:
virtual bool on_init();
virtual std::string on_get_name() const;
virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const;
virtual void on_start_dragging(const GLCanvas3D::Selection& selection);
virtual void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection) {}
virtual void on_render(const GLCanvas3D::Selection& selection) const;
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const;
virtual void on_set_state()
{
if (m_state == On && is_plane_update_necessary())
update_planes();
}
};
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GLGizmoFlatten_hpp_

View File

@ -0,0 +1,255 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoMove.hpp"
#include <GL/glew.h>
namespace Slic3r {
namespace GUI {
const double GLGizmoMove3D::Offset = 10.0;
#if ENABLE_SVG_ICONS
GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
, m_displacement(Vec3d::Zero())
, m_snap_step(1.0)
, m_starting_drag_position(Vec3d::Zero())
, m_starting_box_center(Vec3d::Zero())
, m_starting_box_bottom_center(Vec3d::Zero())
, m_quadric(nullptr)
{
m_quadric = ::gluNewQuadric();
if (m_quadric != nullptr)
::gluQuadricDrawStyle(m_quadric, GLU_FILL);
}
GLGizmoMove3D::~GLGizmoMove3D()
{
if (m_quadric != nullptr)
::gluDeleteQuadric(m_quadric);
}
bool GLGizmoMove3D::on_init()
{
for (int i = 0; i < 3; ++i)
{
m_grabbers.push_back(Grabber());
}
m_shortcut_key = WXK_CONTROL_M;
return true;
}
std::string GLGizmoMove3D::on_get_name() const
{
return L("Move [M]");
}
void GLGizmoMove3D::on_start_dragging(const GLCanvas3D::Selection& selection)
{
if (m_hover_id != -1)
{
m_displacement = Vec3d::Zero();
const BoundingBoxf3& box = selection.get_bounding_box();
m_starting_drag_position = m_grabbers[m_hover_id].center;
m_starting_box_center = box.center();
m_starting_box_bottom_center = box.center();
m_starting_box_bottom_center(2) = box.min(2);
}
}
void GLGizmoMove3D::on_stop_dragging()
{
m_displacement = Vec3d::Zero();
}
void GLGizmoMove3D::on_update(const UpdateData& data, const GLCanvas3D::Selection& selection)
{
if (m_hover_id == 0)
m_displacement(0) = calc_projection(data);
else if (m_hover_id == 1)
m_displacement(1) = calc_projection(data);
else if (m_hover_id == 2)
m_displacement(2) = calc_projection(data);
}
void GLGizmoMove3D::on_render(const GLCanvas3D::Selection& selection) const
{
bool show_position = selection.is_single_full_instance();
const Vec3d& position = selection.get_bounding_box().center();
if ((show_position && (m_hover_id == 0)) || m_grabbers[0].dragging)
set_tooltip("X: " + format(show_position ? position(0) : m_displacement(0), 2));
else if (!m_grabbers[0].dragging && (m_hover_id == 0))
set_tooltip("X");
else if ((show_position && (m_hover_id == 1)) || m_grabbers[1].dragging)
set_tooltip("Y: " + format(show_position ? position(1) : m_displacement(1), 2));
else if (!m_grabbers[1].dragging && (m_hover_id == 1))
set_tooltip("Y");
else if ((show_position && (m_hover_id == 2)) || m_grabbers[2].dragging)
set_tooltip("Z: " + format(show_position ? position(2) : m_displacement(2), 2));
else if (!m_grabbers[2].dragging && (m_hover_id == 2))
set_tooltip("Z");
::glClear(GL_DEPTH_BUFFER_BIT);
::glEnable(GL_DEPTH_TEST);
const BoundingBoxf3& box = selection.get_bounding_box();
const Vec3d& center = box.center();
// x axis
m_grabbers[0].center = Vec3d(box.max(0) + Offset, center(1), center(2));
::memcpy((void*)m_grabbers[0].color, (const void*)&AXES_COLOR[0], 3 * sizeof(float));
// y axis
m_grabbers[1].center = Vec3d(center(0), box.max(1) + Offset, center(2));
::memcpy((void*)m_grabbers[1].color, (const void*)&AXES_COLOR[1], 3 * sizeof(float));
// z axis
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);
if (m_hover_id == -1)
{
// draw axes
for (unsigned int i = 0; i < 3; ++i)
{
if (m_grabbers[i].enabled)
{
::glColor3fv(AXES_COLOR[i]);
::glBegin(GL_LINES);
::glVertex3dv(center.data());
::glVertex3dv(m_grabbers[i].center.data());
::glEnd();
}
}
// draw grabbers
render_grabbers(box);
for (unsigned int i = 0; i < 3; ++i)
{
if (m_grabbers[i].enabled)
render_grabber_extension((Axis)i, box, false);
}
}
else
{
// draw axis
::glColor3fv(AXES_COLOR[m_hover_id]);
::glBegin(GL_LINES);
::glVertex3dv(center.data());
::glVertex3dv(m_grabbers[m_hover_id].center.data());
::glEnd();
// draw grabber
m_grabbers[m_hover_id].render(true, box.max_size());
render_grabber_extension((Axis)m_hover_id, box, false);
}
}
void GLGizmoMove3D::on_render_for_picking(const GLCanvas3D::Selection& selection) const
{
::glDisable(GL_DEPTH_TEST);
const BoundingBoxf3& box = selection.get_bounding_box();
render_grabbers_for_picking(box);
render_grabber_extension(X, box, true);
render_grabber_extension(Y, box, true);
render_grabber_extension(Z, box, true);
}
void GLGizmoMove3D::on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection)
{
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
bool show_position = selection.is_single_full_instance();
const Vec3d& position = selection.get_bounding_box().center();
Vec3d displacement = show_position ? position : m_displacement;
wxString label = show_position ? _(L("Position (mm)")) : _(L("Displacement (mm)"));
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
m_imgui->set_next_window_bg_alpha(0.5f);
m_imgui->begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
m_imgui->input_vec3("", displacement, 100.0f, "%.2f");
m_imgui->end();
#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
}
double GLGizmoMove3D::calc_projection(const UpdateData& data) const
{
double projection = 0.0;
Vec3d starting_vec = m_starting_drag_position - m_starting_box_center;
double len_starting_vec = starting_vec.norm();
if (len_starting_vec != 0.0)
{
Vec3d mouse_dir = data.mouse_ray.unit_vector();
// finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
// in our case plane normal and ray direction are the same (orthogonal view)
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir;
// vector from the starting position to the found intersection
Vec3d inters_vec = inters - m_starting_drag_position;
// finds projection of the vector along the staring direction
projection = inters_vec.dot(starting_vec.normalized());
}
if (data.shift_down)
projection = m_snap_step * (double)std::round(projection / m_snap_step);
return projection;
}
void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box, bool picking) const
{
if (m_quadric == nullptr)
return;
double size = m_dragging ? (double)m_grabbers[axis].get_dragging_half_size((float)box.max_size()) : (double)m_grabbers[axis].get_half_size((float)box.max_size());
float color[3];
::memcpy((void*)color, (const void*)m_grabbers[axis].color, 3 * sizeof(float));
if (!picking && (m_hover_id != -1))
{
color[0] = 1.0f - color[0];
color[1] = 1.0f - color[1];
color[2] = 1.0f - color[2];
}
if (!picking)
::glEnable(GL_LIGHTING);
::glColor3fv(color);
::glPushMatrix();
::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);
else if (axis == Y)
::glRotated(-90.0, 1.0, 0.0, 0.0);
::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();
if (!picking)
::glDisable(GL_LIGHTING);
}
} // namespace GUI
} // namespace Slic3r

View File

@ -0,0 +1,57 @@
#ifndef slic3r_GLGizmoMove_hpp_
#define slic3r_GLGizmoMove_hpp_
#include "GLGizmoBase.hpp"
namespace Slic3r {
namespace GUI {
class GLGizmoMove3D : public GLGizmoBase
{
static const double Offset;
Vec3d m_displacement;
double m_snap_step;
Vec3d m_starting_drag_position;
Vec3d m_starting_box_center;
Vec3d m_starting_box_bottom_center;
GLUquadricObj* m_quadric;
public:
#if ENABLE_SVG_ICONS
GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoMove3D(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
virtual ~GLGizmoMove3D();
double get_snap_step(double step) const { return m_snap_step; }
void set_snap_step(double step) { m_snap_step = step; }
const Vec3d& get_displacement() const { return m_displacement; }
protected:
virtual bool on_init();
virtual std::string on_get_name() const;
virtual void on_start_dragging(const GLCanvas3D::Selection& selection);
virtual void on_stop_dragging();
virtual void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection);
virtual void on_render(const GLCanvas3D::Selection& selection) const;
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const;
virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection);
private:
double calc_projection(const UpdateData& data) const;
void render_grabber_extension(Axis axis, const BoundingBoxf3& box, bool picking) const;
};
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GLGizmoMove_hpp_

View File

@ -0,0 +1,503 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoRotate.hpp"
#include <GL/glew.h>
namespace Slic3r {
namespace GUI {
const float GLGizmoRotate::Offset = 5.0f;
const unsigned int GLGizmoRotate::CircleResolution = 64;
const unsigned int GLGizmoRotate::AngleResolution = 64;
const unsigned int GLGizmoRotate::ScaleStepsCount = 72;
const float GLGizmoRotate::ScaleStepRad = 2.0f * (float)PI / GLGizmoRotate::ScaleStepsCount;
const unsigned int GLGizmoRotate::ScaleLongEvery = 2;
const float GLGizmoRotate::ScaleLongTooth = 0.1f; // in percent of radius
const unsigned int GLGizmoRotate::SnapRegionsCount = 8;
const float GLGizmoRotate::GrabberOffset = 0.15f; // in percent of radius
GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis)
#if ENABLE_SVG_ICONS
: GLGizmoBase(parent, "", -1)
#else
: GLGizmoBase(parent, -1)
#endif // ENABLE_SVG_ICONS
, m_axis(axis)
, m_angle(0.0)
, m_quadric(nullptr)
, m_center(0.0, 0.0, 0.0)
, m_radius(0.0f)
, m_snap_coarse_in_radius(0.0f)
, m_snap_coarse_out_radius(0.0f)
, m_snap_fine_in_radius(0.0f)
, m_snap_fine_out_radius(0.0f)
{
m_quadric = ::gluNewQuadric();
if (m_quadric != nullptr)
::gluQuadricDrawStyle(m_quadric, GLU_FILL);
}
GLGizmoRotate::GLGizmoRotate(const GLGizmoRotate& other)
#if ENABLE_SVG_ICONS
: GLGizmoBase(other.m_parent, other.m_icon_filename, other.m_sprite_id)
#else
: GLGizmoBase(other.m_parent, other.m_sprite_id)
#endif // ENABLE_SVG_ICONS
, m_axis(other.m_axis)
, m_angle(other.m_angle)
, m_quadric(nullptr)
, m_center(other.m_center)
, m_radius(other.m_radius)
, m_snap_coarse_in_radius(other.m_snap_coarse_in_radius)
, m_snap_coarse_out_radius(other.m_snap_coarse_out_radius)
, m_snap_fine_in_radius(other.m_snap_fine_in_radius)
, m_snap_fine_out_radius(other.m_snap_fine_out_radius)
{
m_quadric = ::gluNewQuadric();
if (m_quadric != nullptr)
::gluQuadricDrawStyle(m_quadric, GLU_FILL);
}
GLGizmoRotate::~GLGizmoRotate()
{
if (m_quadric != nullptr)
::gluDeleteQuadric(m_quadric);
}
void GLGizmoRotate::set_angle(double angle)
{
if (std::abs(angle - 2.0 * (double)PI) < EPSILON)
angle = 0.0;
m_angle = angle;
}
bool GLGizmoRotate::on_init()
{
m_grabbers.push_back(Grabber());
return true;
}
void GLGizmoRotate::on_start_dragging(const GLCanvas3D::Selection& selection)
{
const BoundingBoxf3& box = selection.get_bounding_box();
m_center = box.center();
m_radius = Offset + box.radius();
m_snap_coarse_in_radius = m_radius / 3.0f;
m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius;
m_snap_fine_in_radius = m_radius;
m_snap_fine_out_radius = m_snap_fine_in_radius + m_radius * ScaleLongTooth;
}
void GLGizmoRotate::on_update(const UpdateData& data, const GLCanvas3D::Selection& selection)
{
Vec2d mouse_pos = to_2d(mouse_position_in_local_plane(data.mouse_ray, selection));
Vec2d orig_dir = Vec2d::UnitX();
Vec2d new_dir = mouse_pos.normalized();
double theta = ::acos(clamp(-1.0, 1.0, new_dir.dot(orig_dir)));
if (cross2(orig_dir, new_dir) < 0.0)
theta = 2.0 * (double)PI - theta;
double len = mouse_pos.norm();
// snap to coarse snap region
if ((m_snap_coarse_in_radius <= len) && (len <= m_snap_coarse_out_radius))
{
double step = 2.0 * (double)PI / (double)SnapRegionsCount;
theta = step * (double)std::round(theta / step);
}
else
{
// snap to fine snap region (scale)
if ((m_snap_fine_in_radius <= len) && (len <= m_snap_fine_out_radius))
{
double step = 2.0 * (double)PI / (double)ScaleStepsCount;
theta = step * (double)std::round(theta / step);
}
}
if (theta == 2.0 * (double)PI)
theta = 0.0;
m_angle = theta;
}
void GLGizmoRotate::on_render(const GLCanvas3D::Selection& selection) const
{
if (!m_grabbers[0].enabled)
return;
const BoundingBoxf3& box = selection.get_bounding_box();
std::string axis;
switch (m_axis)
{
case X: { axis = "X"; break; }
case Y: { axis = "Y"; break; }
case Z: { axis = "Z"; break; }
}
if (!m_dragging && (m_hover_id == 0))
set_tooltip(axis);
else if (m_dragging)
set_tooltip(axis + ": " + format((float)Geometry::rad2deg(m_angle), 4) + "\u00B0");
else
{
m_center = box.center();
m_radius = Offset + box.radius();
m_snap_coarse_in_radius = m_radius / 3.0f;
m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius;
m_snap_fine_in_radius = m_radius;
m_snap_fine_out_radius = m_radius * (1.0f + ScaleLongTooth);
}
::glEnable(GL_DEPTH_TEST);
::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);
render_circle();
if (m_hover_id != -1)
{
render_scale();
render_snap_radii();
render_reference_radius();
}
::glColor3fv(m_highlight_color);
if (m_hover_id != -1)
render_angle();
render_grabber(box);
render_grabber_extension(box, false);
::glPopMatrix();
}
void GLGizmoRotate::on_render_for_picking(const GLCanvas3D::Selection& selection) const
{
::glDisable(GL_DEPTH_TEST);
::glPushMatrix();
transform_to_local(selection);
const BoundingBoxf3& box = selection.get_bounding_box();
render_grabbers_for_picking(box);
render_grabber_extension(box, true);
::glPopMatrix();
}
void GLGizmoRotate::render_circle() const
{
::glBegin(GL_LINE_LOOP);
for (unsigned int i = 0; i < ScaleStepsCount; ++i)
{
float angle = (float)i * ScaleStepRad;
float x = ::cos(angle) * m_radius;
float y = ::sin(angle) * m_radius;
float z = 0.0f;
::glVertex3f((GLfloat)x, (GLfloat)y, (GLfloat)z);
}
::glEnd();
}
void GLGizmoRotate::render_scale() const
{
float out_radius_long = m_snap_fine_out_radius;
float out_radius_short = m_radius * (1.0f + 0.5f * ScaleLongTooth);
::glBegin(GL_LINES);
for (unsigned int i = 0; i < ScaleStepsCount; ++i)
{
float angle = (float)i * ScaleStepRad;
float cosa = ::cos(angle);
float sina = ::sin(angle);
float in_x = cosa * m_radius;
float in_y = sina * m_radius;
float in_z = 0.0f;
float out_x = (i % ScaleLongEvery == 0) ? cosa * out_radius_long : cosa * out_radius_short;
float out_y = (i % ScaleLongEvery == 0) ? sina * out_radius_long : sina * out_radius_short;
float out_z = 0.0f;
::glVertex3f((GLfloat)in_x, (GLfloat)in_y, (GLfloat)in_z);
::glVertex3f((GLfloat)out_x, (GLfloat)out_y, (GLfloat)out_z);
}
::glEnd();
}
void GLGizmoRotate::render_snap_radii() const
{
float step = 2.0f * (float)PI / (float)SnapRegionsCount;
float in_radius = m_radius / 3.0f;
float out_radius = 2.0f * in_radius;
::glBegin(GL_LINES);
for (unsigned int i = 0; i < SnapRegionsCount; ++i)
{
float angle = (float)i * step;
float cosa = ::cos(angle);
float sina = ::sin(angle);
float in_x = cosa * in_radius;
float in_y = sina * in_radius;
float in_z = 0.0f;
float out_x = cosa * out_radius;
float out_y = sina * out_radius;
float out_z = 0.0f;
::glVertex3f((GLfloat)in_x, (GLfloat)in_y, (GLfloat)in_z);
::glVertex3f((GLfloat)out_x, (GLfloat)out_y, (GLfloat)out_z);
}
::glEnd();
}
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();
}
void GLGizmoRotate::render_angle() const
{
float step_angle = (float)m_angle / AngleResolution;
float ex_radius = m_radius * (1.0f + GrabberOffset);
::glBegin(GL_LINE_STRIP);
for (unsigned int i = 0; i <= AngleResolution; ++i)
{
float angle = (float)i * step_angle;
float x = ::cos(angle) * ex_radius;
float y = ::sin(angle) * ex_radius;
float z = 0.0f;
::glVertex3f((GLfloat)x, (GLfloat)y, (GLfloat)z);
}
::glEnd();
}
void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const
{
double grabber_radius = (double)m_radius * (1.0 + (double)GrabberOffset);
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);
::glBegin(GL_LINES);
::glVertex3f(0.0f, 0.0f, 0.0f);
::glVertex3dv(m_grabbers[0].center.data());
::glEnd();
::memcpy((void*)m_grabbers[0].color, (const void*)m_highlight_color, 3 * sizeof(float));
render_grabbers(box);
}
void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool picking) const
{
if (m_quadric == nullptr)
return;
double size = m_dragging ? (double)m_grabbers[0].get_dragging_half_size((float)box.max_size()) : (double)m_grabbers[0].get_half_size((float)box.max_size());
float color[3];
::memcpy((void*)color, (const void*)m_grabbers[0].color, 3 * sizeof(float));
if (!picking && (m_hover_id != -1))
{
color[0] = 1.0f - color[0];
color[1] = 1.0f - color[1];
color[2] = 1.0f - color[2];
}
if (!picking)
::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);
::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);
::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();
if (!picking)
::glDisable(GL_LIGHTING);
}
void GLGizmoRotate::transform_to_local(const GLCanvas3D::Selection& selection) const
{
::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());
}
switch (m_axis)
{
case X:
{
::glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
::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);
break;
}
default:
case Z:
{
// no rotation
break;
}
}
}
Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, const GLCanvas3D::Selection& selection) const
{
double half_pi = 0.5 * (double)PI;
Transform3d m = Transform3d::Identity();
switch (m_axis)
{
case X:
{
m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitZ()));
m.rotate(Eigen::AngleAxisd(-half_pi, Vec3d::UnitY()));
break;
}
case Y:
{
m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitY()));
m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitZ()));
break;
}
default:
case Z:
{
// no rotation applied
break;
}
}
if (selection.is_single_volume() || selection.is_single_modifier() || selection.requires_local_axes())
m = m * selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true).inverse();
m.translate(-m_center);
return transform(mouse_ray, m).intersect_plane(0.0);
}
#if ENABLE_SVG_ICONS
GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
{
m_gizmos.emplace_back(parent, GLGizmoRotate::X);
m_gizmos.emplace_back(parent, GLGizmoRotate::Y);
m_gizmos.emplace_back(parent, GLGizmoRotate::Z);
for (unsigned int i = 0; i < 3; ++i)
{
m_gizmos[i].set_group_id(i);
}
}
bool GLGizmoRotate3D::on_init()
{
for (GLGizmoRotate& g : m_gizmos)
{
if (!g.init())
return false;
}
for (unsigned int i = 0; i < 3; ++i)
{
m_gizmos[i].set_highlight_color(AXES_COLOR[i]);
}
m_shortcut_key = WXK_CONTROL_R;
return true;
}
std::string GLGizmoRotate3D::on_get_name() const
{
return L("Rotate [R]");
}
void GLGizmoRotate3D::on_start_dragging(const GLCanvas3D::Selection& selection)
{
if ((0 <= m_hover_id) && (m_hover_id < 3))
m_gizmos[m_hover_id].start_dragging(selection);
}
void GLGizmoRotate3D::on_stop_dragging()
{
if ((0 <= m_hover_id) && (m_hover_id < 3))
m_gizmos[m_hover_id].stop_dragging();
}
void GLGizmoRotate3D::on_render(const GLCanvas3D::Selection& selection) const
{
::glClear(GL_DEPTH_BUFFER_BIT);
if ((m_hover_id == -1) || (m_hover_id == 0))
m_gizmos[X].render(selection);
if ((m_hover_id == -1) || (m_hover_id == 1))
m_gizmos[Y].render(selection);
if ((m_hover_id == -1) || (m_hover_id == 2))
m_gizmos[Z].render(selection);
}
void GLGizmoRotate3D::on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection)
{
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
Vec3d rotation(Geometry::rad2deg(m_gizmos[0].get_angle()), Geometry::rad2deg(m_gizmos[1].get_angle()), Geometry::rad2deg(m_gizmos[2].get_angle()));
wxString label = _(L("Rotation (deg)"));
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
m_imgui->set_next_window_bg_alpha(0.5f);
m_imgui->begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
m_imgui->input_vec3("", rotation, 100.0f, "%.2f");
m_imgui->end();
#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
}
} // namespace GUI
} // namespace Slic3r

View File

@ -0,0 +1,142 @@
#ifndef slic3r_GLGizmoRotate_hpp_
#define slic3r_GLGizmoRotate_hpp_
#include "GLGizmoBase.hpp"
namespace Slic3r {
namespace GUI {
class GLGizmoRotate : public GLGizmoBase
{
static const float Offset;
static const unsigned int CircleResolution;
static const unsigned int AngleResolution;
static const unsigned int ScaleStepsCount;
static const float ScaleStepRad;
static const unsigned int ScaleLongEvery;
static const float ScaleLongTooth;
static const unsigned int SnapRegionsCount;
static const float GrabberOffset;
public:
enum Axis : unsigned char
{
X,
Y,
Z
};
private:
Axis m_axis;
double m_angle;
GLUquadricObj* m_quadric;
mutable Vec3d m_center;
mutable float m_radius;
mutable float m_snap_coarse_in_radius;
mutable float m_snap_coarse_out_radius;
mutable float m_snap_fine_in_radius;
mutable float m_snap_fine_out_radius;
public:
GLGizmoRotate(GLCanvas3D& parent, Axis axis);
GLGizmoRotate(const GLGizmoRotate& other);
virtual ~GLGizmoRotate();
double get_angle() const { return m_angle; }
void set_angle(double angle);
protected:
virtual bool on_init();
virtual std::string on_get_name() const { return ""; }
virtual void on_start_dragging(const GLCanvas3D::Selection& selection);
virtual void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection);
virtual void on_render(const GLCanvas3D::Selection& selection) const;
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const;
private:
void render_circle() const;
void render_scale() const;
void render_snap_radii() const;
void render_reference_radius() const;
void render_angle() const;
void render_grabber(const BoundingBoxf3& box) const;
void render_grabber_extension(const BoundingBoxf3& box, bool picking) const;
void transform_to_local(const GLCanvas3D::Selection& selection) const;
// returns the intersection of the mouse ray with the plane perpendicular to the gizmo axis, in local coordinate
Vec3d mouse_position_in_local_plane(const Linef3& mouse_ray, const GLCanvas3D::Selection& selection) const;
};
class GLGizmoRotate3D : public GLGizmoBase
{
std::vector<GLGizmoRotate> m_gizmos;
public:
#if ENABLE_SVG_ICONS
GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoRotate3D(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); }
void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); }
protected:
virtual bool on_init();
virtual std::string on_get_name() const;
virtual void on_set_state()
{
for (GLGizmoRotate& g : m_gizmos)
{
g.set_state(m_state);
}
}
virtual void on_set_hover_id()
{
for (unsigned int i = 0; i < 3; ++i)
{
m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1);
}
}
virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return !selection.is_wipe_tower(); }
virtual void on_enable_grabber(unsigned int id)
{
if ((0 <= id) && (id < 3))
m_gizmos[id].enable_grabber(0);
}
virtual void on_disable_grabber(unsigned int id)
{
if ((0 <= id) && (id < 3))
m_gizmos[id].disable_grabber(0);
}
virtual void on_start_dragging(const GLCanvas3D::Selection& selection);
virtual void on_stop_dragging();
virtual void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection)
{
for (GLGizmoRotate& g : m_gizmos)
{
g.update(data, selection);
}
}
virtual void on_render(const GLCanvas3D::Selection& selection) const;
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const
{
for (const GLGizmoRotate& g : m_gizmos)
{
g.render_for_picking(selection);
}
}
virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection);
};
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GLGizmoRotate_hpp_

View File

@ -0,0 +1,362 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoScale.hpp"
#include <GL/glew.h>
namespace Slic3r {
namespace GUI {
const float GLGizmoScale3D::Offset = 5.0f;
#if ENABLE_SVG_ICONS
GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
, m_scale(Vec3d::Ones())
, m_snap_step(0.05)
, m_starting_scale(Vec3d::Ones())
{
}
bool GLGizmoScale3D::on_init()
{
for (int i = 0; i < 10; ++i)
{
m_grabbers.push_back(Grabber());
}
double half_pi = 0.5 * (double)PI;
// x axis
m_grabbers[0].angles(1) = half_pi;
m_grabbers[1].angles(1) = half_pi;
// y axis
m_grabbers[2].angles(0) = half_pi;
m_grabbers[3].angles(0) = half_pi;
m_shortcut_key = WXK_CONTROL_S;
return true;
}
std::string GLGizmoScale3D::on_get_name() const
{
return L("Scale [S]");
}
void GLGizmoScale3D::on_start_dragging(const GLCanvas3D::Selection& selection)
{
if (m_hover_id != -1)
{
m_starting_drag_position = m_grabbers[m_hover_id].center;
m_starting_box = selection.get_bounding_box();
}
}
void GLGizmoScale3D::on_update(const UpdateData& data, const GLCanvas3D::Selection& selection)
{
if ((m_hover_id == 0) || (m_hover_id == 1))
do_scale_x(data);
else if ((m_hover_id == 2) || (m_hover_id == 3))
do_scale_y(data);
else if ((m_hover_id == 4) || (m_hover_id == 5))
do_scale_z(data);
else if (m_hover_id >= 6)
do_scale_uniform(data);
}
void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const
{
bool single_instance = selection.is_single_full_instance();
bool single_volume = selection.is_single_modifier() || selection.is_single_volume();
bool single_selection = single_instance || single_volume;
Vec3f scale = 100.0f * Vec3f::Ones();
if (single_instance)
scale = 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor().cast<float>();
else if (single_volume)
scale = 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_scaling_factor().cast<float>();
if ((single_selection && ((m_hover_id == 0) || (m_hover_id == 1))) || m_grabbers[0].dragging || m_grabbers[1].dragging)
set_tooltip("X: " + format(scale(0), 4) + "%");
else if (!m_grabbers[0].dragging && !m_grabbers[1].dragging && ((m_hover_id == 0) || (m_hover_id == 1)))
set_tooltip("X");
else if ((single_selection && ((m_hover_id == 2) || (m_hover_id == 3))) || m_grabbers[2].dragging || m_grabbers[3].dragging)
set_tooltip("Y: " + format(scale(1), 4) + "%");
else if (!m_grabbers[2].dragging && !m_grabbers[3].dragging && ((m_hover_id == 2) || (m_hover_id == 3)))
set_tooltip("Y");
else if ((single_selection && ((m_hover_id == 4) || (m_hover_id == 5))) || m_grabbers[4].dragging || m_grabbers[5].dragging)
set_tooltip("Z: " + format(scale(2), 4) + "%");
else if (!m_grabbers[4].dragging && !m_grabbers[5].dragging && ((m_hover_id == 4) || (m_hover_id == 5)))
set_tooltip("Z");
else if ((single_selection && ((m_hover_id == 6) || (m_hover_id == 7) || (m_hover_id == 8) || (m_hover_id == 9)))
|| m_grabbers[6].dragging || m_grabbers[7].dragging || m_grabbers[8].dragging || m_grabbers[9].dragging)
{
std::string tooltip = "X: " + format(scale(0), 4) + "%\n";
tooltip += "Y: " + format(scale(1), 4) + "%\n";
tooltip += "Z: " + format(scale(2), 4) + "%";
set_tooltip(tooltip);
}
else if (!m_grabbers[6].dragging && !m_grabbers[7].dragging && !m_grabbers[8].dragging && !m_grabbers[9].dragging &&
((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);
BoundingBoxf3 box;
Transform3d transform = Transform3d::Identity();
Vec3d angles = Vec3d::Zero();
Transform3d offsets_transform = Transform3d::Identity();
Vec3d grabber_size = Vec3d::Zero();
if (single_instance)
{
// calculate bounding box in instance local reference system
const GLCanvas3D::Selection::IndicesList& idxs = selection.get_volume_idxs();
for (unsigned int idx : idxs)
{
const GLVolume* vol = selection.get_volume(idx);
box.merge(vol->bounding_box.transformed(vol->get_volume_transformation().get_matrix()));
}
// gets transform from first selected volume
const GLVolume* v = selection.get_volume(*idxs.begin());
transform = v->get_instance_transformation().get_matrix();
// gets angles from first selected volume
angles = v->get_instance_rotation();
// consider rotation+mirror only components of the transform for offsets
offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror());
grabber_size = v->get_instance_transformation().get_matrix(true, true, false, true) * box.size();
}
else if (single_volume)
{
const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin());
box = v->bounding_box;
transform = v->world_matrix();
angles = Geometry::extract_euler_angles(transform);
// consider rotation+mirror only components of the transform for offsets
offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror());
grabber_size = v->get_volume_transformation().get_matrix(true, true, false, true) * box.size();
}
else
{
box = selection.get_bounding_box();
grabber_size = box.size();
}
m_box = box;
const Vec3d& center = m_box.center();
Vec3d offset_x = offsets_transform * Vec3d((double)Offset, 0.0, 0.0);
Vec3d offset_y = offsets_transform * Vec3d(0.0, (double)Offset, 0.0);
Vec3d offset_z = offsets_transform * Vec3d(0.0, 0.0, (double)Offset);
// x axis
m_grabbers[0].center = transform * Vec3d(m_box.min(0), center(1), center(2)) - offset_x;
m_grabbers[1].center = transform * Vec3d(m_box.max(0), center(1), center(2)) + offset_x;
::memcpy((void*)m_grabbers[0].color, (const void*)&AXES_COLOR[0], 3 * sizeof(float));
::memcpy((void*)m_grabbers[1].color, (const void*)&AXES_COLOR[0], 3 * sizeof(float));
// y axis
m_grabbers[2].center = transform * Vec3d(center(0), m_box.min(1), center(2)) - offset_y;
m_grabbers[3].center = transform * Vec3d(center(0), m_box.max(1), center(2)) + offset_y;
::memcpy((void*)m_grabbers[2].color, (const void*)&AXES_COLOR[1], 3 * sizeof(float));
::memcpy((void*)m_grabbers[3].color, (const void*)&AXES_COLOR[1], 3 * sizeof(float));
// z axis
m_grabbers[4].center = transform * Vec3d(center(0), center(1), m_box.min(2)) - offset_z;
m_grabbers[5].center = transform * Vec3d(center(0), center(1), m_box.max(2)) + offset_z;
::memcpy((void*)m_grabbers[4].color, (const void*)&AXES_COLOR[2], 3 * sizeof(float));
::memcpy((void*)m_grabbers[5].color, (const void*)&AXES_COLOR[2], 3 * sizeof(float));
// uniform
m_grabbers[6].center = transform * Vec3d(m_box.min(0), m_box.min(1), center(2)) - offset_x - offset_y;
m_grabbers[7].center = transform * Vec3d(m_box.max(0), m_box.min(1), center(2)) + offset_x - offset_y;
m_grabbers[8].center = transform * Vec3d(m_box.max(0), m_box.max(1), center(2)) + offset_x + offset_y;
m_grabbers[9].center = transform * Vec3d(m_box.min(0), m_box.max(1), center(2)) - offset_x + offset_y;
for (int i = 6; i < 10; ++i)
{
::memcpy((void*)m_grabbers[i].color, (const void*)m_highlight_color, 3 * sizeof(float));
}
// sets grabbers orientation
for (int i = 0; i < 10; ++i)
{
m_grabbers[i].angles = angles;
}
::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f);
float grabber_mean_size = (float)(grabber_size(0) + grabber_size(1) + grabber_size(2)) / 3.0f;
if (m_hover_id == -1)
{
// draw connections
if (m_grabbers[0].enabled && m_grabbers[1].enabled)
{
::glColor3fv(m_grabbers[0].color);
render_grabbers_connection(0, 1);
}
if (m_grabbers[2].enabled && m_grabbers[3].enabled)
{
::glColor3fv(m_grabbers[2].color);
render_grabbers_connection(2, 3);
}
if (m_grabbers[4].enabled && m_grabbers[5].enabled)
{
::glColor3fv(m_grabbers[4].color);
render_grabbers_connection(4, 5);
}
::glColor3fv(m_base_color);
render_grabbers_connection(6, 7);
render_grabbers_connection(7, 8);
render_grabbers_connection(8, 9);
render_grabbers_connection(9, 6);
// draw grabbers
render_grabbers(grabber_mean_size);
}
else if ((m_hover_id == 0) || (m_hover_id == 1))
{
// draw connection
::glColor3fv(m_grabbers[0].color);
render_grabbers_connection(0, 1);
// draw grabbers
m_grabbers[0].render(true, grabber_mean_size);
m_grabbers[1].render(true, grabber_mean_size);
}
else if ((m_hover_id == 2) || (m_hover_id == 3))
{
// draw connection
::glColor3fv(m_grabbers[2].color);
render_grabbers_connection(2, 3);
// draw grabbers
m_grabbers[2].render(true, grabber_mean_size);
m_grabbers[3].render(true, grabber_mean_size);
}
else if ((m_hover_id == 4) || (m_hover_id == 5))
{
// draw connection
::glColor3fv(m_grabbers[4].color);
render_grabbers_connection(4, 5);
// draw grabbers
m_grabbers[4].render(true, grabber_mean_size);
m_grabbers[5].render(true, grabber_mean_size);
}
else if (m_hover_id >= 6)
{
// draw connection
::glColor3fv(m_drag_color);
render_grabbers_connection(6, 7);
render_grabbers_connection(7, 8);
render_grabbers_connection(8, 9);
render_grabbers_connection(9, 6);
// draw grabbers
for (int i = 6; i < 10; ++i)
{
m_grabbers[i].render(true, grabber_mean_size);
}
}
}
void GLGizmoScale3D::on_render_for_picking(const GLCanvas3D::Selection& selection) const
{
::glDisable(GL_DEPTH_TEST);
render_grabbers_for_picking(selection.get_bounding_box());
}
void GLGizmoScale3D::on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection)
{
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
bool single_instance = selection.is_single_full_instance();
wxString label = _(L("Scale (%)"));
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
m_imgui->set_next_window_bg_alpha(0.5f);
m_imgui->begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
m_imgui->input_vec3("", m_scale * 100.f, 100.0f, "%.2f");
m_imgui->end();
#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
}
void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int id_2) const
{
unsigned int grabbers_count = (unsigned int)m_grabbers.size();
if ((id_1 < grabbers_count) && (id_2 < grabbers_count))
{
::glBegin(GL_LINES);
::glVertex3dv(m_grabbers[id_1].center.data());
::glVertex3dv(m_grabbers[id_2].center.data());
::glEnd();
}
}
void GLGizmoScale3D::do_scale_x(const UpdateData& data)
{
double ratio = calc_ratio(data);
if (ratio > 0.0)
m_scale(0) = m_starting_scale(0) * ratio;
}
void GLGizmoScale3D::do_scale_y(const UpdateData& data)
{
double ratio = calc_ratio(data);
if (ratio > 0.0)
m_scale(1) = m_starting_scale(1) * ratio;
}
void GLGizmoScale3D::do_scale_z(const UpdateData& data)
{
double ratio = calc_ratio(data);
if (ratio > 0.0)
m_scale(2) = m_starting_scale(2) * ratio;
}
void GLGizmoScale3D::do_scale_uniform(const UpdateData& data)
{
double ratio = calc_ratio(data);
if (ratio > 0.0)
m_scale = m_starting_scale * ratio;
}
double GLGizmoScale3D::calc_ratio(const UpdateData& data) const
{
double ratio = 0.0;
// vector from the center to the starting position
Vec3d starting_vec = m_starting_drag_position - m_starting_box.center();
double len_starting_vec = starting_vec.norm();
if (len_starting_vec != 0.0)
{
Vec3d mouse_dir = data.mouse_ray.unit_vector();
// finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
// in our case plane normal and ray direction are the same (orthogonal view)
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir;
// vector from the starting position to the found intersection
Vec3d inters_vec = inters - m_starting_drag_position;
// finds projection of the vector along the staring direction
double proj = inters_vec.dot(starting_vec.normalized());
ratio = (len_starting_vec + proj) / len_starting_vec;
}
if (data.shift_down)
ratio = m_snap_step * (double)std::round(ratio / m_snap_step);
return ratio;
}
} // namespace GUI
} // namespace Slic3r

View File

@ -0,0 +1,62 @@
#ifndef slic3r_GLGizmoScale_hpp_
#define slic3r_GLGizmoScale_hpp_
#include "GLGizmoBase.hpp"
namespace Slic3r {
namespace GUI {
class GLGizmoScale3D : public GLGizmoBase
{
static const float Offset;
mutable BoundingBoxf3 m_box;
Vec3d m_scale;
double m_snap_step;
Vec3d m_starting_scale;
Vec3d m_starting_drag_position;
BoundingBoxf3 m_starting_box;
public:
#if ENABLE_SVG_ICONS
GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoScale3D(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
double get_snap_step(double step) const { return m_snap_step; }
void set_snap_step(double step) { m_snap_step = step; }
const Vec3d& get_scale() const { return m_scale; }
void set_scale(const Vec3d& scale) { m_starting_scale = scale; m_scale = scale; }
protected:
virtual bool on_init();
virtual std::string on_get_name() const;
virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return !selection.is_wipe_tower(); }
virtual void on_start_dragging(const GLCanvas3D::Selection& selection);
virtual void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection);
virtual void on_render(const GLCanvas3D::Selection& selection) const;
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const;
virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection);
private:
void render_grabbers_connection(unsigned int id_1, unsigned int id_2) const;
void do_scale_x(const UpdateData& data);
void do_scale_y(const UpdateData& data);
void do_scale_z(const UpdateData& data);
void do_scale_uniform(const UpdateData& data);
double calc_ratio(const UpdateData& data) const;
};
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GLGizmoScale_hpp_

View File

@ -0,0 +1,874 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoSlaSupports.hpp"
#include <GL/glew.h>
#include <wx/msgdlg.h>
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/GUI_ObjectSettings.hpp"
#include "slic3r/GUI/GUI_ObjectList.hpp"
#include "slic3r/GUI/PresetBundle.hpp"
namespace Slic3r {
namespace GUI {
#if ENABLE_SVG_ICONS
GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
, m_starting_center(Vec3d::Zero()), m_quadric(nullptr)
{
m_quadric = ::gluNewQuadric();
if (m_quadric != nullptr)
// using GLU_FILL does not work when the instance's transformation
// contains mirroring (normals are reverted)
::gluQuadricDrawStyle(m_quadric, GLU_FILL);
}
GLGizmoSlaSupports::~GLGizmoSlaSupports()
{
if (m_quadric != nullptr)
::gluDeleteQuadric(m_quadric);
}
bool GLGizmoSlaSupports::on_init()
{
m_shortcut_key = WXK_CONTROL_L;
return true;
}
void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection)
{
m_starting_center = Vec3d::Zero();
m_old_model_object = m_model_object;
m_model_object = model_object;
if (selection.is_empty())
m_old_instance_id = -1;
m_active_instance = selection.get_instance_idx();
if (model_object && selection.is_from_single_instance())
{
if (is_mesh_update_necessary()) {
update_mesh();
editing_mode_reload_cache();
}
if (m_model_object != m_old_model_object)
m_editing_mode = false;
if (m_editing_mode_cache.empty() && m_model_object->sla_points_status != sla::PointsStatus::UserModified)
get_data_from_backend();
if (m_state == On) {
m_parent.toggle_model_objects_visibility(false);
m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance);
}
}
}
void GLGizmoSlaSupports::on_render(const GLCanvas3D::Selection& selection) const
{
::glEnable(GL_BLEND);
::glEnable(GL_DEPTH_TEST);
render_points(selection, false);
render_selection_rectangle();
::glDisable(GL_BLEND);
}
void GLGizmoSlaSupports::render_selection_rectangle() const
{
if (!m_selection_rectangle_active)
return;
::glLineWidth(1.5f);
float render_color[3] = {1.f, 0.f, 0.f};
::glColor3fv(render_color);
::glPushAttrib(GL_TRANSFORM_BIT); // remember current MatrixMode
::glMatrixMode(GL_MODELVIEW); // cache modelview matrix and set to identity
::glPushMatrix();
::glLoadIdentity();
::glMatrixMode(GL_PROJECTION); // cache projection matrix and set to identity
::glPushMatrix();
::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
// render the selection rectangle (window coordinates):
::glPushAttrib(GL_ENABLE_BIT);
::glLineStipple(4, 0xAAAA);
::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();
::glPopMatrix(); // restore former projection matrix
::glMatrixMode(GL_MODELVIEW);
::glPopMatrix(); // restore former modelview matrix
::glPopAttrib(); // restore former MatrixMode
}
void GLGizmoSlaSupports::on_render_for_picking(const GLCanvas3D::Selection& selection) const
{
::glEnable(GL_DEPTH_TEST);
render_points(selection, true);
}
void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, bool picking) const
{
if (m_quadric == nullptr || !selection.is_from_single_instance())
return;
if (!picking)
::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());
float render_color[3];
for (int i = 0; i < (int)m_editing_mode_cache.size(); ++i)
{
const sla::SupportPoint& support_point = m_editing_mode_cache[i].support_point;
const bool& point_selected = m_editing_mode_cache[i].selected;
// First decide about the color of the point.
if (picking) {
std::array<float, 3> color = picking_color_component(i);
render_color[0] = color[0];
render_color[1] = color[1];
render_color[2] = color[2];
}
else {
if ((m_hover_id == i && m_editing_mode)) { // ignore hover state unless editing mode is active
render_color[0] = 0.f;
render_color[1] = 1.0f;
render_color[2] = 1.0f;
}
else { // neigher hover nor picking
bool supports_new_island = m_lock_unique_islands && m_editing_mode_cache[i].support_point.is_new_island;
if (m_editing_mode) {
render_color[0] = point_selected ? 1.0f : (supports_new_island ? 0.3f : 0.7f);
render_color[1] = point_selected ? 0.3f : (supports_new_island ? 0.3f : 0.7f);
render_color[2] = point_selected ? 0.3f : (supports_new_island ? 1.0f : 0.7f);
}
else
for (unsigned char i=0; i<3; ++i) render_color[i] = 0.5f;
}
}
::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);
// 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());
// Matrices set, we can render the point mark now.
// If in editing mode, we'll also render a cone pointing to the sphere.
if (m_editing_mode) {
if (m_editing_mode_cache[i].normal == Vec3f::Zero())
update_cache_entry_normal(i); // in case the normal is not yet cached, find and cache it
Eigen::Quaterniond q;
q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * m_editing_mode_cache[i].normal.cast<double>());
Eigen::AngleAxisd aa(q);
::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);
::gluCylinder(m_quadric, 0.f, cone_radius, cone_height, 36, 1);
::glTranslatef(0.f, 0.f, cone_height);
::gluDisk(m_quadric, 0.0, cone_radius, 36, 1);
::glPopMatrix();
}
::gluSphere(m_quadric, m_editing_mode_cache[i].support_point.head_front_radius * RenderPointScale, 64, 36);
::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);
}
if (!picking)
::glDisable(GL_LIGHTING);
::glPopMatrix();
}
bool GLGizmoSlaSupports::is_mesh_update_necessary() const
{
return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty())
&& ((m_model_object != m_old_model_object) || m_V.size()==0);
//if (m_state != On || !m_model_object || m_model_object->instances.empty() || ! m_instance_matrix.isApprox(m_source_data.matrix))
// return false;
}
void GLGizmoSlaSupports::update_mesh()
{
wxBusyCursor wait;
Eigen::MatrixXf& V = m_V;
Eigen::MatrixXi& F = m_F;
// Composite mesh of all instances in the world coordinate system.
// This mesh does not account for the possible Z up SLA offset.
TriangleMesh mesh = m_model_object->raw_mesh();
const stl_file& stl = mesh.stl;
V.resize(3 * stl.stats.number_of_facets, 3);
F.resize(stl.stats.number_of_facets, 3);
for (unsigned int i=0; i<stl.stats.number_of_facets; ++i) {
const stl_facet* facet = stl.facet_start+i;
V(3*i+0, 0) = facet->vertex[0](0); V(3*i+0, 1) = facet->vertex[0](1); V(3*i+0, 2) = facet->vertex[0](2);
V(3*i+1, 0) = facet->vertex[1](0); V(3*i+1, 1) = facet->vertex[1](1); V(3*i+1, 2) = facet->vertex[1](2);
V(3*i+2, 0) = facet->vertex[2](0); V(3*i+2, 1) = facet->vertex[2](1); V(3*i+2, 2) = facet->vertex[2](2);
F(i, 0) = 3*i+0;
F(i, 1) = 3*i+1;
F(i, 2) = 3*i+2;
}
m_AABB = igl::AABB<Eigen::MatrixXf,3>();
m_AABB.init(m_V, m_F);
}
std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos)
{
// if the gizmo doesn't have the V, F structures for igl, calculate them first:
if (m_V.size() == 0)
update_mesh();
Eigen::Matrix<GLint, 4, 1, Eigen::DontAlign> viewport;
::glGetIntegerv(GL_VIEWPORT, viewport.data());
Eigen::Matrix<GLdouble, 4, 4, Eigen::DontAlign> modelview_matrix;
::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data());
Eigen::Matrix<GLdouble, 4, 4, Eigen::DontAlign> projection_matrix;
::glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix.data());
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));
igl::Hit hit;
const GLCanvas3D::Selection& selection = m_parent.get_selection();
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
double z_offset = volume->get_sla_shift_z();
point1(2) -= z_offset;
point2(2) -= z_offset;
Transform3d inv = volume->get_instance_transformation().get_matrix().inverse();
point1 = inv * point1;
point2 = inv * point2;
if (!m_AABB.intersect_ray(m_V, m_F, point1.cast<float>(), (point2-point1).cast<float>(), hit))
throw std::invalid_argument("unproject_on_mesh(): No intersection found.");
int fid = hit.id; // facet id
Vec3f bc(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit
Vec3f a = (m_V.row(m_F(fid, 1)) - m_V.row(m_F(fid, 0)));
Vec3f b = (m_V.row(m_F(fid, 2)) - m_V.row(m_F(fid, 0)));
// Calculate and return both the point and the facet normal.
return std::make_pair(
bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2)),
a.cross(b)
);
}
// Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event.
// The gizmo has an opportunity to react - if it does, it should return true so that the Canvas3D is
// aware that the event was reacted to and stops trying to make different sense of it. If the gizmo
// concludes that the event was not intended for it, it should return false.
bool GLGizmoSlaSupports::mouse_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down)
{
if (m_editing_mode) {
// left down - show the selection rectangle:
if (action == SLAGizmoEventType::LeftDown && shift_down) {
if (m_hover_id == -1) {
m_selection_rectangle_active = true;
m_selection_rectangle_start_corner = mouse_position;
m_selection_rectangle_end_corner = mouse_position;
m_canvas_width = m_parent.get_canvas_size().get_width();
m_canvas_height = m_parent.get_canvas_size().get_height();
}
else
select_point(m_hover_id);
return true;
}
// dragging the selection rectangle:
if (action == SLAGizmoEventType::Dragging && m_selection_rectangle_active) {
m_selection_rectangle_end_corner = mouse_position;
return true;
}
// mouse up without selection rectangle - place point on the mesh:
if (action == SLAGizmoEventType::LeftUp && !m_selection_rectangle_active && !shift_down) {
if (m_ignore_up_event) {
m_ignore_up_event = false;
return false;
}
int instance_id = m_parent.get_selection().get_instance_idx();
if (m_old_instance_id != instance_id)
{
bool something_selected = (m_old_instance_id != -1);
m_old_instance_id = instance_id;
if (something_selected)
return false;
}
if (instance_id == -1)
return false;
// If there is some selection, don't add new point and deselect everything instead.
if (m_selection_empty) {
try {
std::pair<Vec3f, Vec3f> pos_and_normal = unproject_on_mesh(mouse_position); // don't create anything if this throws
m_editing_mode_cache.emplace_back(sla::SupportPoint(pos_and_normal.first, m_new_point_head_diameter/2.f, false), false, pos_and_normal.second);
m_unsaved_changes = true;
}
catch (...) { // not clicked on object
return true; // prevents deselection of the gizmo by GLCanvas3D
}
}
else
select_point(NoPoints);
return true;
}
// left up with selection rectangle - select points inside the rectangle:
if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp)
&& m_selection_rectangle_active) {
if (action == SLAGizmoEventType::ShiftUp)
m_ignore_up_event = true;
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 GLCanvas3D::Selection& selection = m_parent.get_selection();
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
double z_offset = volume->get_sla_shift_z();
// bounding box created from the rectangle corners - will take care of order of the corners
BoundingBox rectangle(Points{Point(m_selection_rectangle_start_corner.cast<int>()), Point(m_selection_rectangle_end_corner.cast<int>())});
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]);
// ...and transform it to model coords.
direction_to_camera = (instance_matrix_no_translation.inverse().cast<float>() * direction_to_camera).normalized().eval();
// Iterate over all points, check if they're in the rectangle and if so, check that they are not obscured by the mesh:
for (unsigned int i=0; i<m_editing_mode_cache.size(); ++i) {
const sla::SupportPoint &support_point = m_editing_mode_cache[i].support_point;
Vec3f pos = instance_matrix.cast<float>() * 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;
if (rectangle.contains(Point(out_x, out_y))) {
bool is_obscured = false;
// Cast a ray in the direction of the camera and look for intersection with the mesh:
std::vector<igl::Hit> hits;
// Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies.
if (m_AABB.intersect_ray(m_V, m_F, support_point.pos + direction_to_camera * (support_point.head_front_radius + EPSILON), direction_to_camera, hits))
// FIXME: the intersection could in theory be behind the camera, but as of now we only have camera direction.
// Also, the threshold is in mesh coordinates, not in actual dimensions.
if (hits.size() > 1 || hits.front().t > 0.001f)
is_obscured = true;
if (!is_obscured)
select_point(i);
}
}
m_selection_rectangle_active = false;
return true;
}
if (action == SLAGizmoEventType::Delete) {
// delete key pressed
delete_selected_points();
return true;
}
if (action == SLAGizmoEventType::ApplyChanges) {
editing_mode_apply_changes();
return true;
}
if (action == SLAGizmoEventType::DiscardChanges) {
editing_mode_discard_changes();
return true;
}
if (action == SLAGizmoEventType::RightDown) {
if (m_hover_id != -1) {
select_point(NoPoints);
select_point(m_hover_id);
delete_selected_points();
return true;
}
return false;
}
if (action == SLAGizmoEventType::SelectAll) {
select_point(AllPoints);
return true;
}
}
if (!m_editing_mode) {
if (action == SLAGizmoEventType::AutomaticGeneration) {
auto_generate();
return true;
}
if (action == SLAGizmoEventType::ManualEditing) {
switch_to_editing_mode();
return true;
}
}
return false;
}
void GLGizmoSlaSupports::delete_selected_points(bool force)
{
for (unsigned int idx=0; idx<m_editing_mode_cache.size(); ++idx) {
if (m_editing_mode_cache[idx].selected && (!m_editing_mode_cache[idx].support_point.is_new_island || !m_lock_unique_islands || force)) {
m_editing_mode_cache.erase(m_editing_mode_cache.begin() + (idx--));
m_unsaved_changes = true;
}
// This should trigger the support generation
// wxGetApp().plater()->reslice_SLA_supports(*m_model_object);
}
select_point(NoPoints);
//m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
}
void GLGizmoSlaSupports::on_update(const UpdateData& data, const GLCanvas3D::Selection& selection)
{
if (m_editing_mode && m_hover_id != -1 && data.mouse_pos && (!m_editing_mode_cache[m_hover_id].support_point.is_new_island || !m_lock_unique_islands)) {
std::pair<Vec3f, Vec3f> pos_and_normal;
try {
pos_and_normal = unproject_on_mesh(Vec2d((*data.mouse_pos)(0), (*data.mouse_pos)(1)));
}
catch (...) { return; }
m_editing_mode_cache[m_hover_id].support_point.pos = pos_and_normal.first;
m_editing_mode_cache[m_hover_id].support_point.is_new_island = false;
m_editing_mode_cache[m_hover_id].normal = pos_and_normal.second;
m_unsaved_changes = true;
// Do not update immediately, wait until the mouse is released.
// m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
}
}
std::vector<const ConfigOption*> GLGizmoSlaSupports::get_config_options(const std::vector<std::string>& keys) const
{
std::vector<const ConfigOption*> out;
if (!m_model_object)
return out;
const DynamicPrintConfig& object_cfg = m_model_object->config;
const DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
std::unique_ptr<DynamicPrintConfig> default_cfg = nullptr;
for (const std::string& key : keys) {
if (object_cfg.has(key))
out.push_back(object_cfg.option(key));
else
if (print_cfg.has(key))
out.push_back(print_cfg.option(key));
else { // we must get it from defaults
if (default_cfg == nullptr)
default_cfg.reset(DynamicPrintConfig::new_from_defaults_keys(keys));
out.push_back(default_cfg->option(key));
}
}
return out;
}
void GLGizmoSlaSupports::update_cache_entry_normal(unsigned int i) const
{
int idx = 0;
Eigen::Matrix<float, 1, 3> pp = m_editing_mode_cache[i].support_point.pos;
Eigen::Matrix<float, 1, 3> cc;
m_AABB.squared_distance(m_V, m_F, pp, idx, cc);
Vec3f a = (m_V.row(m_F(idx, 1)) - m_V.row(m_F(idx, 0)));
Vec3f b = (m_V.row(m_F(idx, 2)) - m_V.row(m_F(idx, 0)));
m_editing_mode_cache[i].normal = a.cross(b);
}
void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection)
{
if (!m_model_object)
return;
bool first_run = true; // This is a hack to redraw the button when all points are removed,
// so it is not delayed until the background process finishes.
RENDER_AGAIN:
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
const float scaling = m_imgui->get_style_scaling();
const ImVec2 window_size(285.f * scaling, 300.f * scaling);
ImGui::SetNextWindowPos(ImVec2(x, y - std::max(0.f, y+window_size.y-bottom_limit) ));
ImGui::SetNextWindowSize(ImVec2(window_size));
m_imgui->set_next_window_bg_alpha(0.5f);
m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
ImGui::PushItemWidth(100.0f);
bool force_refresh = false;
bool remove_selected = false;
bool remove_all = false;
if (m_editing_mode) {
m_imgui->text(_(L("Left mouse click - add point")));
m_imgui->text(_(L("Right mouse click - remove point")));
m_imgui->text(_(L("Shift + Left (+ drag) - select point(s)")));
m_imgui->text(" "); // vertical gap
float diameter_upper_cap = static_cast<ConfigOptionFloat*>(wxGetApp().preset_bundle->sla_prints.get_edited_preset().config.option("support_pillar_diameter"))->value;
if (m_new_point_head_diameter > diameter_upper_cap)
m_new_point_head_diameter = diameter_upper_cap;
m_imgui->text(_(L("Head diameter: ")));
ImGui::SameLine();
if (ImGui::SliderFloat("", &m_new_point_head_diameter, 0.1f, diameter_upper_cap, "%.1f")) {
// value was changed
for (auto& cache_entry : m_editing_mode_cache)
if (cache_entry.selected) {
cache_entry.support_point.head_front_radius = m_new_point_head_diameter / 2.f;
m_unsaved_changes = true;
}
}
bool changed = m_lock_unique_islands;
m_imgui->checkbox(_(L("Lock supports under new islands")), m_lock_unique_islands);
force_refresh |= changed != m_lock_unique_islands;
m_imgui->disabled_begin(m_selection_empty);
remove_selected = m_imgui->button(_(L("Remove selected points")));
m_imgui->disabled_end();
m_imgui->disabled_begin(m_editing_mode_cache.empty());
remove_all = m_imgui->button(_(L("Remove all points")));
m_imgui->disabled_end();
m_imgui->text(" "); // vertical gap
if (m_imgui->button(_(L("Apply changes")))) {
editing_mode_apply_changes();
force_refresh = true;
}
ImGui::SameLine();
bool discard_changes = m_imgui->button(_(L("Discard changes")));
if (discard_changes) {
editing_mode_discard_changes();
force_refresh = true;
}
}
else { // not in editing mode:
ImGui::PushItemWidth(100.0f);
m_imgui->text(_(L("Minimal points distance: ")));
ImGui::SameLine();
std::vector<const ConfigOption*> opts = get_config_options({"support_points_density_relative", "support_points_minimal_distance"});
float density = static_cast<const ConfigOptionInt*>(opts[0])->value;
float minimal_point_distance = static_cast<const ConfigOptionFloat*>(opts[1])->value;
bool value_changed = ImGui::SliderFloat("", &minimal_point_distance, 0.f, 20.f, "%.f mm");
if (value_changed)
m_model_object->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = minimal_point_distance;
m_imgui->text(_(L("Support points density: ")));
ImGui::SameLine();
if (ImGui::SliderFloat(" ", &density, 0.f, 200.f, "%.f %%")) {
value_changed = true;
m_model_object->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)density;
}
if (value_changed) { // Update side panel
wxTheApp->CallAfter([]() {
wxGetApp().obj_settings()->UpdateAndShow(true);
wxGetApp().obj_list()->update_settings_items();
});
}
bool generate = m_imgui->button(_(L("Auto-generate points [A]")));
if (generate)
auto_generate();
m_imgui->text("");
if (m_imgui->button(_(L("Manual editing [M]"))))
switch_to_editing_mode();
m_imgui->disabled_begin(m_editing_mode_cache.empty());
remove_all = m_imgui->button(_(L("Remove all points")));
m_imgui->disabled_end();
m_imgui->text("");
m_imgui->text(m_model_object->sla_points_status == sla::PointsStatus::None ? "No points (will be autogenerated)" :
(m_model_object->sla_points_status == sla::PointsStatus::AutoGenerated ? "Autogenerated points (no modifications)" :
(m_model_object->sla_points_status == sla::PointsStatus::UserModified ? "User-modified points" :
(m_model_object->sla_points_status == sla::PointsStatus::Generating ? "Generation in progress..." : "UNKNOWN STATUS"))));
}
m_imgui->end();
if (m_editing_mode != m_old_editing_state) { // user toggled between editing/non-editing mode
m_parent.toggle_sla_auxiliaries_visibility(!m_editing_mode);
force_refresh = true;
}
m_old_editing_state = m_editing_mode;
if (remove_selected || remove_all) {
force_refresh = false;
m_parent.set_as_dirty();
if (remove_all)
select_point(AllPoints);
delete_selected_points(remove_all);
if (remove_all && !m_editing_mode)
editing_mode_apply_changes();
if (first_run) {
first_run = false;
goto RENDER_AGAIN;
}
}
if (force_refresh)
m_parent.set_as_dirty();
}
bool GLGizmoSlaSupports::on_is_activable(const GLCanvas3D::Selection& selection) const
{
if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA
|| !selection.is_from_single_instance())
return false;
// Check that none of the selected volumes is outside.
const GLCanvas3D::Selection::IndicesList& list = selection.get_volume_idxs();
for (const auto& idx : list)
if (selection.get_volume(idx)->is_outside)
return false;
return true;
}
bool GLGizmoSlaSupports::on_is_selectable() const
{
return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA);
}
std::string GLGizmoSlaSupports::on_get_name() const
{
return L("SLA Support Points [L]");
}
void GLGizmoSlaSupports::on_set_state()
{
if (m_state == On && m_old_state != On) { // the gizmo was just turned on
if (is_mesh_update_necessary())
update_mesh();
// we'll now reload support points:
if (m_model_object)
editing_mode_reload_cache();
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<const ConfigOptionFloat*>(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(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;
}
void GLGizmoSlaSupports::on_start_dragging(const GLCanvas3D::Selection& selection)
{
if (m_hover_id != -1) {
select_point(NoPoints);
select_point(m_hover_id);
}
}
void GLGizmoSlaSupports::select_point(int i)
{
if (i == AllPoints || i == NoPoints) {
for (auto& point_and_selection : m_editing_mode_cache)
point_and_selection.selected = ( i == AllPoints );
m_selection_empty = (i == NoPoints);
if (i == AllPoints)
m_new_point_head_diameter = m_editing_mode_cache[0].support_point.head_front_radius * 2.f;
}
else {
m_editing_mode_cache[i].selected = true;
m_selection_empty = false;
m_new_point_head_diameter = m_editing_mode_cache[i].support_point.head_front_radius * 2.f;
}
}
void GLGizmoSlaSupports::editing_mode_discard_changes()
{
m_editing_mode_cache.clear();
for (const sla::SupportPoint& point : m_model_object->sla_support_points)
m_editing_mode_cache.emplace_back(point, false);
m_editing_mode = false;
m_unsaved_changes = false;
}
void GLGizmoSlaSupports::editing_mode_apply_changes()
{
// If there are no changes, don't touch the front-end. The data in the cache could have been
// taken from the backend and copying them to ModelObject would needlessly invalidate them.
if (m_unsaved_changes) {
m_model_object->sla_points_status = sla::PointsStatus::UserModified;
m_model_object->sla_support_points.clear();
for (const CacheEntry& cache_entry : m_editing_mode_cache)
m_model_object->sla_support_points.push_back(cache_entry.support_point);
// Recalculate support structures once the editing mode is left.
// m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
// m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
wxGetApp().plater()->reslice_SLA_supports(*m_model_object);
}
m_editing_mode = false;
m_unsaved_changes = false;
}
void GLGizmoSlaSupports::editing_mode_reload_cache()
{
m_editing_mode_cache.clear();
for (const sla::SupportPoint& point : m_model_object->sla_support_points)
m_editing_mode_cache.emplace_back(point, false);
m_unsaved_changes = false;
}
void GLGizmoSlaSupports::get_data_from_backend()
{
for (const SLAPrintObject* po : m_parent.sla_print()->objects()) {
if (po->model_object()->id() == m_model_object->id() && po->is_step_done(slaposSupportPoints)) {
m_editing_mode_cache.clear();
const std::vector<sla::SupportPoint>& points = po->get_support_points();
auto mat = po->trafo().inverse().cast<float>();
for (unsigned int i=0; i<points.size();++i)
m_editing_mode_cache.emplace_back(sla::SupportPoint(mat * points[i].pos, points[i].head_front_radius, points[i].is_new_island), false);
if (m_model_object->sla_points_status != sla::PointsStatus::UserModified)
m_model_object->sla_points_status = sla::PointsStatus::AutoGenerated;
break;
}
}
m_unsaved_changes = false;
// We don't copy the data into ModelObject, as this would stop the background processing.
}
void GLGizmoSlaSupports::auto_generate()
{
wxMessageDialog dlg(GUI::wxGetApp().plater(), _(L(
"Autogeneration will erase all manually edited points.\n\n"
"Are you sure you want to do it?\n"
)), _(L("Warning")), wxICON_WARNING | wxYES | wxNO);
if (m_model_object->sla_points_status != sla::PointsStatus::UserModified || m_editing_mode_cache.empty() || dlg.ShowModal() == wxID_YES) {
m_model_object->sla_support_points.clear();
m_model_object->sla_points_status = sla::PointsStatus::Generating;
m_editing_mode_cache.clear();
wxGetApp().plater()->reslice_SLA_supports(*m_model_object);
}
}
void GLGizmoSlaSupports::switch_to_editing_mode()
{
if (m_model_object->sla_points_status != sla::PointsStatus::AutoGenerated)
editing_mode_reload_cache();
m_unsaved_changes = false;
m_editing_mode = true;
}
} // namespace GUI
} // namespace Slic3r

View File

@ -0,0 +1,128 @@
#ifndef slic3r_GLGizmoSlaSupports_hpp_
#define slic3r_GLGizmoSlaSupports_hpp_
#include "GLGizmoBase.hpp"
// There is an L function in igl that would be overridden by our localization macro - let's undefine it...
#undef L
#include <igl/AABB.h>
#include "slic3r/GUI/I18N.hpp" // ...and redefine again when we are done with the igl code
#include "libslic3r/SLA/SLACommon.hpp"
#include "libslic3r/SLAPrint.hpp"
namespace Slic3r {
namespace GUI {
class GLGizmoSlaSupports : public GLGizmoBase
{
private:
ModelObject* m_model_object = nullptr;
ModelObject* m_old_model_object = nullptr;
int m_active_instance = -1;
int m_old_instance_id = -1;
std::pair<Vec3f, Vec3f> unproject_on_mesh(const Vec2d& mouse_pos);
const float RenderPointScale = 1.f;
GLUquadricObj* m_quadric;
Eigen::MatrixXf m_V; // vertices
Eigen::MatrixXi m_F; // facets indices
igl::AABB<Eigen::MatrixXf,3> m_AABB;
struct SourceDataSummary {
Geometry::Transformation transformation;
};
class CacheEntry {
public:
CacheEntry(const sla::SupportPoint& point, bool sel, const Vec3f& norm = Vec3f::Zero()) :
support_point(point), selected(sel), normal(norm) {}
sla::SupportPoint support_point;
bool selected; // whether the point is selected
Vec3f normal;
};
// This holds information to decide whether recalculation is necessary:
SourceDataSummary m_source_data;
mutable Vec3d m_starting_center;
public:
#if ENABLE_SVG_ICONS
GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
virtual ~GLGizmoSlaSupports();
void set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection);
bool mouse_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down);
void delete_selected_points(bool force = false);
std::pair<float, float> get_sla_clipping_plane() const;
private:
bool on_init();
void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection);
virtual void on_render(const GLCanvas3D::Selection& selection) const;
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const;
void render_selection_rectangle() const;
void render_points(const GLCanvas3D::Selection& selection, bool picking = false) const;
bool is_mesh_update_necessary() const;
void update_mesh();
void update_cache_entry_normal(unsigned int i) const;
bool m_lock_unique_islands = false;
bool m_editing_mode = false; // Is editing mode active?
bool m_old_editing_state = false; // To keep track of whether the user toggled between the modes (needed for imgui refreshes).
float m_new_point_head_diameter; // Size of a new point.
float m_minimal_point_distance = 20.f;
float m_density = 100.f;
mutable std::vector<CacheEntry> m_editing_mode_cache; // a support point and whether it is currently selected
float m_clipping_plane_distance = 0.f;
bool m_selection_rectangle_active = false;
Vec2d m_selection_rectangle_start_corner;
Vec2d m_selection_rectangle_end_corner;
bool m_ignore_up_event = false;
bool m_combo_box_open = false; // To ensure proper rendering of the imgui combobox.
bool m_unsaved_changes = false; // Are there unsaved changes in manual mode?
bool m_selection_empty = true;
EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state)
int m_canvas_width;
int m_canvas_height;
std::vector<const ConfigOption*> get_config_options(const std::vector<std::string>& keys) const;
// Methods that do the model_object and editing cache synchronization,
// editing mode selection, etc:
enum {
AllPoints = -2,
NoPoints,
};
void select_point(int i);
void editing_mode_apply_changes();
void editing_mode_discard_changes();
void editing_mode_reload_cache();
void get_data_from_backend();
void auto_generate();
void switch_to_editing_mode();
protected:
void on_set_state() override;
void on_start_dragging(const GLCanvas3D::Selection& selection) override;
virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection) override;
virtual std::string on_get_name() const;
virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const;
virtual bool on_is_selectable() const;
};
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GLGizmoSlaSupports_hpp_

View File

@ -0,0 +1,11 @@
#ifndef slic3r_GLGizmos_hpp_
#define slic3r_GLGizmos_hpp_
#include "slic3r/GUI/Gizmos/GLGizmoMove.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoScale.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoRotate.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoFlatten.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoCut.hpp"
#endif //slic3r_GLGizmos_hpp_

View File

@ -107,11 +107,11 @@ bool ImGuiWrapper::update_mouse_data(wxMouseEvent& evt)
ImGuiIO& io = ImGui::GetIO();
io.MousePos = ImVec2((float)evt.GetX(), (float)evt.GetY());
io.MouseDown[0] = evt.LeftDown();
io.MouseDown[1] = evt.RightDown();
io.MouseDown[2] = evt.MiddleDown();
io.MouseDown[0] = evt.LeftIsDown();
io.MouseDown[1] = evt.RightIsDown();
io.MouseDown[2] = evt.MiddleIsDown();
unsigned buttons = (evt.LeftDown() ? 1 : 0) | (evt.RightDown() ? 2 : 0) | (evt.MiddleDown() ? 4 : 0);
unsigned buttons = (evt.LeftIsDown() ? 1 : 0) | (evt.RightIsDown() ? 2 : 0) | (evt.MiddleIsDown() ? 4 : 0);
m_mouse_buttons = buttons;
new_frame();
@ -441,6 +441,10 @@ void ImGuiWrapper::init_style()
set_color(ImGuiCol_Header, COL_ORANGE_DARK);
set_color(ImGuiCol_HeaderHovered, COL_ORANGE_LIGHT);
set_color(ImGuiCol_HeaderActive, COL_ORANGE_LIGHT);
// Slider
set_color(ImGuiCol_SliderGrab, COL_ORANGE_DARK);
set_color(ImGuiCol_SliderGrabActive, COL_ORANGE_LIGHT);
}
void ImGuiWrapper::render_draw_data(ImDrawData *draw_data)

View File

@ -56,7 +56,8 @@ 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(size.x-1);
wxGetApp().set_em_unit(std::max<size_t>(10, size.x - 1));
// initialize tabpanel and menubar
init_tabpanel();
@ -76,12 +77,14 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
sizer->SetSizeHints(this);
SetSizer(sizer);
Fit();
const wxSize min_size = wxSize(76*wxGetApp().em_unit(), 49*wxGetApp().em_unit());
#ifdef __APPLE__
// Using SetMinSize() on Mac messes up the window position in some cases
// cf. https://groups.google.com/forum/#!topic/wx-users/yUKPBBfXWO0
SetSize(wxSize(760, 490));
SetSize(min_size/*wxSize(760, 490)*/);
#else
SetMinSize(wxSize(760, 490));
SetMinSize(min_size/*wxSize(760, 490)*/);
SetSize(GetMinSize());
#endif
Layout();
@ -93,6 +96,12 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
return;
}
// Weird things happen as the Paint messages are floating around the windows being destructed.
// Avoid the Paint messages by hiding the main window.
// Also the application closes much faster without these unnecessary screen refreshes.
// In addition, there were some crashes due to the Paint events sent to already destructed windows.
this->Show(false);
// Save the slic3r.ini.Usually the ini file is saved from "on idle" callback,
// but in rare cases it may not have been called yet.
wxGetApp().app_config->save();
@ -124,7 +133,9 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
void MainFrame::init_tabpanel()
{
m_tabpanel = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL);
// wxNB_NOPAGETHEME: Disable Windows Vista theme for the Notebook background. The theme performance is terrible on Windows 10
// with multiple high resolution displays connected.
m_tabpanel = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME);
m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxEvent&) {
auto panel = m_tabpanel->GetCurrentPage();
@ -881,9 +892,10 @@ void MainFrame::on_config_changed(DynamicPrintConfig* config) const
// Update the UI based on the current preferences.
void MainFrame::update_ui_from_settings()
{
bool bp_on = wxGetApp().app_config->get("background_processing") == "1";
const bool bp_on = wxGetApp().app_config->get("background_processing") == "1";
// m_menu_item_reslice_now->Enable(!bp_on);
m_plater->sidebar().show_reslice(!bp_on);
m_plater->sidebar().show_export(bp_on);
m_plater->sidebar().Layout();
if (m_plater)
m_plater->update_ui_from_settings();

View File

@ -24,12 +24,11 @@ namespace GUI {
MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxWindowID button_id) :
// MsgDialog(parent, title, headline, wxBitmap(from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG), button_id)
MsgDialog(parent, title, headline, create_scaled_bitmap("Slic3r_192px.png"), button_id)
{}
MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxBitmap bitmap, wxWindowID button_id) :
wxDialog(parent, wxID_ANY, title),
wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxRESIZE_BORDER),
boldfont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)),
content_sizer(new wxBoxSizer(wxVERTICAL)),
btn_sizer(new wxBoxSizer(wxHORIZONTAL))
@ -70,7 +69,6 @@ MsgDialog::~MsgDialog() {}
ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg)
: MsgDialog(parent, _(L("Slic3r error")), _(L("Slic3r has encountered an error")),
// wxBitmap(from_u8(Slic3r::var("Slic3r_192px_grayscale.png")), wxBITMAP_TYPE_PNG),
create_scaled_bitmap("Slic3r_192px_grayscale.png"),
wxID_NONE)
, msg(msg)

View File

@ -23,18 +23,18 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
// is the normal type.
if (opt.gui_type.compare("select") == 0) {
} else if (opt.gui_type.compare("select_open") == 0) {
m_fields.emplace(id, std::move(Choice::Create<Choice>(parent(), opt, id)));
m_fields.emplace(id, std::move(Choice::Create<Choice>(this->ctrl_parent(), opt, id)));
} else if (opt.gui_type.compare("color") == 0) {
m_fields.emplace(id, std::move(ColourPicker::Create<ColourPicker>(parent(), opt, id)));
m_fields.emplace(id, std::move(ColourPicker::Create<ColourPicker>(this->ctrl_parent(), opt, id)));
} else if (opt.gui_type.compare("f_enum_open") == 0 ||
opt.gui_type.compare("i_enum_open") == 0 ||
opt.gui_type.compare("i_enum_closed") == 0) {
m_fields.emplace(id, std::move(Choice::Create<Choice>(parent(), opt, id)));
m_fields.emplace(id, std::move(Choice::Create<Choice>(this->ctrl_parent(), opt, id)));
} else if (opt.gui_type.compare("slider") == 0) {
m_fields.emplace(id, std::move(SliderCtrl::Create<SliderCtrl>(parent(), opt, id)));
m_fields.emplace(id, std::move(SliderCtrl::Create<SliderCtrl>(this->ctrl_parent(), opt, id)));
} else if (opt.gui_type.compare("i_spin") == 0) { // Spinctrl
} else if (opt.gui_type.compare("legend") == 0) { // StaticText
m_fields.emplace(id, std::move(StaticText::Create<StaticText>(parent(), opt, id)));
m_fields.emplace(id, std::move(StaticText::Create<StaticText>(this->ctrl_parent(), opt, id)));
} else {
switch (opt.type) {
case coFloatOrPercent:
@ -44,21 +44,21 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
case coPercents:
case coString:
case coStrings:
m_fields.emplace(id, std::move(TextCtrl::Create<TextCtrl>(parent(), opt, id)));
m_fields.emplace(id, std::move(TextCtrl::Create<TextCtrl>(this->ctrl_parent(), opt, id)));
break;
case coBool:
case coBools:
m_fields.emplace(id, std::move(CheckBox::Create<CheckBox>(parent(), opt, id)));
m_fields.emplace(id, std::move(CheckBox::Create<CheckBox>(this->ctrl_parent(), opt, id)));
break;
case coInt:
case coInts:
m_fields.emplace(id, std::move(SpinCtrl::Create<SpinCtrl>(parent(), opt, id)));
m_fields.emplace(id, std::move(SpinCtrl::Create<SpinCtrl>(this->ctrl_parent(), opt, id)));
break;
case coEnum:
m_fields.emplace(id, std::move(Choice::Create<Choice>(parent(), opt, id)));
m_fields.emplace(id, std::move(Choice::Create<Choice>(this->ctrl_parent(), opt, id)));
break;
case coPoints:
m_fields.emplace(id, std::move(PointCtrl::Create<PointCtrl>(parent(), opt, id)));
m_fields.emplace(id, std::move(PointCtrl::Create<PointCtrl>(this->ctrl_parent(), opt, id)));
break;
case coNone: break;
default:
@ -119,7 +119,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
return;
}
if (line.widget != nullptr) {
sizer->Add(line.widget(m_parent), 0, wxEXPAND | wxALL, wxOSX ? 0 : 15);
sizer->Add(line.widget(this->ctrl_parent()), 0, wxEXPAND | wxALL, wxOSX ? 0 : 15);
return;
}
}
@ -167,7 +167,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
// if we have an extra column, build it
if (extra_column)
grid_sizer->Add(extra_column(parent(), line), 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 3);
grid_sizer->Add(extra_column(this->ctrl_parent(), line), 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 3);
// Build a label if we have it
wxStaticText* label=nullptr;
@ -179,18 +179,21 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
// Text is properly aligned only when Ellipsize is checked.
label_style |= staticbox ? 0 : wxST_ELLIPSIZE_END;
#endif /* __WXGTK__ */
label = new wxStaticText(parent(), wxID_ANY, line.label + (line.label.IsEmpty() ? "" : ": "),
label = new wxStaticText(this->ctrl_parent(), wxID_ANY, line.label + (line.label.IsEmpty() ? "" : ": "),
wxDefaultPosition, wxSize(label_width, -1), label_style);
label->SetBackgroundStyle(wxBG_STYLE_PAINT);
label->SetFont(label_font);
label->Wrap(label_width); // avoid a Linux/GTK bug
if (!line.near_label_widget)
grid_sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, line.label.IsEmpty() ? 0 : 5);
else if (line.near_label_widget && line.label.IsEmpty())
grid_sizer->Add(line.near_label_widget(this->ctrl_parent()), 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 7);
else {
// If we're here, we have some widget near the label
// so we need a horizontal sizer to arrange these things
auto sizer = new wxBoxSizer(wxHORIZONTAL);
grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM | wxTOP | wxLEFT), staticbox ? 0 : 1);
sizer->Add(line.near_label_widget(parent()), 0, wxRIGHT, 7);
sizer->Add(line.near_label_widget(this->ctrl_parent()), 0, wxRIGHT, 7);
sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, 5);
}
if (line.label_tooltip.compare("") != 0)
@ -201,7 +204,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
*full_Label = label; // Initiate the pointer to the control of the full label, if we need this one.
// If there's a widget, build it and add the result to the sizer.
if (line.widget != nullptr) {
auto wgt = line.widget(parent());
auto wgt = line.widget(this->ctrl_parent());
// If widget doesn't have label, don't use border
grid_sizer->Add(wgt, 0, wxEXPAND | wxBOTTOM | wxTOP, (wxOSX || line.label.IsEmpty()) ? 0 : 5);
return;
@ -237,7 +240,8 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
wxString str_label = (option.label == "Top" || option.label == "Bottom") ?
_CTX(option.label, "Layers") :
_(option.label);
label = new wxStaticText(parent(), wxID_ANY, str_label + ": ", wxDefaultPosition, wxDefaultSize);
label = new wxStaticText(this->ctrl_parent(), wxID_ANY, str_label + ": ", wxDefaultPosition, wxDefaultSize);
label->SetBackgroundStyle(wxBG_STYLE_PAINT);
label->SetFont(label_font);
sizer_tmp->Add(label, 0, /*wxALIGN_RIGHT |*/ wxALIGN_CENTER_VERTICAL, 0);
}
@ -262,8 +266,9 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
// add sidetext if any
if (option.sidetext != "") {
auto sidetext = new wxStaticText( parent(), wxID_ANY, _(option.sidetext), wxDefaultPosition,
auto sidetext = new wxStaticText( this->ctrl_parent(), wxID_ANY, _(option.sidetext), wxDefaultPosition,
wxSize(sidetext_width, -1)/*wxDefaultSize*/, wxALIGN_LEFT);
sidetext->SetBackgroundStyle(wxBG_STYLE_PAINT);
sidetext->SetFont(sidetext_font);
sizer_tmp->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4);
field->set_side_text_ptr(sidetext);
@ -271,7 +276,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
// add side widget if any
if (opt.side_widget != nullptr) {
sizer_tmp->Add(opt.side_widget(parent())/*!.target<wxWindow>()*/, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 1); //! requires verification
sizer_tmp->Add(opt.side_widget(this->ctrl_parent())/*!.target<wxWindow>()*/, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 1); //! requires verification
}
if (opt.opt_id != option_set.back().opt_id) //! istead of (opt != option_set.back())
@ -287,11 +292,11 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
// extra widget for non-staticbox option group (like for the frequently used parameters on the sidebar) should be wxALIGN_RIGHT
const auto v_sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(v_sizer, 1, wxEXPAND);
v_sizer->Add(extra_widget(parent()), 0, wxALIGN_RIGHT);
v_sizer->Add(extra_widget(this->ctrl_parent()), 0, wxALIGN_RIGHT);
return;
}
sizer->Add(extra_widget(parent())/*!.target<wxWindow>()*/, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4); //! requires verification
sizer->Add(extra_widget(this->ctrl_parent())/*!.target<wxWindow>()*/, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4); //! requires verification
}
}

View File

@ -112,6 +112,10 @@ public:
}
#endif /* __WXGTK__ */
wxWindow* ctrl_parent() const {
return this->stb ? (wxWindow*)this->stb : this->parent();
}
void append_line(const Line& line, wxStaticText** full_Label = nullptr);
Line create_single_option_line(const Option& option) const;
void append_single_option_line(const Option& option) { append_line(create_single_option_line(option)); }
@ -161,8 +165,10 @@ public:
staticbox(title!=""), extra_column(extra_clmn) {
if (staticbox) {
stb = new wxStaticBox(_parent, wxID_ANY, title);
stb->SetBackgroundStyle(wxBG_STYLE_PAINT);
stb->SetFont(wxGetApp().bold_font());
}
} else
stb = nullptr;
sizer = (staticbox ? new wxStaticBoxSizer(stb, wxVERTICAL) : new wxBoxSizer(wxVERTICAL));
auto num_columns = 1U;
if (label_width != 0) num_columns++;

View File

@ -50,6 +50,7 @@
#include "GLToolbar.hpp"
#include "GUI_Preview.hpp"
#include "3DBed.hpp"
#include "Camera.hpp"
#include "Tab.hpp"
#include "PresetBundle.hpp"
#include "BackgroundSlicingProcess.hpp"
@ -364,20 +365,20 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
Line line = Line { "", "" };
ConfigOptionDef def;
def.label = L("Supports");
def.type = coStrings;
def.gui_type = "select_open";
def.tooltip = L("Select what kind of support do you need");
def.enum_labels.push_back(L("None"));
def.enum_labels.push_back(L("Support on build plate only"));
def.enum_labels.push_back(L("Everywhere"));
const std::string selection = !config->opt_bool("support_material") ?
"None" : config->opt_bool("support_material_buildplate_only") ?
"Support on build plate only" :
"Everywhere";
def.default_value = new ConfigOptionStrings{ selection };
Option option = Option(def, "support");
ConfigOptionDef support_def;
support_def.label = L("Supports");
support_def.type = coStrings;
support_def.gui_type = "select_open";
support_def.tooltip = L("Select what kind of support do you need");
support_def.enum_labels.push_back(L("None"));
support_def.enum_labels.push_back(L("Support on build plate only"));
support_def.enum_labels.push_back(L("Everywhere"));
std::string selection = !config->opt_bool("support_material") ?
"None" : config->opt_bool("support_material_buildplate_only") ?
"Support on build plate only" :
"Everywhere";
support_def.default_value = new ConfigOptionStrings{ selection };
Option option = Option(support_def, "support");
option.opt.full_width = true;
line.append_option(option);
m_og->append_line(line);
@ -392,6 +393,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
line.append_option(option);
m_brim_width = config->opt_float("brim_width");
ConfigOptionDef def;
def.label = L("Brim");
def.type = coBool;
def.tooltip = L("This flag enables the brim that will be printed around each object on the first layer.");
@ -427,6 +429,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
m_og->append_line(line);
// Frequently changed parameters for SLA_technology
m_og_sla = std::make_shared<ConfigOptionsGroup>(parent, "");
DynamicPrintConfig* config_sla = &wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
@ -437,20 +440,43 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
Tab* tab = wxGetApp().get_tab(Preset::TYPE_SLA_PRINT);
if (!tab) return;
tab->set_value(opt_key, value);
if (opt_key == "pad_enable") {
tab->set_value(opt_key, value);
tab->update();
}
else //(opt_key == "support")
{
DynamicPrintConfig new_conf = *config_sla;
const wxString& selection = boost::any_cast<wxString>(value);
const bool supports_enable = selection == _("None") ? false : true;
new_conf.set_key_value("supports_enable", new ConfigOptionBool(supports_enable));
if (selection == _("Everywhere"))
new_conf.set_key_value("support_buildplate_only", new ConfigOptionBool(false));
else if (selection == _("Support on build plate only"))
new_conf.set_key_value("support_buildplate_only", new ConfigOptionBool(true));
tab->load_config(new_conf);
}
DynamicPrintConfig new_conf = *config_sla;
new_conf.set_key_value(opt_key, new ConfigOptionBool(boost::any_cast<bool>(value)));
tab->load_config(new_conf);
tab->update_dirty();
};
};
line = Line{ "", "" };
option = m_og_sla->get_option("supports_enable");
option.opt.sidetext = " ";
selection = !config_sla->opt_bool("supports_enable") ?
"None" : config_sla->opt_bool("support_buildplate_only") ?
"Support on build plate only" :
"Everywhere";
support_def.default_value = new ConfigOptionStrings{ selection };
option = Option(support_def, "support");
option.opt.full_width = true;
line.append_option(option);
m_og_sla->append_line(line);
line = Line{ "", "" };
option = m_og_sla->get_option("pad_enable");
option.opt.sidetext = " ";
@ -487,11 +513,18 @@ ConfigOptionsGroup* FreqChangedParams::get_og(const bool is_fff)
// Sidebar / private
enum class ActionButtonType : int {
abReslice,
abExport,
abSendGCode
};
struct Sidebar::priv
{
Plater *plater;
wxScrolledWindow *scrolled;
wxPanel* presets_panel; // Used for MSW better layouts
PrusaModeSizer *mode_sizer;
wxFlexGridSizer *sizer_presets;
@ -523,8 +556,6 @@ void Sidebar::priv::show_preset_comboboxes()
{
const bool showSLA = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA;
wxWindowUpdateLocker noUpdates_scrolled(scrolled->GetParent());
for (size_t i = 0; i < 4; ++i)
sizer_presets->Show(i, !showSLA);
@ -548,6 +579,7 @@ Sidebar::Sidebar(Plater *parent)
p->scrolled = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(40 * wxGetApp().em_unit(), -1));
p->scrolled->SetScrollbars(0, 20, 1, 2);
// Sizer in the scrolled area
auto *scrolled_sizer = new wxBoxSizer(wxVERTICAL);
p->scrolled->SetSizer(scrolled_sizer);
@ -559,12 +591,25 @@ Sidebar::Sidebar(Plater *parent)
p->sizer_presets = new wxFlexGridSizer(10, 1, 1, 2);
p->sizer_presets->AddGrowableCol(0, 1);
p->sizer_presets->SetFlexibleDirection(wxBOTH);
bool is_msw = false;
#ifdef __WINDOWS__
p->scrolled->SetDoubleBuffered(true);
p->presets_panel = new wxPanel(p->scrolled, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
p->presets_panel->SetSizer(p->sizer_presets);
is_msw = true;
#else
p->presets_panel = p->scrolled;
#endif //__WINDOWS__
p->sizer_filaments = new wxBoxSizer(wxVERTICAL);
auto init_combo = [this](PresetComboBox **combo, wxString label, Preset::Type preset_type, bool filament) {
auto *text = new wxStaticText(p->scrolled, wxID_ANY, label+" :");
auto *text = new wxStaticText(p->presets_panel, wxID_ANY, label + " :");
text->SetFont(wxGetApp().small_font());
*combo = new PresetComboBox(p->scrolled, preset_type);
*combo = new PresetComboBox(p->presets_panel, preset_type);
auto *sizer_presets = this->p->sizer_presets;
auto *sizer_filaments = this->p->sizer_filaments;
@ -613,9 +658,7 @@ Sidebar::Sidebar(Plater *parent)
p->object_settings->Hide();
p->sizer_params->Add(p->object_settings->get_sizer(), 0, wxEXPAND | wxTOP, margin_5);
wxBitmap arrow_up(GUI::from_u8(Slic3r::var("brick_go.png")), wxBITMAP_TYPE_PNG);
p->btn_send_gcode = new wxButton(this, wxID_ANY, _(L("Send to printer")));
p->btn_send_gcode->SetBitmap(arrow_up);
p->btn_send_gcode->SetFont(wxGetApp().bold_font());
p->btn_send_gcode->Hide();
@ -625,7 +668,9 @@ Sidebar::Sidebar(Plater *parent)
// Sizer in the scrolled area
scrolled_sizer->Add(p->mode_sizer, 0, wxALIGN_CENTER_HORIZONTAL/*RIGHT | wxBOTTOM | wxRIGHT, 5*/);
scrolled_sizer->Add(p->sizer_presets, 0, wxEXPAND | wxLEFT, margin_5);
is_msw ?
scrolled_sizer->Add(p->presets_panel, 0, wxEXPAND | wxLEFT, margin_5) :
scrolled_sizer->Add(p->sizer_presets, 0, wxEXPAND | wxLEFT, margin_5);
scrolled_sizer->Add(p->sizer_params, 1, wxEXPAND | wxLEFT, margin_5);
scrolled_sizer->Add(p->object_info, 0, wxEXPAND | wxTOP | wxLEFT, margin_5);
scrolled_sizer->Add(p->sliced_info, 0, wxEXPAND | wxTOP | wxLEFT, margin_5);
@ -649,14 +694,21 @@ Sidebar::Sidebar(Plater *parent)
// Events
p->btn_export_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(); });
p->btn_reslice->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->reslice(); });
p->btn_reslice->Bind(wxEVT_BUTTON, [this](wxCommandEvent&)
{
const bool export_gcode_after_slicing = wxGetKeyState(WXK_SHIFT);
if (export_gcode_after_slicing)
p->plater->export_gcode();
else
p->plater->reslice();
});
p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); });
}
Sidebar::~Sidebar() {}
void Sidebar::init_filament_combo(PresetComboBox **combo, const int extr_idx) {
*combo = new PresetComboBox(p->scrolled, Slic3r::Preset::TYPE_FILAMENT);
*combo = new PresetComboBox(p->presets_panel, Slic3r::Preset::TYPE_FILAMENT);
// # copy icons from first choice
// $choice->SetItemBitmap($_, $choices->[0]->GetItemBitmap($_)) for 0..$#presets;
@ -718,6 +770,8 @@ void Sidebar::update_presets(Preset::Type preset_type)
case Preset::TYPE_PRINTER:
{
// wxWindowUpdateLocker noUpdates_scrolled(p->scrolled);
// Update the print choosers to only contain the compatible presets, update the dirty flags.
if (print_tech == ptFFF)
preset_bundle.prints.update_platter_ui(p->combo_print);
@ -747,9 +801,15 @@ void Sidebar::update_presets(Preset::Type preset_type)
wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config);
}
void Sidebar::update_mode_sizer(const Slic3r::ConfigOptionMode& mode)
void Sidebar::update_mode_sizer() const
{
p->mode_sizer->SetMode(mode);
p->mode_sizer->SetMode(m_mode);
}
void Sidebar::update_reslice_btn_tooltip() const
{
const wxString tooltip = m_mode == comSimple ? wxString("") : _(L("Hold Shift to Slice & Export G-code"));
p->btn_reslice->SetToolTip(tooltip);
}
ObjectManipulation* Sidebar::obj_manipul()
@ -772,6 +832,11 @@ wxScrolledWindow* Sidebar::scrolled_panel()
return p->scrolled;
}
wxPanel* Sidebar::presets_panel()
{
return p->presets_panel;
}
ConfigOptionsGroup* Sidebar::og_freq_chng_params(const bool is_fff)
{
return p->frequently_changed_parameters->get_og(is_fff);
@ -942,8 +1007,9 @@ void Sidebar::enable_buttons(bool enable)
p->btn_send_gcode->Enable(enable);
}
void Sidebar::show_reslice(bool show) { p->btn_reslice->Show(show); }
void Sidebar::show_send(bool show) { p->btn_send_gcode->Show(show); }
void Sidebar::show_reslice(bool show) const { p->btn_reslice->Show(show); }
void Sidebar::show_export(bool show) const { p->btn_export_gcode->Show(show); }
void Sidebar::show_send(bool show) const { p->btn_send_gcode->Show(show); }
bool Sidebar::is_multifilament()
{
@ -951,6 +1017,24 @@ bool Sidebar::is_multifilament()
}
void Sidebar::update_mode()
{
m_mode = wxGetApp().get_mode();
update_reslice_btn_tooltip();
update_mode_sizer();
wxWindowUpdateLocker noUpdates(this);
p->object_list->get_sizer()->Show(m_mode > comSimple);
p->object_list->unselect_objects();
p->object_list->update_selections();
p->object_list->update_object_menu();
Layout();
}
std::vector<PresetComboBox*>& Sidebar::combos_filament()
{
return p->combos_filament;
@ -1019,6 +1103,7 @@ struct Plater::priv
std::vector<wxPanel*> panels;
Sidebar *sidebar;
Bed3D bed;
Camera camera;
View3D* view3D;
GLToolbar view_toolbar;
Preview *preview;
@ -1033,6 +1118,9 @@ struct Plater::priv
wxTimer background_process_timer;
std::string label_btn_export;
std::string label_btn_send;
static const std::regex pattern_bundle;
static const std::regex pattern_3mf;
static const std::regex pattern_zip_amf;
@ -1115,13 +1203,13 @@ struct Plater::priv
void on_action_layersediting(SimpleEvent&);
void on_object_select(SimpleEvent&);
void on_viewport_changed(SimpleEvent&);
void on_right_click(Vec2dEvent&);
void on_wipetower_moved(Vec3dEvent&);
void on_update_geometry(Vec3dsEvent<2>&);
void on_3dcanvas_mouse_dragging_finished(SimpleEvent&);
void update_object_menu();
void show_action_buttons(const bool is_ready_to_slice) const;
// Set the bed shape to a single closed 2D polygon(array of two element arrays),
// triangulate the bed and store the triangles into m_bed.m_triangles,
@ -1202,11 +1290,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
sla_print.set_status_callback(statuscb);
this->q->Bind(EVT_SLICING_UPDATE, &priv::on_slicing_update, this);
view3D = new View3D(q, &model, config, &background_process);
preview = new Preview(q, config, &background_process, &gcode_preview_data, [this](){ schedule_background_process(); });
view3D->set_bed(&bed);
preview->set_bed(&bed);
view3D = new View3D(q, bed, camera, view_toolbar, &model, config, &background_process);
preview = new Preview(q, bed, camera, view_toolbar, config, &background_process, &gcode_preview_data, [this](){ schedule_background_process(); });
panels.push_back(view3D);
panels.push_back(preview);
@ -1238,7 +1323,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
// 3DScene events:
view3D_canvas->Bind(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, [this](SimpleEvent&) { this->schedule_background_process(); });
view3D_canvas->Bind(EVT_GLCANVAS_OBJECT_SELECT, &priv::on_object_select, this);
view3D_canvas->Bind(EVT_GLCANVAS_VIEWPORT_CHANGED, &priv::on_viewport_changed, this);
view3D_canvas->Bind(EVT_GLCANVAS_RIGHT_CLICK, &priv::on_right_click, this);
view3D_canvas->Bind(EVT_GLCANVAS_REMOVE_OBJECT, [q](SimpleEvent&) { q->remove_selected(); });
view3D_canvas->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { arrange(); });
@ -1268,7 +1352,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) { set_bed_shape(config->option<ConfigOptionPoints>("bed_shape")->values); });
// Preview events:
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_VIEWPORT_CHANGED, &priv::on_viewport_changed, this);
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); });
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) { set_bed_shape(config->option<ConfigOptionPoints>("bed_shape")->values); });
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); });
@ -2089,13 +2172,36 @@ unsigned int Plater::priv::update_background_process(bool force_validation)
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone());
}
//FIXME update "Slice Now / Schedule background process"
//background_process.is_export_scheduled() - byl zavolan "Export G-code", background processing ma jmeno export souboru
//background_process.is_upload_scheduled() - byl zavolan "Send to OctoPrint", jeste nebylo doslajsovano (pak se preda upload fronte a background process zapomene)
//background_process.empty() - prazdna plocha
// pokud (return_state & UPDATE_BACKGROUND_PROCESS_INVALID) != 0 -> doslo k chybe (gray out "Slice now") mozna "Invalid data"???
// jinak background_process.running() -> Zobraz "Slicing ..."
// jinak pokud ! background_process.empty() && ! background_process.finished() -> je neco ke slajsovani (povol tlacitko) "Slice Now"
if ((return_state & UPDATE_BACKGROUND_PROCESS_INVALID) != 0)
{
// Validation of the background data failed.
const wxString invalid_str = _(L("Invalid data"));
for (auto btn : {ActionButtonType::abReslice, ActionButtonType::abSendGCode, ActionButtonType::abExport})
sidebar->set_btn_label(btn, invalid_str);
}
else
{
// Background data is valid.
if ((return_state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 ||
(return_state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) != 0 )
this->statusbar()->set_status_text(L("Ready to slice"));
sidebar->set_btn_label(ActionButtonType::abExport, _(label_btn_export));
sidebar->set_btn_label(ActionButtonType::abSendGCode, _(label_btn_send));
const wxString slice_string = background_process.running() && wxGetApp().get_mode() == comSimple ?
_(L("Slicing")) + dots : _(L("Slice now"));
sidebar->set_btn_label(ActionButtonType::abReslice, slice_string);
if (background_process.finished())
show_action_buttons(false);
else if (!background_process.empty() &&
!background_process.running()) /* Do not update buttons if background process is running
* This condition is important for SLA mode especially,
* when this function is called several times during calculations
* */
show_action_buttons(true);
}
return return_state;
}
@ -2226,6 +2332,10 @@ void Plater::priv::set_current_panel(wxPanel* panel)
if (std::find(panels.begin(), panels.end(), panel) == panels.end())
return;
#ifdef __WXMAC__
bool force_render = (current_panel != nullptr);
#endif // __WXMAC__
if (current_panel == panel)
return;
@ -2234,7 +2344,19 @@ void Plater::priv::set_current_panel(wxPanel* panel)
for (wxPanel* p : panels)
{
if (p == current_panel)
{
#ifdef __WXMAC__
// On Mac we need also to force a render to avoid flickering when changing view
if (force_render)
{
if (p == view3D)
dynamic_cast<View3D*>(p)->get_canvas3d()->render();
else if (p == preview)
dynamic_cast<Preview*>(p)->get_canvas3d()->render();
}
#endif // __WXMAC__
p->Show();
}
}
// then set to invisible the other
for (wxPanel* p : panels)
@ -2266,7 +2388,7 @@ void Plater::priv::set_current_panel(wxPanel* panel)
{
this->q->reslice();
// keeps current gcode preview, if any
preview->reload_print(false, true);
preview->reload_print(true);
preview->set_canvas_as_dirty();
view_toolbar.select_item("Preview");
}
@ -2288,7 +2410,7 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
//! instead of
//! combo->GetStringSelection().ToUTF8().data());
std::string selected_string = combo->GetString(combo->GetSelection()).ToUTF8().data();
const std::string& selected_string = combo->GetString(combo->GetSelection()).ToUTF8().data();
if (preset_type == Preset::TYPE_FILAMENT) {
wxGetApp().preset_bundle->set_filament_preset(idx, selected_string);
@ -2300,12 +2422,8 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
wxGetApp().preset_bundle->update_platter_filament_ui(idx, combo);
}
else {
for (Tab* tab : wxGetApp().tabs_list) {
if (tab->type() == preset_type) {
tab->select_preset(selected_string);
break;
}
}
wxWindowUpdateLocker noUpdates(sidebar->presets_panel());
wxGetApp().get_tab(preset_type)->select_preset(selected_string);
}
// update plater with new config
@ -2316,8 +2434,10 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
{
this->statusbar()->set_progress(evt.status.percent);
this->statusbar()->set_status_text(_(L(evt.status.text)) + wxString::FromUTF8(""));
if (evt.status.percent >= -1) {
this->statusbar()->set_progress(evt.status.percent);
this->statusbar()->set_status_text(_(L(evt.status.text)) + wxString::FromUTF8(""));
}
if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SCENE) {
switch (this->printer_technology) {
case ptFFF:
@ -2335,6 +2455,10 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
// Update SLA gizmo (reload_scene calls update_gizmos_data)
q->canvas3D()->reload_scene(true);
}
if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SLA_PREVIEW) {
// Update the SLA preview
this->preview->reload_print();
}
}
void Plater::priv::on_slicing_completed(wxCommandEvent &)
@ -2361,14 +2485,17 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
this->statusbar()->reset_cancel_callback();
this->statusbar()->stop_busy();
bool canceled = evt.GetInt() < 0;
bool success = evt.GetInt() > 0;
const bool canceled = evt.GetInt() < 0;
const bool error = evt.GetInt() == 0;
const bool success = evt.GetInt() > 0;
// Reset the "export G-code path" name, so that the automatic background processing will be enabled again.
this->background_process.reset_export();
if (! success) {
if (error) {
wxString message = evt.GetString();
if (message.IsEmpty())
message = _(L("Export failed"));
show_error(q, message);
this->statusbar()->set_status_text(message);
}
if (canceled)
@ -2393,6 +2520,14 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
this->update_sla_scene();
break;
}
if (canceled) {
if (wxGetApp().get_mode() == comSimple)
sidebar->set_btn_label(ActionButtonType::abReslice, "Slice now");
show_action_buttons(true);
}
else if (wxGetApp().get_mode() == comSimple)
show_action_buttons(false);
}
void Plater::priv::on_layer_editing_toggled(bool enable)
@ -2435,15 +2570,6 @@ void Plater::priv::on_object_select(SimpleEvent& evt)
selection_changed();
}
void Plater::priv::on_viewport_changed(SimpleEvent& evt)
{
wxObject* o = evt.GetEventObject();
if (o == preview->get_wxglcanvas())
preview->set_viewport_into_scene(view3D->get_canvas3d());
else if (o == view3D->get_wxglcanvas())
preview->set_viewport_from_scene(view3D->get_canvas3d());
}
void Plater::priv::on_right_click(Vec2dEvent& evt)
{
int obj_idx = get_selected_object_idx();
@ -2506,15 +2632,20 @@ bool Plater::priv::init_object_menu()
bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/)
{
wxMenuItem* item_delete = append_menu_item(menu, wxID_ANY, _(L("Delete")) + "\tDel", _(L("Remove the selected object")),
[this](wxCommandEvent&) { q->remove_selected(); }, "brick_delete.png");
if (!is_part){
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 {
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");
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");
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");
// 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");
menu->AppendSeparator();
wxMenuItem* item_instance_to_object = sidebar->obj_list()->append_menu_item_instance_to_object(menu);
@ -2551,7 +2682,7 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
wxMenuItem* item_mirror = append_submenu(menu, mirror_menu, wxID_ANY, _(L("Mirror")), _(L("Mirror the selected object")));
// ui updates needs to be binded to the parent panel
// ui updates needs to be bound to the parent panel
if (q != nullptr)
{
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_mirror()); }, item_mirror->GetId());
@ -2681,9 +2812,6 @@ void Plater::priv::init_view_toolbar()
view_toolbar.select_item("3D");
view_toolbar.set_enabled(true);
view3D->set_view_toolbar(&view_toolbar);
preview->set_view_toolbar(&view_toolbar);
}
bool Plater::priv::can_delete_object() const
@ -2768,6 +2896,38 @@ void Plater::priv::update_object_menu()
view3D->update_toolbar_items_visibility();
}
void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const
{
wxWindowUpdateLocker noUpdater(sidebar);
const auto prin_host_opt = config->option<ConfigOptionString>("print_host");
const bool send_gcode_shown = prin_host_opt != nullptr && !prin_host_opt->value.empty();
// when a background processing is ON, export_btn and/or send_btn are showing
if (wxGetApp().app_config->get("background_processing") == "1")
{
sidebar->show_reslice(false);
sidebar->show_export(true);
sidebar->show_send(send_gcode_shown);
}
else
{
sidebar->show_reslice(is_ready_to_slice);
sidebar->show_export(!is_ready_to_slice);
sidebar->show_send(send_gcode_shown && !is_ready_to_slice);
}
sidebar->Layout();
}
void Sidebar::set_btn_label(const ActionButtonType btn_type, const wxString& label) const
{
switch (btn_type)
{
case ActionButtonType::abReslice: p->btn_reslice->SetLabelText(label); break;
case ActionButtonType::abExport: p->btn_export_gcode->SetLabelText(label); break;
case ActionButtonType::abSendGCode: p->btn_send_gcode->SetLabelText(label); break;
}
}
// Plater / Public
Plater::Plater(wxWindow *parent, MainFrame *main_frame)
@ -3094,6 +3254,22 @@ void Plater::reslice()
this->p->background_process.set_task(PrintBase::TaskParams());
// Only restarts if the state is valid.
this->p->restart_background_process(state | priv::UPDATE_BACKGROUND_PROCESS_FORCE_RESTART);
if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0)
return;
if (p->background_process.running())
{
if (wxGetApp().get_mode() == comSimple)
p->sidebar->set_btn_label(ActionButtonType::abReslice, _(L("Slicing")) + dots);
else
{
p->sidebar->set_btn_label(ActionButtonType::abReslice, _(L("Slice now")));
p->show_action_buttons(false);
}
}
else if (!p->background_process.empty() && !p->background_process.idle())
p->show_action_buttons(true);
}
void Plater::reslice_SLA_supports(const ModelObject &object)
@ -3157,8 +3333,10 @@ void Plater::on_extruders_change(int num_extruders)
{
auto& choices = sidebar().combos_filament();
if (num_extruders == choices.size())
return;
wxWindowUpdateLocker noUpdates_scrolled_panel(&sidebar()/*.scrolled_panel()*/);
// sidebar().scrolled_panel()->Freeze();
int i = choices.size();
while ( i < num_extruders )
@ -3292,6 +3470,9 @@ void Plater::set_printer_technology(PrinterTechnology printer_technology)
}
//FIXME for SLA synchronize
//p->background_process.apply(Model)!
p->label_btn_export = printer_technology == ptFFF ? L("Export G-code") : L("Export");
p->label_btn_send = printer_technology == ptFFF ? L("Send G-code") : L("Send to printer");
}
void Plater::changed_object(int obj_idx)

View File

@ -38,6 +38,7 @@ class GLCanvas3D;
using t_optgroups = std::vector <std::shared_ptr<ConfigOptionsGroup>>;
class Plater;
enum class ActionButtonType : int;
class PresetComboBox : public wxBitmapComboBox
{
@ -61,7 +62,7 @@ private:
class Sidebar : public wxPanel
{
/*ConfigOptionMode*/int m_mode;
ConfigOptionMode m_mode;
public:
Sidebar(Plater *parent);
Sidebar(Sidebar &&) = delete;
@ -73,12 +74,14 @@ public:
void init_filament_combo(PresetComboBox **combo, const int extr_idx);
void remove_unused_filament_combos(const int current_extruder_count);
void update_presets(Slic3r::Preset::Type preset_type);
void update_mode_sizer(const Slic3r::ConfigOptionMode& mode);
void update_mode_sizer() const;
void update_reslice_btn_tooltip() const;
ObjectManipulation* obj_manipul();
ObjectList* obj_list();
ObjectSettings* obj_settings();
wxScrolledWindow* scrolled_panel();
wxPanel* presets_panel();
ConfigOptionsGroup* og_freq_chng_params(const bool is_fff);
wxButton* get_wiping_dialog_button();
@ -86,10 +89,12 @@ public:
void show_info_sizer();
void show_sliced_info_sizer(const bool show);
void enable_buttons(bool enable);
void show_reslice(bool show);
void show_send(bool show);
void set_btn_label(const ActionButtonType btn_type, const wxString& label) const;
void show_reslice(bool show) const;
void show_export(bool show) const;
void show_send(bool show) const;
bool is_multifilament();
void set_mode_value(const /*ConfigOptionMode*/int mode) { m_mode = mode; }
void update_mode();
std::vector<PresetComboBox*>& combos_filament();
private:

View File

@ -457,6 +457,7 @@ const std::vector<std::string>& Preset::sla_print_options()
"support_base_height",
"support_critical_angle",
"support_max_bridge_length",
"support_max_pillar_link_distance",
"support_object_elevation",
"support_points_density_relative",
"support_points_minimal_distance",
@ -880,6 +881,7 @@ void PresetCollection::update_platter_ui(GUI::PresetComboBox *ui)
{
if (ui == nullptr)
return;
// Otherwise fill in the list from scratch.
ui->Freeze();
ui->Clear();

View File

@ -1289,7 +1289,7 @@ void PresetBundle::update_compatible(bool select_other_if_incompatible)
{
const Preset &printer_preset = this->printers.get_edited_preset();
switch (printers.get_edited_preset().printer_technology()) {
switch (printer_preset.printer_technology()) {
case ptFFF:
{
assert(printer_preset.config.has("default_print_profile"));

View File

@ -58,6 +58,13 @@ Tab::Tab(wxNotebook* parent, const wxString& title, const char* name) :
wxGetApp().tabs_list.push_back(this);
m_em_unit = wxGetApp().em_unit();
Bind(wxEVT_SIZE, ([this](wxSizeEvent &evt) {
for (auto page : m_pages)
if (! page.get()->IsShown())
page->layout_valid = false;
evt.Skip();
}));
}
void Tab::set_type()
@ -73,6 +80,10 @@ void Tab::set_type()
// sub new
void Tab::create_preset_tab()
{
#ifdef __WINDOWS__
SetDoubleBuffered(true);
#endif //__WINDOWS__
m_preset_bundle = wxGetApp().preset_bundle;
// Vertical sizer to hold the choice menu and the rest of the page.
@ -290,6 +301,11 @@ Slic3r::GUI::PageShp Tab::add_options_page(const wxString& title, const std::str
auto panel = this;
#endif
PageShp page(new Page(panel, title, icon_idx));
// page->SetBackgroundStyle(wxBG_STYLE_SYSTEM);
#ifdef __WINDOWS__
// page->SetDoubleBuffered(true);
#endif //__WINDOWS__
page->SetScrollbars(1, 20, 1, 2);
page->Hide();
m_hsizer->Add(page.get(), 1, wxEXPAND | wxLEFT, 5);
@ -315,7 +331,7 @@ void Tab::OnActivate()
void Tab::update_labels_colour()
{
Freeze();
// Freeze();
//update options "decoration"
for (const auto opt : m_options_list)
{
@ -342,7 +358,7 @@ void Tab::update_labels_colour()
if (field == nullptr) continue;
field->set_label_colour_force(color);
}
Thaw();
// Thaw();
auto cur_item = m_treectrl->GetFirstVisibleItem();
while (cur_item) {
@ -386,7 +402,7 @@ void Tab::update_changed_ui()
for (auto opt_key : dirty_options) m_options_list[opt_key] &= ~osInitValue;
for (auto opt_key : nonsys_options) m_options_list[opt_key] &= ~osSystemValue;
Freeze();
// Freeze();
//update options "decoration"
for (const auto opt : m_options_list)
{
@ -436,7 +452,7 @@ void Tab::update_changed_ui()
field->set_undo_to_sys_tooltip(sys_tt);
field->set_label_colour(color);
}
Thaw();
// Thaw();
wxTheApp->CallAfter([this]() {
update_changed_tree_ui();
@ -683,16 +699,16 @@ void Tab::load_config(const DynamicPrintConfig& config)
// Reload current $self->{config} (aka $self->{presets}->edited_preset->config) into the UI fields.
void Tab::reload_config()
{
Freeze();
// Freeze();
for (auto page : m_pages)
page->reload_config();
Thaw();
// Thaw();
}
void Tab::update_visibility()
{
const ConfigOptionMode mode = wxGetApp().get_mode();
Freeze();
// Freeze();
for (auto page : m_pages)
page->update_visibility(mode);
@ -702,7 +718,7 @@ void Tab::update_visibility()
m_mode_sizer->SetMode(mode);
Layout();
Thaw();
// Thaw();
// to update tree items color
// wxTheApp->CallAfter([this]() {
@ -752,21 +768,29 @@ void Tab::load_key_value(const std::string& opt_key, const boost::any& value, bo
void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
{
ConfigOptionsGroup* og_freq_chng_params = wxGetApp().sidebar().og_freq_chng_params(supports_printer_technology(ptFFF));
if (opt_key == "fill_density" || opt_key == "supports_enable" || opt_key == "pad_enable")
if (wxGetApp().plater() == nullptr) {
return;
}
const bool is_fff = supports_printer_technology(ptFFF);
ConfigOptionsGroup* og_freq_chng_params = wxGetApp().sidebar().og_freq_chng_params(is_fff);
if (opt_key == "fill_density" || opt_key == "pad_enable")
{
boost::any val = og_freq_chng_params->get_config_value(*m_config, opt_key);
og_freq_chng_params->set_value(opt_key, val);
}
if (opt_key == "support_material" || opt_key == "support_material_buildplate_only")
if ( is_fff && (opt_key == "support_material" || opt_key == "support_material_buildplate_only") ||
!is_fff && (opt_key == "supports_enable" || opt_key == "support_buildplate_only"))
{
wxString new_selection = !m_config->opt_bool("support_material") ?
_("None") :
m_config->opt_bool("support_material_buildplate_only") ?
_("Support on build plate only") :
_("Everywhere");
const std::string support = is_fff ? "support_material" : "supports_enable";
const std::string buildplate_only = is_fff ? "support_material_buildplate_only" : "support_buildplate_only";
wxString new_selection = !m_config->opt_bool(support) ? _("None") :
m_config->opt_bool(buildplate_only) ? _("Support on build plate only") :
_("Everywhere");
og_freq_chng_params->set_value("support", new_selection);
}
if (opt_key == "brim_width")
{
bool val = m_config->opt_float("brim_width") > 0.0 ? true : false;
@ -780,25 +804,6 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
wxGetApp().plater()->on_extruders_change(boost::any_cast<size_t>(value));
update();
// #ys_FIXME_to_delete
// Post event to the Plater after updating of the all dirty options
// It helps to avoid needless schedule_background_processing
// if (update_completed())
// if (m_update_stack.empty())
// {
// // wxCommandEvent event(EVT_TAB_VALUE_CHANGED);
// // event.SetEventObject(this);
// // event.SetString(opt_key);
// // if (opt_key == "extruders_count")
// // {
// // const int val = boost::any_cast<size_t>(value);
// // event.SetInt(val);
// // }
// //
// // wxPostEvent(this, event);
// wxGetApp().mainframe->on_value_changed(m_config);
// }
}
// Show/hide the 'purging volumes' button
@ -821,9 +826,17 @@ void Tab::update_wiping_button_visibility() {
// To update the content of the selection boxes,
// to update the filament colors of the selection boxes,
// to update the "dirty" flags of the selection boxes,
// to uddate number of "filament" selection boxes when the number of extruders change.
// to update number of "filament" selection boxes when the number of extruders change.
void Tab::on_presets_changed()
{
if (wxGetApp().plater() == nullptr) {
return;
}
// Instead of PostEvent (EVT_TAB_PRESETS_CHANGED) just call update_presets
wxGetApp().plater()->sidebar().update_presets(m_type);
update_preset_description_line();
// Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors.
for (auto t: m_dependent_tabs)
{
@ -834,16 +847,6 @@ void Tab::on_presets_changed()
// clear m_dependent_tabs after first update from select_preset()
// to avoid needless preset loading from update() function
m_dependent_tabs.clear();
// #ys_FIXME_to_delete
// wxCommandEvent event(EVT_TAB_PRESETS_CHANGED);
// event.SetEventObject(this);
// wxPostEvent(this, event);
// Instead of PostEvent (EVT_TAB_PRESETS_CHANGED) just call update_presets
wxGetApp().plater()->sidebar().update_presets(m_type);
update_preset_description_line();
}
void Tab::update_preset_description_line()
@ -1190,7 +1193,7 @@ void TabPrint::update()
// return; // ! TODO Let delete this part of code after a common aplication testing
m_update_cnt++;
Freeze();
// Freeze();
double fill_density = m_config->option<ConfigOptionPercent>("fill_density")->value;
@ -1401,7 +1404,7 @@ void TabPrint::update()
m_recommended_thin_wall_thickness_description_line->SetText(
from_u8(PresetHints::recommended_thin_wall_thickness(*m_preset_bundle)));
Thaw();
// Thaw();
m_update_cnt--;
if (m_update_cnt==0)
@ -1496,6 +1499,7 @@ void TabFilament::build()
line = optgroup->create_single_option_line("filament_ramming_parameters");// { _(L("Ramming")), "" };
line.widget = [this](wxWindow* parent) {
auto ramming_dialog_btn = new wxButton(parent, wxID_ANY, _(L("Ramming settings"))+dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
ramming_dialog_btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(ramming_dialog_btn);
@ -1576,7 +1580,7 @@ void TabFilament::update()
return; // ys_FIXME
m_update_cnt++;
Freeze();
// Freeze();
wxString text = from_u8(PresetHints::cooling_description(m_presets->get_edited_preset()));
m_cooling_description_line->SetText(text);
text = from_u8(PresetHints::maximum_volumetric_flow_description(*m_preset_bundle));
@ -1590,7 +1594,7 @@ void TabFilament::update()
for (auto el : { "min_fan_speed", "disable_fan_first_layers" })
get_field(el)->toggle(fan_always_on);
Thaw();
// Thaw();
m_update_cnt--;
if (m_update_cnt == 0)
@ -1622,25 +1626,22 @@ bool Tab::current_preset_is_dirty()
void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
{
const bool sla = m_presets->get_selected_preset().printer_technology() == ptSLA;
const PrinterTechnology tech = m_presets->get_selected_preset().printer_technology();
// Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment)
if (! sla) {
if (tech == ptFFF) {
optgroup->append_single_option_line("host_type");
}
auto printhost_browse = [this, optgroup] (wxWindow* parent) {
// TODO: SLA Bonjour
auto printhost_browse = [=](wxWindow* parent) {
auto btn = m_printhost_browse_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);
sizer->Add(btn);
btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent &e) {
BonjourDialog dialog(parent);
btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) {
BonjourDialog dialog(parent, tech);
if (dialog.show_and_lookup()) {
optgroup->set_value("print_host", std::move(dialog.get_selected()), true);
optgroup->get_field("print_host")->field_changed();
@ -1653,7 +1654,7 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
auto print_host_test = [this](wxWindow* parent) {
auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")),
wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
// btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG));
btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
btn->SetBitmap(create_scaled_bitmap("wrench.png"));
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn);
@ -1691,6 +1692,7 @@ 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);
sizer->Add(btn);
@ -1729,6 +1731,7 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
\tOn this system, Slic3r uses HTTPS certificates from the system Certificate Store or Keychain.\n\
\tTo use a custom CA file, please import your CA file into Certificate Store / Keychain.")),
ca_file_hint));
txt->SetFont(Slic3r::GUI::wxGetApp().normal_font());
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(txt);
return sizer;
@ -1969,7 +1972,7 @@ void TabPrinter::build_sla()
Line line = optgroup->create_single_option_line("bed_shape");//{ _(L("Bed shape")), "" };
line.widget = [this](wxWindow* parent) {
auto btn = new wxButton(parent, wxID_ANY, _(L(" Set ")) + dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
// btn->SetFont(Slic3r::GUI::small_font);
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"));
@ -2276,7 +2279,7 @@ void TabPrinter::update()
void TabPrinter::update_fff()
{
Freeze();
// Freeze();
bool en;
auto serial_speed = get_field("serial_speed");
@ -2375,7 +2378,7 @@ void TabPrinter::update_fff()
(have_multiple_extruders && toolchange_retraction);
}
Thaw();
// Thaw();
}
void TabPrinter::update_sla()
@ -2469,7 +2472,7 @@ void Tab::load_current_preset()
//Regerenerate content of the page tree.
void Tab::rebuild_page_tree(bool tree_sel_change_event /*= false*/)
{
Freeze();
// Freeze();
// get label of the currently selected item
const auto sel_item = m_treectrl->GetSelection();
@ -2495,7 +2498,7 @@ void Tab::rebuild_page_tree(bool tree_sel_change_event /*= false*/)
// this is triggered on first load, so we don't disable the sel change event
m_treectrl->SelectItem(m_treectrl->GetFirstVisibleItem());//! (treectrl->GetFirstChild(rootItem));
}
Thaw();
// Thaw();
}
void Tab::update_page_tree_visibility()
@ -2609,7 +2612,7 @@ void Tab::select_preset(std::string preset_name)
} else {
if (current_dirty)
m_presets->discard_current_changes();
m_presets->select_preset_by_name(preset_name, false);
const bool is_selected = m_presets->select_preset_by_name(preset_name, false);
// Mark the print & filament enabled if they are compatible with the currently selected preset.
// The following method should not discard changes of current print or filament presets on change of a printer profile,
// if they are compatible with the current printer.
@ -2618,6 +2621,28 @@ void Tab::select_preset(std::string preset_name)
// Initialize the UI from the current preset.
if (printer_tab)
static_cast<TabPrinter*>(this)->update_pages();
if (!is_selected && printer_tab)
{
/* There is a case, when :
* after Config Wizard applying we try to select previously selected preset, but
* in a current configuration this one:
* 1. doesn't exist now,
* 2. have another printer_technology
* So, it is necessary to update list of dependent tabs
* to the corresponding printer_technology
*/
const PrinterTechnology printer_technology = m_presets->get_edited_preset().printer_technology();
if (printer_technology == ptFFF && m_dependent_tabs.front() != Preset::Type::TYPE_PRINT ||
printer_technology == ptSLA && m_dependent_tabs.front() != Preset::Type::TYPE_SLA_PRINT )
{
m_dependent_tabs.clear();
if (printer_technology == ptFFF)
m_dependent_tabs = { Preset::Type::TYPE_PRINT, Preset::Type::TYPE_FILAMENT };
else
m_dependent_tabs = { Preset::Type::TYPE_SLA_PRINT, Preset::Type::TYPE_SLA_MATERIAL };
}
}
load_current_preset();
}
}
@ -2689,7 +2714,7 @@ void Tab::OnTreeSelChange(wxTreeEvent& event)
#ifdef __linux__
std::unique_ptr<wxWindowUpdateLocker> no_updates(new wxWindowUpdateLocker(this));
#else
wxWindowUpdateLocker noUpdates(this);
// wxWindowUpdateLocker noUpdates(this);
#endif
if (m_pages.empty())
@ -2709,17 +2734,22 @@ void Tab::OnTreeSelChange(wxTreeEvent& event)
if (page == nullptr) return;
for (auto& el : m_pages)
el.get()->Hide();
// if (el.get()->IsShown()) {
el.get()->Hide();
// break;
// }
#ifdef __linux__
no_updates.reset(nullptr);
#endif
page->Show();
m_hsizer->Layout();
Refresh();
#ifdef __linux__
no_updates.reset(nullptr);
#endif
update_undo_buttons();
page->Show();
// if (! page->layout_valid) {
page->layout_valid = true;
m_hsizer->Layout();
Refresh();
// }
}
void Tab::OnKeyDown(wxKeyEvent& event)
@ -2859,7 +2889,9 @@ void Tab::update_ui_from_settings()
wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &deps)
{
deps.checkbox = new wxCheckBox(parent, wxID_ANY, _(L("All")));
deps.checkbox->SetFont(Slic3r::GUI::wxGetApp().normal_font());
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"));
@ -3055,6 +3087,7 @@ ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_la
}
// 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;
};
@ -3270,7 +3303,8 @@ void TabSLAPrint::build()
optgroup->append_single_option_line("support_pillar_diameter");
optgroup->append_single_option_line("support_pillar_connection_mode");
optgroup->append_single_option_line("support_buildplate_only");
optgroup->append_single_option_line("support_pillar_widening_factor");
// TODO: This parameter is not used at the moment.
// optgroup->append_single_option_line("support_pillar_widening_factor");
optgroup->append_single_option_line("support_base_diameter");
optgroup->append_single_option_line("support_base_height");
optgroup->append_single_option_line("support_object_elevation");
@ -3278,6 +3312,7 @@ void TabSLAPrint::build()
optgroup = page->new_optgroup(_(L("Connection of the support sticks and junctions")));
optgroup->append_single_option_line("support_critical_angle");
optgroup->append_single_option_line("support_max_bridge_length");
optgroup->append_single_option_line("support_max_pillar_link_distance");
optgroup = page->new_optgroup(_(L("Automatic generation")));
optgroup->append_single_option_line("support_points_density_relative");
@ -3336,11 +3371,37 @@ void TabSLAPrint::update()
return; // #ys_FIXME
// #ys_FIXME
// m_update_cnt++;
// ! something to update
// m_update_cnt--;
//
// if (m_update_cnt == 0)
m_update_cnt++;
double head_penetration = m_config->opt_float("support_head_penetration");
double head_width = m_config->opt_float("support_head_width");
if(head_penetration > head_width) {
wxString msg_text = _(L("Head penetration should not be greater than the head width."));
auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Invalid Head penetration")), wxICON_WARNING | wxOK);
DynamicPrintConfig new_conf = *m_config;
if (dialog->ShowModal() == wxID_OK) {
new_conf.set_key_value("support_head_penetration", new ConfigOptionFloat(head_width));
}
load_config(new_conf);
}
double pinhead_d = m_config->opt_float("support_head_front_diameter");
double pillar_d = m_config->opt_float("support_pillar_diameter");
if(pinhead_d > pillar_d) {
wxString msg_text = _(L("Pinhead diameter should be smaller than the pillar diameter."));
auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Invalid pinhead diameter")), wxICON_WARNING | wxOK);
DynamicPrintConfig new_conf = *m_config;
if (dialog->ShowModal() == wxID_OK) {
new_conf.set_key_value("support_head_front_diameter", new ConfigOptionFloat(pillar_d / 2.0));
}
load_config(new_conf);
}
m_update_cnt--;
if (m_update_cnt == 0)
wxGetApp().mainframe->on_config_changed(m_config);
}

Some files were not shown because too many files have changed in this diff Show More