Fixed conflicts after merge with master
This commit is contained in:
commit
fe6b288743
@ -65,6 +65,7 @@ SET PS_DESTDIR=
|
||||
CALL :RESOLVE_DESTDIR_CACHE
|
||||
|
||||
REM Set up parameters used by help menu
|
||||
SET EXIT_STATUS=0
|
||||
SET PS_CONFIG_DEFAULT=%PS_CONFIG%
|
||||
SET PS_ARCH_HOST=%PS_ARCH%
|
||||
(echo " -help /help -h /h -? /? ")| findstr /I /C:" %~1 ">nul && GOTO :HELP
|
||||
@ -159,11 +160,14 @@ REM Build deps
|
||||
:BUILD_DEPS
|
||||
SET EXIT_STATUS=3
|
||||
SET PS_CURRENT_STEP=deps
|
||||
IF "%PS_STEPS_DIRTY%" EQU "" CALL :MAKE_OR_CLEAN_DIRECTORY deps\build "%PS_DEPS_PATH_FILE_NAME%"
|
||||
IF "%PS_STEPS_DIRTY%" EQU "" CALL :MAKE_OR_CLEAN_DIRECTORY deps\build "%PS_DEPS_PATH_FILE_NAME%" .vs
|
||||
cd deps\build || GOTO :END
|
||||
cmake.exe .. -DDESTDIR="%PS_DESTDIR%" || GOTO :END
|
||||
cmake.exe .. -DDESTDIR="%PS_DESTDIR%"
|
||||
IF %ERRORLEVEL% NEQ 0 IF "%PS_STEPS_DIRTY%" NEQ "" (
|
||||
(del CMakeCache.txt && cmake.exe .. -DDESTDIR="%PS_DESTDIR%") || GOTO :END
|
||||
) ELSE GOTO :END
|
||||
(echo %PS_DESTDIR%)> "%PS_DEPS_PATH_FILE%"
|
||||
msbuild /m ALL_BUILD.vcxproj /p:Configuration=%PS_CONFIG% || GOTO :END
|
||||
msbuild /m ALL_BUILD.vcxproj /p:Configuration=%PS_CONFIG% /v:quiet || GOTO :END
|
||||
cd ..\..
|
||||
IF /I "%PS_STEPS:~0,4%" EQU "deps" GOTO :RUN_APP
|
||||
|
||||
@ -171,7 +175,7 @@ REM Build app
|
||||
:BUILD_APP
|
||||
SET EXIT_STATUS=4
|
||||
SET PS_CURRENT_STEP=app
|
||||
IF "%PS_STEPS_DIRTY%" EQU "" CALL :MAKE_OR_CLEAN_DIRECTORY build "%PS_CUSTOM_RUN_FILE%"
|
||||
IF "%PS_STEPS_DIRTY%" EQU "" CALL :MAKE_OR_CLEAN_DIRECTORY build "%PS_CUSTOM_RUN_FILE%" .vs
|
||||
cd build || GOTO :END
|
||||
REM Make sure we have a custom batch file skeleton for the run stage
|
||||
set PS_CUSTOM_BAT=%PS_CUSTOM_RUN_FILE%
|
||||
@ -181,9 +185,12 @@ SET PS_PROJECT_IS_OPEN=
|
||||
FOR /F "tokens=2 delims=," %%I in (
|
||||
'tasklist /V /FI "IMAGENAME eq devenv.exe " /NH /FO CSV ^| find "%PS_SOLUTION_NAME%"'
|
||||
) do SET PS_PROJECT_IS_OPEN=%%~I
|
||||
cmake.exe .. -DCMAKE_PREFIX_PATH="%PS_DESTDIR%\usr\local" -DCMAKE_CONFIGURATION_TYPES=%PS_CONFIG_LIST% || GOTO :END
|
||||
cmake.exe .. -DCMAKE_PREFIX_PATH="%PS_DESTDIR%\usr\local" -DCMAKE_CONFIGURATION_TYPES=%PS_CONFIG_LIST%
|
||||
IF %ERRORLEVEL% NEQ 0 IF "%PS_STEPS_DIRTY%" NEQ "" (
|
||||
(del CMakeCache.txt && cmake.exe .. -DCMAKE_PREFIX_PATH="%PS_DESTDIR%\usr\local" -DCMAKE_CONFIGURATION_TYPES=%PS_CONFIG_LIST%) || GOTO :END
|
||||
) ELSE GOTO :END
|
||||
REM Skip the build step if we're using the undocumented app-cmake to regenerate the full config from inside devenv
|
||||
IF "%PS_STEPS%" NEQ "app-cmake" msbuild /m ALL_BUILD.vcxproj /p:Configuration=%PS_CONFIG% || GOTO :END
|
||||
IF "%PS_STEPS%" NEQ "app-cmake" msbuild /m ALL_BUILD.vcxproj /p:Configuration=%PS_CONFIG% /v:quiet || GOTO :END
|
||||
(echo %PS_DESTDIR%)> "%PS_DEPS_PATH_FILE_FOR_CONFIG%"
|
||||
|
||||
REM Run app
|
||||
@ -262,8 +269,11 @@ REM Functions and stubs start here.
|
||||
|
||||
:RESOLVE_DESTDIR_CACHE
|
||||
@REM Resolves all DESTDIR cache values and sets PS_STEPS_DEFAULT
|
||||
@REM Note: This just sets global variableq, so it doesn't use setlocal.
|
||||
SET PS_DEPS_PATH_FILE_FOR_CONFIG=%~dp0build\%PS_ARCH%\%PS_CONFIG%\%PS_DEPS_PATH_FILE_NAME%
|
||||
@REM Note: This just sets global variables, so it doesn't use setlocal.
|
||||
SET PS_DEPS_PATH_FILE_FOR_CONFIG=%~dp0build\.vs\%PS_ARCH%\%PS_CONFIG%\%PS_DEPS_PATH_FILE_NAME%
|
||||
mkdir "%~dp0build\.vs\%PS_ARCH%\%PS_CONFIG%" > nul 2> nul
|
||||
REM Copy a legacy file if we don't have one in the proper location.
|
||||
echo f|xcopy /D "%~dp0build\%PS_ARCH%\%PS_CONFIG%\%PS_DEPS_PATH_FILE_NAME%" "%PS_DEPS_PATH_FILE_FOR_CONFIG%"
|
||||
CALL :CANONICALIZE_PATH PS_DEPS_PATH_FILE_FOR_CONFIG
|
||||
IF EXIST "%PS_DEPS_PATH_FILE_FOR_CONFIG%" (
|
||||
FOR /F "tokens=* USEBACKQ" %%I IN ("%PS_DEPS_PATH_FILE_FOR_CONFIG%") DO (
|
||||
|
@ -3,7 +3,7 @@
|
||||
### Install the tools
|
||||
|
||||
Install Visual Studio Community 2019 from [visualstudio.microsoft.com/vs/](https://visualstudio.microsoft.com/vs/). Older versions are not supported as PrusaSlicer requires support for C++17.
|
||||
Select all workload options for C++
|
||||
Select all workload options for C++ and make sure to launch Visual Studio after install (to ensure that the full setup completes).
|
||||
|
||||
Install git for Windows from [gitforwindows.org](https://gitforwindows.org/)
|
||||
Download and run the exe accepting all defaults
|
||||
@ -17,6 +17,42 @@ c:> cd src
|
||||
c:\src> git clone https://github.com/prusa3d/PrusaSlicer.git
|
||||
```
|
||||
|
||||
### Run the automatic build script
|
||||
|
||||
The script `build_win.bat` will automatically find the default Visual Studio installation, set up the build environment, and then run both CMake and MSBuild to generate the dependencies and application as needed. If you'd rather do these steps manually, you can skip to the [Manual Build Instructions](#manual-build-instructions) in the next section. Otherwise, just run the following command to get everything going with the default configs:
|
||||
|
||||
```
|
||||
c:\src>cd c:\src\PrusaSlicer
|
||||
c:\src\PrusaSlicer>build_win.bat -d=..\PrusaSlicer-deps -r=console
|
||||
```
|
||||
|
||||
The build script will run for a while (over an hour, depending on your machine) and automatically perform the following steps:
|
||||
1. Configure and build [deps](#compile-the-dependencies) as RelWithDebInfo with `c:\src\PrusaSlicer-deps` as the destination directory
|
||||
2. Configure and build all [application targets](#compile-prusaslicer) as RelWithDebInfo
|
||||
3. Launch the resulting `prusa-slicer-console.exe` binary
|
||||
|
||||
You can change the above command line options to do things like:
|
||||
* Change the destination for the dependencies by pointing `-d` to a different directory such as: `build_win.bat -d=s:\PrusaSlicerDeps`
|
||||
* Open the solution in Visual Studio after the build completes by changing the `-r` switch to `-r=ide`
|
||||
* Generate a release build without debug info by adding `-c=Release` or a full debug build with `-c=Debug`
|
||||
* Perform an incremental application build (the default) with: `build_win.bat -s=app-dirty`
|
||||
* Clean and rebuild the application: `build_win.bat -s=app`
|
||||
* Clean and rebuild the dependencies: `build_win.bat -s=deps`
|
||||
* Clean and rebuild everything (app and deps): `build_win.bat -s=all`
|
||||
* _The full list of build script options can be listed by running:_ `build_win.bat -?`
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
You're best off initiating builds from within Visual Studio for day-to-day development. However, the `build_win.bat` script can be very helpful if you run into build failures after updating your source tree. Here are some tips to keep in mind:
|
||||
* The last several lines of output from `build_win.bat` will usually have the most helpful error messages.
|
||||
* If CMake complains about missing binaries or paths (e.g. after updating Visual Studio), building with `build_win.bat` will force CMake to regenerate its cache on an error.
|
||||
* After a deps change, you may just need to rebuild everything with the `-s=all` switch.
|
||||
* Reading through the instructions in the next section may help diagnose more complex issues.
|
||||
|
||||
# Manual Build Instructions
|
||||
|
||||
_Follow the steps below if you want to understand how to perform a manual build, or if you're troubleshooting issues with the automatic build script._
|
||||
|
||||
### Compile the dependencies.
|
||||
Dependencies are updated seldomly, thus they are compiled out of the PrusaSlicer source tree.
|
||||
Go to the Windows Start Menu and Click on "Visual Studio 2019" folder, then select the ->"x64 Native Tools Command Prompt" to open a command window and run the following:
|
||||
|
@ -1,3 +1,5 @@
|
||||
min_slic3r_version = 2.4.0-beta0
|
||||
1.4.0-beta0 Added multiple Filatech and BASF filament profiles. Added material profiles for SL1S.
|
||||
min_slic3r_version = 2.4.0-alpha0
|
||||
1.4.0-alpha8 Added material profiles for Prusament Resin. Detect bridging perimeters enabled by default.
|
||||
1.4.0-alpha7 Updated brim_separation value. Updated Prusa MINI end g-code. Added Filamentworld filament profiles.
|
||||
|
@ -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 PrusaSlicer configuration to be downgraded.
|
||||
config_version = 1.4.0-alpha8
|
||||
config_version = 1.4.0-beta0
|
||||
# Where to get the updates from?
|
||||
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/
|
||||
changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
|
||||
@ -2715,6 +2715,275 @@ min_print_speed = 15
|
||||
slowdown_below_layer_time = 10
|
||||
cooling = 1
|
||||
|
||||
[filament:Filatech FilaFlex30]
|
||||
inherits = Filatech FilaFlex40
|
||||
temperature = 225
|
||||
filament_density = 1.15
|
||||
extrusion_multiplier = 1.1
|
||||
filament_cost =
|
||||
|
||||
[filament:Filatech FilaFlex55]
|
||||
inherits = Filatech FilaFlex40
|
||||
temperature = 230
|
||||
filament_density = 1.18
|
||||
bed_temperature = 60
|
||||
fan_always_on = 0
|
||||
fan_below_layer_time = 60
|
||||
filament_cost =
|
||||
first_layer_temperature = 235
|
||||
extrusion_multiplier = 1
|
||||
|
||||
# [filament:Filatech TPE]
|
||||
# inherits = Filatech FilaFlex40
|
||||
# first_layer_temperature = 230
|
||||
# temperature = 225
|
||||
# filament_density = 1.2
|
||||
# fan_below_layer_time = 60
|
||||
# max_fan_speed = 80
|
||||
# min_fan_speed = 80
|
||||
# fan_always_on = 1
|
||||
|
||||
[filament:Filatech TPU]
|
||||
inherits = Filatech FilaFlex40
|
||||
first_layer_temperature = 230
|
||||
filament_density = 1.2
|
||||
fan_below_layer_time = 60
|
||||
max_fan_speed = 80
|
||||
min_fan_speed = 80
|
||||
fan_always_on = 1
|
||||
temperature = 235
|
||||
|
||||
[filament:Filatech ABS]
|
||||
inherits = *ABSC*
|
||||
filament_vendor = Filatech
|
||||
filament_cost =
|
||||
extrusion_multiplier = 0.95
|
||||
filament_density = 1.05
|
||||
|
||||
[filament:Filatech ABS @MINI]
|
||||
inherits = Filatech ABS; *ABSMINI*
|
||||
|
||||
[filament:Filatech FilaCarbon]
|
||||
inherits = *ABSC*
|
||||
filament_vendor = Filatech
|
||||
filament_cost =
|
||||
extrusion_multiplier = 0.95
|
||||
filament_density = 1.1
|
||||
first_layer_bed_temperature = 105
|
||||
bed_temperature = 100
|
||||
compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Filatech FilaCarbon @MINI]
|
||||
inherits = Filatech FilaCarbon; *ABSMINI*
|
||||
compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MINI"
|
||||
|
||||
[filament:Filatech FilaPLA]
|
||||
inherits = *PLA*
|
||||
filament_vendor = Filatech
|
||||
filament_cost =
|
||||
filament_density = 1.3
|
||||
first_layer_temperature = 235
|
||||
first_layer_bed_temperature = 50
|
||||
temperature = 230
|
||||
bed_temperature = 55
|
||||
|
||||
[filament:Filatech PLA]
|
||||
inherits = *PLA*
|
||||
filament_vendor = Filatech
|
||||
filament_cost =
|
||||
filament_density = 1.25
|
||||
first_layer_temperature = 215
|
||||
temperature = 210
|
||||
|
||||
[filament:Filatech PLA+]
|
||||
inherits = Filatech PLA
|
||||
filament_density = 1.24
|
||||
|
||||
[filament:Filatech FilaTough]
|
||||
inherits = Filatech ABS
|
||||
filament_cost =
|
||||
extrusion_multiplier = 0.95
|
||||
filament_density = 1.29
|
||||
first_layer_temperature = 245
|
||||
first_layer_bed_temperature = 80
|
||||
temperature = 240
|
||||
bed_temperature = 90
|
||||
cooling = 0
|
||||
compatible_printers_condition = ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Filatech HIPS]
|
||||
inherits = Prusa HIPS
|
||||
filament_vendor = Filatech
|
||||
filament_cost =
|
||||
filament_density = 1.07
|
||||
filament_spool_weight =
|
||||
first_layer_temperature = 230
|
||||
first_layer_bed_temperature = 100
|
||||
temperature = 225
|
||||
bed_temperature = 110
|
||||
compatible_printers_condition = printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Filatech HIPS @MINI]
|
||||
inherits = Filatech HIPS; *ABSMINI*
|
||||
|
||||
[filament:Filatech PA]
|
||||
inherits = *ABSC*
|
||||
filament_vendor = Filatech
|
||||
filament_cost =
|
||||
filament_density = 1.1
|
||||
first_layer_temperature = 275
|
||||
first_layer_bed_temperature = 110
|
||||
temperature = 275
|
||||
bed_temperature = 115
|
||||
fan_always_on = 0
|
||||
cooling = 0
|
||||
bridge_fan_speed = 25
|
||||
filament_type = NYLON
|
||||
filament_max_volumetric_speed = 8
|
||||
compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Filatech PA @MK2]
|
||||
inherits = Filatech PA
|
||||
first_layer_bed_temperature = 105
|
||||
bed_temperature = 110
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Filatech PA @MINI]
|
||||
inherits = Filatech PA
|
||||
first_layer_bed_temperature = 100
|
||||
bed_temperature = 100
|
||||
compatible_printers_condition = printer_model=="MINI"
|
||||
|
||||
[filament:Filatech PC]
|
||||
inherits = Filatech PA
|
||||
first_layer_bed_temperature = 110
|
||||
bed_temperature = 115
|
||||
filament_density = 1.2
|
||||
filament_type = PC
|
||||
|
||||
[filament:Filatech PC @MK2]
|
||||
inherits = Filatech PC
|
||||
first_layer_bed_temperature = 105
|
||||
bed_temperature = 110
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Filatech PC-ABS]
|
||||
inherits = Filatech PC
|
||||
first_layer_temperature = 270
|
||||
temperature = 270
|
||||
first_layer_bed_temperature = 110
|
||||
bed_temperature = 115
|
||||
filament_density = 1.08
|
||||
filament_type = PC
|
||||
fan_always_on = 0
|
||||
cooling = 1
|
||||
extrusion_multiplier = 0.95
|
||||
disable_fan_first_layers = 6
|
||||
|
||||
[filament:Filatech PC-ABS @MK2]
|
||||
inherits = Filatech PC-ABS
|
||||
first_layer_bed_temperature = 105
|
||||
bed_temperature = 110
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Filatech PETG]
|
||||
inherits = *PET*
|
||||
filament_vendor = Filatech
|
||||
filament_cost =
|
||||
filament_density = 1.27
|
||||
first_layer_temperature = 240
|
||||
first_layer_bed_temperature = 75
|
||||
temperature = 245
|
||||
bed_temperature = 80
|
||||
extrusion_multiplier = 0.95
|
||||
fan_always_on = 0
|
||||
|
||||
[filament:Filatech PETG @MINI]
|
||||
inherits = Filatech PETG; *PETMINI*
|
||||
|
||||
[filament:Filatech Wood-PLA]
|
||||
inherits = Filatech PLA
|
||||
filament_cost =
|
||||
filament_density = 1.05
|
||||
first_layer_temperature = 210
|
||||
compatible_printers_condition = nozzle_diameter[0]>=0.4 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Ultrafuse PET]
|
||||
inherits = *PET*
|
||||
filament_vendor = BASF
|
||||
filament_cost =
|
||||
filament_density = 1.33
|
||||
first_layer_temperature = 220
|
||||
first_layer_bed_temperature = 70
|
||||
temperature = 215
|
||||
bed_temperature = 70
|
||||
fan_below_layer_time = 10
|
||||
min_fan_speed = 75
|
||||
max_fan_speed = 100
|
||||
bridge_fan_speed = 100
|
||||
filament_type = PET
|
||||
disable_fan_first_layers = 1
|
||||
full_fan_speed_layer = 3
|
||||
filament_retract_lift = 0
|
||||
# filament_retract_length = 3
|
||||
# filament_max_volumetric_speed = 7
|
||||
|
||||
[filament:Ultrafuse PET @MINI]
|
||||
inherits = Ultrafuse PET; *PETMINI*
|
||||
|
||||
[filament:Ultrafuse PRO1]
|
||||
inherits = Prusament PLA
|
||||
filament_vendor = BASF
|
||||
filament_cost =
|
||||
filament_density = 1.25
|
||||
filament_spool_weight = 0
|
||||
filament_colour = #FFFFFF
|
||||
# filament overrides
|
||||
# filament_retract_length = 2
|
||||
# filament_retract_speed = 40
|
||||
# filament_retract_before_travel = 2
|
||||
# filament_wipe = 0
|
||||
|
||||
[filament:Ultrafuse ABS]
|
||||
inherits = *ABSC*
|
||||
filament_vendor = BASF
|
||||
filament_cost =
|
||||
filament_density = 1.04
|
||||
min_fan_speed = 10
|
||||
max_fan_speed = 20
|
||||
bed_temperature = 100
|
||||
disable_fan_first_layers = 3
|
||||
filament_colour = #FFFFFF
|
||||
# filament overrides
|
||||
# filament_retract_length = 2
|
||||
# filament_retract_speed = 40
|
||||
# filament_retract_before_travel = 2
|
||||
# filament_wipe = 0
|
||||
|
||||
[filament:Ultrafuse ABS @MINI]
|
||||
inherits = Ultrafuse ABS; *ABSMINI*
|
||||
|
||||
[filament:Ultrafuse 17-4 PH]
|
||||
inherits = *ABSC*
|
||||
filament_vendor = BASF
|
||||
filament_cost =
|
||||
filament_density = 4.5
|
||||
extrusion_multiplier = 1.08
|
||||
first_layer_temperature = 250
|
||||
first_layer_bed_temperature = 100
|
||||
temperature = 250
|
||||
bed_temperature = 100
|
||||
min_fan_speed = 0
|
||||
max_fan_speed = 0
|
||||
bridge_fan_speed = 0
|
||||
cooling = 0
|
||||
fan_always_on = 0
|
||||
filament_max_volumetric_speed = 4
|
||||
filament_type = METAL
|
||||
compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
start_filament_gcode = "M900 K0"
|
||||
filament_colour = #FFFFFF
|
||||
|
||||
[filament:Polymaker PC-Max]
|
||||
inherits = *ABS*
|
||||
filament_vendor = Polymaker
|
||||
@ -5712,6 +5981,83 @@ initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Made for Prusa
|
||||
|
||||
[sla_material:Prusa Vibrant Orange Tough @0.025 SL1S]
|
||||
inherits = *0.025_sl1s*
|
||||
exposure_time = 4
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Made for Prusa
|
||||
|
||||
[sla_material:Prusa Deep Blue Transparent Tough @0.025 SL1S]
|
||||
inherits = *0.025_sl1s*
|
||||
exposure_time = 4
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Made for Prusa
|
||||
|
||||
[sla_material:Prusa Green Dental Casting @0.025 SL1S]
|
||||
inherits = *0.025_sl1s*
|
||||
exposure_time = 3
|
||||
initial_exposure_time = 50
|
||||
material_type = Casting
|
||||
material_vendor = Made for Prusa
|
||||
|
||||
[sla_material:PrimaCreator Tough Light Grey @0.025 SL1S]
|
||||
inherits = *0.025_sl1s*
|
||||
exposure_time = 1.8
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = PrimaCreator
|
||||
|
||||
[sla_material:PrimaCreator Tough Clear @0.025 SL1S]
|
||||
inherits = *0.025_sl1s*
|
||||
exposure_time = 1.8
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = PrimaCreator
|
||||
|
||||
[sla_material:PrimaCreator Tough White @0.025 SL1S]
|
||||
inherits = *0.025_sl1s*
|
||||
exposure_time = 1.8
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = PrimaCreator
|
||||
|
||||
[sla_material:PrimaCreator Flex Clear @0.025 SL1S]
|
||||
inherits = *0.025_sl1s*
|
||||
exposure_time = 1.8
|
||||
initial_exposure_time = 25
|
||||
material_type = Flexible
|
||||
material_vendor = PrimaCreator
|
||||
|
||||
[sla_material:PrimaCreator Water Washable Transparent @0.025 SL1S]
|
||||
inherits = *0.025_sl1s*
|
||||
exposure_time = 1.8
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = PrimaCreator
|
||||
|
||||
[sla_material:DruckWege Type D Dental Model @0.025 SL1S]
|
||||
inherits = *0.025_sl1s*
|
||||
exposure_time = 1.2
|
||||
initial_exposure_time = 15
|
||||
material_type = Dental
|
||||
material_vendor = DruckWege
|
||||
|
||||
[sla_material:DruckWege Type D Standard White @0.025 SL1S]
|
||||
inherits = *0.025_sl1s*
|
||||
exposure_time = 1.6
|
||||
initial_exposure_time = 15
|
||||
material_type = Tough
|
||||
material_vendor = DruckWege
|
||||
|
||||
[sla_material:DruckWege Type D Standard Pigmentfrei Clear @0.025 SL1S]
|
||||
inherits = *0.025_sl1s*
|
||||
exposure_time = 1.8
|
||||
initial_exposure_time = 15
|
||||
material_type = Tough
|
||||
material_vendor = DruckWege
|
||||
|
||||
[sla_material:3DM-ABS Orange @0.025 SL1S]
|
||||
inherits = *0.025_sl1s*
|
||||
exposure_time = 1.8
|
||||
@ -5851,6 +6197,83 @@ initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Made for Prusa
|
||||
|
||||
[sla_material:Prusa Vibrant Orange Tough @0.05 SL1S]
|
||||
inherits = *0.05_sl1s*
|
||||
exposure_time = 5
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Made for Prusa
|
||||
|
||||
[sla_material:Prusa Deep Blue Transparent Tough @0.05 SL1S]
|
||||
inherits = *0.05_sl1s*
|
||||
exposure_time = 5
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Made for Prusa
|
||||
|
||||
[sla_material:Prusa Green Dental Casting @0.05 SL1S]
|
||||
inherits = *0.05_sl1s*
|
||||
exposure_time = 4
|
||||
initial_exposure_time = 50
|
||||
material_type = Casting
|
||||
material_vendor = Made for Prusa
|
||||
|
||||
[sla_material:PrimaCreator Tough Light Grey @0.05 SL1S]
|
||||
inherits = *0.05_sl1s*
|
||||
exposure_time = 2.4
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = PrimaCreator
|
||||
|
||||
[sla_material:PrimaCreator Tough Clear @0.05 SL1S]
|
||||
inherits = *0.05_sl1s*
|
||||
exposure_time = 2
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = PrimaCreator
|
||||
|
||||
[sla_material:PrimaCreator Tough White @0.05 SL1S]
|
||||
inherits = *0.05_sl1s*
|
||||
exposure_time = 2
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = PrimaCreator
|
||||
|
||||
[sla_material:PrimaCreator Flex Clear @0.05 SL1S]
|
||||
inherits = *0.05_sl1s*
|
||||
exposure_time = 2
|
||||
initial_exposure_time = 25
|
||||
material_type = Flexible
|
||||
material_vendor = PrimaCreator
|
||||
|
||||
[sla_material:PrimaCreator Water Washable Transparent @0.05 SL1S]
|
||||
inherits = *0.05_sl1s*
|
||||
exposure_time = 2
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = PrimaCreator
|
||||
|
||||
[sla_material:DruckWege Type D Dental Model @0.05 SL1S]
|
||||
inherits = *0.05_sl1s*
|
||||
exposure_time = 1.4
|
||||
initial_exposure_time = 15
|
||||
material_type = Dental
|
||||
material_vendor = DruckWege
|
||||
|
||||
[sla_material:DruckWege Type D Standard White @0.05 SL1S]
|
||||
inherits = *0.05_sl1s*
|
||||
exposure_time = 2
|
||||
initial_exposure_time = 15
|
||||
material_type = Tough
|
||||
material_vendor = DruckWege
|
||||
|
||||
[sla_material:DruckWege Type D Standard Pigmentfrei Clear @0.05 SL1S]
|
||||
inherits = *0.05_sl1s*
|
||||
exposure_time = 2
|
||||
initial_exposure_time = 15
|
||||
material_type = Tough
|
||||
material_vendor = DruckWege
|
||||
|
||||
[sla_material:3DM-ABS Orange @0.05 SL1S]
|
||||
inherits = *0.05_sl1s*
|
||||
exposure_time = 2.6
|
||||
@ -5990,6 +6413,69 @@ initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Made for Prusa
|
||||
|
||||
[sla_material:Prusa Vibrant Orange Tough @0.1 SL1S]
|
||||
inherits = *0.1_sl1s*
|
||||
exposure_time = 10
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Made for Prusa
|
||||
|
||||
[sla_material:Prusa Deep Blue Transparent Tough @0.1 SL1S]
|
||||
inherits = *0.1_sl1s*
|
||||
exposure_time = 10
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Made for Prusa
|
||||
|
||||
[sla_material:Prusa Green Dental Casting @0.1 SL1S]
|
||||
inherits = *0.1_sl1s*
|
||||
exposure_time = 8
|
||||
initial_exposure_time = 50
|
||||
material_type = Casting
|
||||
material_vendor = Made for Prusa
|
||||
|
||||
[sla_material:PrimaCreator Tough Light Grey @0.1 SL1S]
|
||||
inherits = *0.1_sl1s*
|
||||
exposure_time = 3
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = PrimaCreator
|
||||
|
||||
[sla_material:PrimaCreator Tough Clear @0.1 SL1S]
|
||||
inherits = *0.1_sl1s*
|
||||
exposure_time = 2.6
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = PrimaCreator
|
||||
|
||||
[sla_material:PrimaCreator Tough White @0.1 SL1S]
|
||||
inherits = *0.1_sl1s*
|
||||
exposure_time = 2.6
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = PrimaCreator
|
||||
|
||||
[sla_material:PrimaCreator Flex Clear @0.1 SL1S]
|
||||
inherits = *0.1_sl1s*
|
||||
exposure_time = 2.6
|
||||
initial_exposure_time = 25
|
||||
material_type = Flexible
|
||||
material_vendor = PrimaCreator
|
||||
|
||||
[sla_material:PrimaCreator Water Washable Transparent @0.1 SL1S]
|
||||
inherits = *0.1_sl1s*
|
||||
exposure_time = 2.6
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = PrimaCreator
|
||||
|
||||
[sla_material:DruckWege Type D Dental Model @0.1 SL1S]
|
||||
inherits = *0.1_sl1s*
|
||||
exposure_time = 2.6
|
||||
initial_exposure_time = 15
|
||||
material_type = Dental
|
||||
material_vendor = DruckWege
|
||||
|
||||
[sla_material:3DM-ABS Orange @0.1 SL1S]
|
||||
inherits = *0.1_sl1s*
|
||||
exposure_time = 3
|
||||
|
@ -22,6 +22,8 @@ uniform vec4 uniform_color;
|
||||
varying vec3 clipping_planes_dots;
|
||||
varying vec4 model_pos;
|
||||
|
||||
uniform bool volume_mirrored;
|
||||
|
||||
void main()
|
||||
{
|
||||
if (any(lessThan(clipping_planes_dots, ZERO)))
|
||||
@ -34,6 +36,9 @@ void main()
|
||||
triangle_normal = -triangle_normal;
|
||||
#endif
|
||||
|
||||
if (volume_mirrored)
|
||||
triangle_normal = -triangle_normal;
|
||||
|
||||
// First transform the normal into camera space and normalize the result.
|
||||
vec3 eye_normal = normalize(gl_NormalMatrix * triangle_normal);
|
||||
|
||||
|
@ -705,7 +705,7 @@ bool CLI::setup(int argc, char **argv)
|
||||
|
||||
// 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)
|
||||
for (const t_optiondef_map::value_type &optdef : *options)
|
||||
m_config.option(optdef.first, true);
|
||||
|
||||
set_data_dir(m_config.opt_string("datadir"));
|
||||
|
@ -577,7 +577,7 @@ private:
|
||||
|
||||
|
||||
template<class Level>
|
||||
Shapes calcnfp(const Item &trsh, Level)
|
||||
Shapes calcnfp(const Item &/*trsh*/, Level)
|
||||
{ // Function for arbitrary level of nfp implementation
|
||||
|
||||
// TODO: implement
|
||||
|
@ -33,7 +33,8 @@ public:
|
||||
PackResult(Item& item):
|
||||
item_ptr_(&item),
|
||||
move_(item.translation()),
|
||||
rot_(item.rotation()) {}
|
||||
rot_(item.rotation()),
|
||||
overfit_(1.0) {}
|
||||
|
||||
PackResult(double overfit = 1.0):
|
||||
item_ptr_(nullptr), overfit_(overfit) {}
|
||||
|
@ -114,6 +114,7 @@ static ConstPrintObjectPtrs get_top_level_objects_with_brim(const Print &print,
|
||||
clipper.AddPaths(islands_clip, ClipperLib_Z::ptSubject, true);
|
||||
// Execute union operation to construct polytree
|
||||
ClipperLib_Z::PolyTree islands_polytree;
|
||||
//FIXME likely pftNonZero or ptfPositive would be better. Why are we using ptfEvenOdd for Unions?
|
||||
clipper.Execute(ClipperLib_Z::ctUnion, islands_polytree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd);
|
||||
|
||||
std::unordered_set<size_t> processed_objects_idx;
|
||||
@ -486,7 +487,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
|
||||
clipper.AddPaths(input_subject, ClipperLib_Z::ptSubject, true);
|
||||
clipper.AddPaths(input_clip, ClipperLib_Z::ptClip, true);
|
||||
// perform operation
|
||||
clipper.Execute(ClipperLib_Z::ctDifference, trimming, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd);
|
||||
clipper.Execute(ClipperLib_Z::ctDifference, trimming, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero);
|
||||
}
|
||||
|
||||
// Second, trim the extrusion loops with the trimming regions.
|
||||
@ -515,7 +516,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
|
||||
clipper.AddPaths(trimming, ClipperLib_Z::ptClip, true);
|
||||
// perform operation
|
||||
ClipperLib_Z::PolyTree loops_trimmed_tree;
|
||||
clipper.Execute(ClipperLib_Z::ctDifference, loops_trimmed_tree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd);
|
||||
clipper.Execute(ClipperLib_Z::ctDifference, loops_trimmed_tree, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero);
|
||||
ClipperLib_Z::PolyTreeToPaths(loops_trimmed_tree, loops_trimmed);
|
||||
}
|
||||
|
||||
|
@ -193,12 +193,8 @@ bool ExtrusionLoop::split_at_vertex(const Point &point)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Splitting an extrusion loop, possibly made of multiple segments, some of the segments may be bridging.
|
||||
void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang)
|
||||
std::pair<size_t, Point> ExtrusionLoop::get_closest_path_and_point(const Point& point, bool prefer_non_overhang) const
|
||||
{
|
||||
if (this->paths.empty())
|
||||
return;
|
||||
|
||||
// Find the closest path and closest point belonging to that path. Avoid overhangs, if asked for.
|
||||
size_t path_idx = 0;
|
||||
Point p;
|
||||
@ -207,15 +203,15 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang)
|
||||
Point p_non_overhang;
|
||||
size_t path_idx_non_overhang = 0;
|
||||
double min_non_overhang = std::numeric_limits<double>::max();
|
||||
for (const ExtrusionPath &path : this->paths) {
|
||||
for (const ExtrusionPath& path : this->paths) {
|
||||
Point p_tmp = point.projection_onto(path.polyline);
|
||||
double dist = (p_tmp - point).cast<double>().norm();
|
||||
if (dist < min) {
|
||||
p = p_tmp;
|
||||
min = dist;
|
||||
path_idx = &path - &this->paths.front();
|
||||
}
|
||||
if (prefer_non_overhang && ! is_bridge(path.role()) && dist < min_non_overhang) {
|
||||
}
|
||||
if (prefer_non_overhang && !is_bridge(path.role()) && dist < min_non_overhang) {
|
||||
p_non_overhang = p_tmp;
|
||||
min_non_overhang = dist;
|
||||
path_idx_non_overhang = &path - &this->paths.front();
|
||||
@ -224,9 +220,19 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang)
|
||||
if (prefer_non_overhang && min_non_overhang != std::numeric_limits<double>::max()) {
|
||||
// Only apply the non-overhang point if there is one.
|
||||
path_idx = path_idx_non_overhang;
|
||||
p = p_non_overhang;
|
||||
p = p_non_overhang;
|
||||
}
|
||||
}
|
||||
return std::make_pair(path_idx, p);
|
||||
}
|
||||
|
||||
// Splitting an extrusion loop, possibly made of multiple segments, some of the segments may be bridging.
|
||||
void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang)
|
||||
{
|
||||
if (this->paths.empty())
|
||||
return;
|
||||
|
||||
auto [path_idx, p] = get_closest_path_and_point(point, prefer_non_overhang);
|
||||
|
||||
// now split path_idx in two parts
|
||||
const ExtrusionPath &path = this->paths[path_idx];
|
||||
|
@ -258,6 +258,7 @@ public:
|
||||
double length() const override;
|
||||
bool split_at_vertex(const Point &point);
|
||||
void split_at(const Point &point, bool prefer_non_overhang);
|
||||
std::pair<size_t, Point> get_closest_path_and_point(const Point& point, bool prefer_non_overhang) const;
|
||||
void clip_end(double distance, ExtrusionPaths* paths) const;
|
||||
// Test, whether the point is extruded by a bridging flow.
|
||||
// This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead.
|
||||
|
@ -916,7 +916,12 @@ namespace Slic3r {
|
||||
}
|
||||
//FIXME Loading a "will be one day a legacy format" of configuration in a form of a G-code comment.
|
||||
// Each config line is prefixed with a semicolon (G-code comment), that is ugly.
|
||||
config_substitutions.substitutions = config.load_from_ini_string_commented(std::move(buffer), config_substitutions.rule);
|
||||
|
||||
// Replacing the legacy function with load_from_ini_string_commented leads to issues when
|
||||
// parsing 3MFs from before PrusaSlicer 2.0.0 (which can have duplicated entries in the INI.
|
||||
// See https://github.com/prusa3d/PrusaSlicer/issues/7155. We'll revert it for now.
|
||||
//config_substitutions.substitutions = config.load_from_ini_string_commented(std::move(buffer), config_substitutions.rule);
|
||||
ConfigBase::load_from_gcode_string_legacy(config, buffer.data(), config_substitutions);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -711,7 +711,12 @@ void AMFParserContext::endElement(const char * /* name */)
|
||||
if ((m_config != nullptr) && strncmp(m_value[0].c_str(), SLIC3R_CONFIG_TYPE, strlen(SLIC3R_CONFIG_TYPE)) == 0) {
|
||||
//FIXME Loading a "will be one day a legacy format" of configuration in a form of a G-code comment.
|
||||
// Each config line is prefixed with a semicolon (G-code comment), that is ugly.
|
||||
m_config_substitutions->substitutions = m_config->load_from_ini_string_commented(std::move(m_value[1].c_str()), m_config_substitutions->rule);
|
||||
|
||||
// Replacing the legacy function with load_from_ini_string_commented leads to issues when
|
||||
// parsing 3MFs from before PrusaSlicer 2.0.0 (which can have duplicated entries in the INI.
|
||||
// See https://github.com/prusa3d/PrusaSlicer/issues/7155. We'll revert it for now.
|
||||
//m_config_substitutions->substitutions = m_config->load_from_ini_string_commented(std::move(m_value[1].c_str()), m_config_substitutions->rule);
|
||||
ConfigBase::load_from_gcode_string_legacy(*m_config, std::move(m_value[1].c_str()), *m_config_substitutions);
|
||||
}
|
||||
else if (strncmp(m_value[0].c_str(), "slic3r.", 7) == 0) {
|
||||
const char *opt_key = m_value[0].c_str() + 7;
|
||||
|
@ -2479,57 +2479,49 @@ std::string GCode::change_layer(coordf_t print_z)
|
||||
|
||||
|
||||
|
||||
static std::unique_ptr<EdgeGrid::Grid> calculate_layer_edge_grid(const Layer& layer)
|
||||
{
|
||||
auto out = make_unique<EdgeGrid::Grid>();
|
||||
|
||||
// Create the distance field for a layer below.
|
||||
const coord_t distance_field_resolution = coord_t(scale_(1.) + 0.5);
|
||||
out->create(layer.lslices, distance_field_resolution);
|
||||
out->calculate_sdf();
|
||||
#if 0
|
||||
{
|
||||
static int iRun = 0;
|
||||
BoundingBox bbox = (*lower_layer_edge_grid)->bbox();
|
||||
bbox.min(0) -= scale_(5.f);
|
||||
bbox.min(1) -= scale_(5.f);
|
||||
bbox.max(0) += scale_(5.f);
|
||||
bbox.max(1) += scale_(5.f);
|
||||
EdgeGrid::save_png(*(*lower_layer_edge_grid), bbox, scale_(0.1f), debug_out_path("GCode_extrude_loop_edge_grid-%d.png", iRun++));
|
||||
}
|
||||
#endif
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, double speed, std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid)
|
||||
{
|
||||
// get a copy; don't modify the orientation of the original loop object otherwise
|
||||
// next copies (if any) would not detect the correct orientation
|
||||
|
||||
if (m_layer->lower_layer != nullptr && lower_layer_edge_grid != nullptr) {
|
||||
if (! *lower_layer_edge_grid) {
|
||||
// Create the distance field for a layer below.
|
||||
const coord_t distance_field_resolution = coord_t(scale_(1.) + 0.5);
|
||||
*lower_layer_edge_grid = make_unique<EdgeGrid::Grid>();
|
||||
(*lower_layer_edge_grid)->create(m_layer->lower_layer->lslices, distance_field_resolution);
|
||||
(*lower_layer_edge_grid)->calculate_sdf();
|
||||
#if 0
|
||||
{
|
||||
static int iRun = 0;
|
||||
BoundingBox bbox = (*lower_layer_edge_grid)->bbox();
|
||||
bbox.min(0) -= scale_(5.f);
|
||||
bbox.min(1) -= scale_(5.f);
|
||||
bbox.max(0) += scale_(5.f);
|
||||
bbox.max(1) += scale_(5.f);
|
||||
EdgeGrid::save_png(*(*lower_layer_edge_grid), bbox, scale_(0.1f), debug_out_path("GCode_extrude_loop_edge_grid-%d.png", iRun++));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (m_layer->lower_layer && lower_layer_edge_grid != nullptr && ! *lower_layer_edge_grid)
|
||||
*lower_layer_edge_grid = calculate_layer_edge_grid(*m_layer->lower_layer);
|
||||
|
||||
// extrude all loops ccw
|
||||
bool was_clockwise = loop.make_counter_clockwise();
|
||||
|
||||
SeamPosition seam_position = m_config.seam_position;
|
||||
if (loop.loop_role() == elrSkirt)
|
||||
seam_position = spNearest;
|
||||
|
||||
// find the point of the loop that is closest to the current extruder position
|
||||
// or randomize if requested
|
||||
Point last_pos = this->last_pos();
|
||||
if (m_config.spiral_vase) {
|
||||
loop.split_at(last_pos, false);
|
||||
} else {
|
||||
const EdgeGrid::Grid* edge_grid_ptr = (lower_layer_edge_grid && *lower_layer_edge_grid)
|
||||
? lower_layer_edge_grid->get()
|
||||
: nullptr;
|
||||
Point seam = m_seam_placer.get_seam(*m_layer, seam_position, loop,
|
||||
last_pos, EXTRUDER_CONFIG(nozzle_diameter),
|
||||
(m_layer == NULL ? nullptr : m_layer->object()),
|
||||
was_clockwise, edge_grid_ptr);
|
||||
// Split the loop at the point with a minium penalty.
|
||||
if (!loop.split_at_vertex(seam))
|
||||
// The point is not in the original loop. Insert it.
|
||||
loop.split_at(seam, true);
|
||||
}
|
||||
else
|
||||
m_seam_placer.place_seam(loop, this->last_pos(), m_config.external_perimeters_first,
|
||||
EXTRUDER_CONFIG(nozzle_diameter), lower_layer_edge_grid ? lower_layer_edge_grid->get() : nullptr);
|
||||
|
||||
// clip the path to avoid the extruder to get exactly on the first point of the loop;
|
||||
// if polyline was shorter than the clipping distance we'd get a null polyline, so
|
||||
@ -2652,7 +2644,17 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vector<Obje
|
||||
for (const ObjectByExtruder::Island::Region ®ion : by_region)
|
||||
if (! region.perimeters.empty()) {
|
||||
m_config.apply(print.get_print_region(®ion - &by_region.front()).config());
|
||||
for (const ExtrusionEntity *ee : region.perimeters)
|
||||
|
||||
// plan_perimeters tries to place seams, it needs to have the lower_layer_edge_grid calculated already.
|
||||
if (m_layer->lower_layer && ! lower_layer_edge_grid)
|
||||
lower_layer_edge_grid = calculate_layer_edge_grid(*m_layer->lower_layer);
|
||||
|
||||
m_seam_placer.plan_perimeters(std::vector<const ExtrusionEntity*>(region.perimeters.begin(), region.perimeters.end()),
|
||||
*m_layer, m_config.seam_position, this->last_pos(), EXTRUDER_CONFIG(nozzle_diameter),
|
||||
(m_layer == NULL ? nullptr : m_layer->object()),
|
||||
(lower_layer_edge_grid ? lower_layer_edge_grid.get() : nullptr));
|
||||
|
||||
for (const ExtrusionEntity* ee : region.perimeters)
|
||||
gcode += this->extrude_entity(*ee, "perimeter", -1., &lower_layer_edge_grid);
|
||||
}
|
||||
return gcode;
|
||||
@ -2807,7 +2809,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
gcode += this->unretract();
|
||||
|
||||
// adjust acceleration
|
||||
{
|
||||
if (m_config.default_acceleration.value > 0) {
|
||||
double acceleration;
|
||||
if (this->on_first_layer() && m_config.first_layer_acceleration.value > 0) {
|
||||
acceleration = m_config.first_layer_acceleration.value;
|
||||
|
@ -208,9 +208,7 @@ void GCodeProcessor::TimeMachine::simulate_st_synchronize(float additional_time)
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
time += additional_time;
|
||||
gcode_time.cache += additional_time;
|
||||
calculate_time();
|
||||
calculate_time(0, additional_time);
|
||||
}
|
||||
|
||||
static void planner_forward_pass_kernel(GCodeProcessor::TimeBlock& prev, GCodeProcessor::TimeBlock& curr)
|
||||
@ -283,7 +281,7 @@ static void recalculate_trapezoids(std::vector<GCodeProcessor::TimeBlock>& block
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks)
|
||||
void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks, float additional_time)
|
||||
{
|
||||
if (!enabled || blocks.size() < 2)
|
||||
return;
|
||||
@ -305,6 +303,9 @@ void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks)
|
||||
for (size_t i = 0; i < n_blocks_process; ++i) {
|
||||
const TimeBlock& block = blocks[i];
|
||||
float block_time = block.time();
|
||||
if (i == 0)
|
||||
block_time += additional_time;
|
||||
|
||||
time += block_time;
|
||||
#if ENABLE_TRAVEL_TIME
|
||||
if (block.move_type == EMoveType::Travel)
|
||||
@ -317,16 +318,14 @@ void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks)
|
||||
#if !ENABLE_TRAVEL_TIME
|
||||
roles_time[static_cast<size_t>(block.role)] += block_time;
|
||||
#endif // !ENABLE_TRAVEL_TIME
|
||||
if (block.layer_id > 0) {
|
||||
if (block.layer_id >= layers_time.size()) {
|
||||
size_t curr_size = layers_time.size();
|
||||
layers_time.resize(block.layer_id);
|
||||
for (size_t i = curr_size; i < layers_time.size(); ++i) {
|
||||
layers_time[i] = 0.0f;
|
||||
}
|
||||
if (block.layer_id >= layers_time.size()) {
|
||||
const size_t curr_size = layers_time.size();
|
||||
layers_time.resize(block.layer_id);
|
||||
for (size_t i = curr_size; i < layers_time.size(); ++i) {
|
||||
layers_time[i] = 0.0f;
|
||||
}
|
||||
layers_time[block.layer_id - 1] += block_time;
|
||||
}
|
||||
layers_time[block.layer_id - 1] += block_time;
|
||||
g1_times_cache.push_back({ block.g1_line_id, time });
|
||||
// update times for remaining time to printer stop placeholders
|
||||
auto it_stop_time = std::lower_bound(stop_times.begin(), stop_times.end(), block.g1_line_id,
|
||||
@ -2565,7 +2564,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||
block.role = m_extrusion_role;
|
||||
block.distance = distance;
|
||||
block.g1_line_id = m_g1_line_id;
|
||||
block.layer_id = m_layer_id;
|
||||
block.layer_id = std::max<unsigned int>(1, m_layer_id);
|
||||
|
||||
// calculates block cruise feedrate
|
||||
float min_feedrate_factor = 1.0f;
|
||||
|
@ -280,7 +280,7 @@ namespace Slic3r {
|
||||
|
||||
// Simulates firmware st_synchronize() call
|
||||
void simulate_st_synchronize(float additional_time = 0.0f);
|
||||
void calculate_time(size_t keep_last_n_blocks = 0);
|
||||
void calculate_time(size_t keep_last_n_blocks = 0, float additional_time = 0.0f);
|
||||
};
|
||||
|
||||
struct TimeProcessor
|
||||
|
@ -292,11 +292,165 @@ void SeamPlacer::init(const Print& print)
|
||||
|
||||
|
||||
|
||||
Point SeamPlacer::get_seam(const Layer& layer, const SeamPosition seam_position,
|
||||
const ExtrusionLoop& loop, Point last_pos, coordf_t nozzle_dmr,
|
||||
const PrintObject* po, bool was_clockwise, const EdgeGrid::Grid* lower_layer_edge_grid)
|
||||
void SeamPlacer::plan_perimeters(const std::vector<const ExtrusionEntity*> perimeters,
|
||||
const Layer& layer, SeamPosition seam_position,
|
||||
Point last_pos, coordf_t nozzle_dmr, const PrintObject* po,
|
||||
const EdgeGrid::Grid* lower_layer_edge_grid)
|
||||
{
|
||||
// When printing the perimeters, we want the seams on external and internal perimeters to match.
|
||||
// We have a list of perimeters in the order to be printed. Each internal perimeter must inherit
|
||||
// the seam from the previous external perimeter.
|
||||
|
||||
m_plan.clear();
|
||||
m_plan_idx = 0;
|
||||
|
||||
if (perimeters.empty() || ! po)
|
||||
return;
|
||||
|
||||
m_plan.resize(perimeters.size());
|
||||
|
||||
for (int i = 0; i < int(perimeters.size()); ++i) {
|
||||
if (perimeters[i]->role() == erExternalPerimeter && perimeters[i]->is_loop()) {
|
||||
last_pos = this->calculate_seam(
|
||||
layer, seam_position, *dynamic_cast<const ExtrusionLoop*>(perimeters[i]), nozzle_dmr,
|
||||
po, lower_layer_edge_grid, last_pos);
|
||||
m_plan[i].external = true;
|
||||
m_plan[i].seam_position = seam_position;
|
||||
m_plan[i].layer = &layer;
|
||||
m_plan[i].po = po;
|
||||
}
|
||||
m_plan[i].pt = last_pos;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SeamPlacer::place_seam(ExtrusionLoop& loop, const Point& last_pos, bool external_first, double nozzle_diameter,
|
||||
const EdgeGrid::Grid* lower_layer_edge_grid)
|
||||
{
|
||||
const double seam_offset = nozzle_diameter;
|
||||
|
||||
Point seam = last_pos;
|
||||
if (! m_plan.empty() && m_plan_idx < m_plan.size()) {
|
||||
if (m_plan[m_plan_idx].external) {
|
||||
seam = m_plan[m_plan_idx].pt;
|
||||
// One more heuristics: if the seam is too far from current nozzle position,
|
||||
// try to place it again. This can happen in cases where the external perimeter
|
||||
// does not belong to the preceding ones and they are ordered so they end up
|
||||
// far from each other.
|
||||
if ((seam.cast<double>() - last_pos.cast<double>()).squaredNorm() > std::pow(scale_(5.*nozzle_diameter), 2.))
|
||||
seam = this->calculate_seam(*m_plan[m_plan_idx].layer, m_plan[m_plan_idx].seam_position, loop, nozzle_diameter,
|
||||
m_plan[m_plan_idx].po, lower_layer_edge_grid, last_pos);
|
||||
}
|
||||
else if (! external_first) {
|
||||
// Internal perimeter printed before the external.
|
||||
// First get list of external seams.
|
||||
std::vector<size_t> ext_seams;
|
||||
for (size_t i = 0; i < m_plan.size(); ++i) {
|
||||
if (m_plan[i].external)
|
||||
ext_seams.emplace_back(i);
|
||||
}
|
||||
|
||||
if (! ext_seams.empty()) {
|
||||
// First find the line segment closest to an external seam:
|
||||
int path_idx = 0;
|
||||
int line_idx = 0;
|
||||
size_t ext_seam_idx = size_t(-1);
|
||||
double min_dist_sqr = std::numeric_limits<double>::max();
|
||||
std::vector<Lines> lines_vect;
|
||||
for (int i = 0; i < int(loop.paths.size()); ++i) {
|
||||
lines_vect.emplace_back(loop.paths[i].polyline.lines());
|
||||
const Lines& lines = lines_vect.back();
|
||||
for (int j = 0; j < int(lines.size()); ++j) {
|
||||
for (size_t k : ext_seams) {
|
||||
double d_sqr = lines[j].distance_to_squared(m_plan[k].pt);
|
||||
if (d_sqr < min_dist_sqr) {
|
||||
path_idx = i;
|
||||
line_idx = j;
|
||||
ext_seam_idx = k;
|
||||
min_dist_sqr = d_sqr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only accept seam that is reasonably close.
|
||||
double limit_dist_sqr = std::pow(double(scale_((ext_seam_idx - m_plan_idx) * nozzle_diameter * 2.)), 2.);
|
||||
if (ext_seam_idx != size_t(-1) && min_dist_sqr < limit_dist_sqr) {
|
||||
// Now find a projection of the external seam
|
||||
const Lines& lines = lines_vect[path_idx];
|
||||
Point closest = m_plan[ext_seam_idx].pt.projection_onto(lines[line_idx]);
|
||||
double dist = (closest.cast<double>() - lines[line_idx].b.cast<double>()).norm();
|
||||
|
||||
// And walk along the perimeter until we make enough space for
|
||||
// seams of all perimeters beforethe external one.
|
||||
double offset = (ext_seam_idx - m_plan_idx) * scale_(seam_offset);
|
||||
double last_offset = offset;
|
||||
offset -= dist;
|
||||
const Point* a = &closest;
|
||||
const Point* b = &lines[line_idx].b;
|
||||
while (++line_idx < int(lines.size()) && offset > 0.) {
|
||||
last_offset = offset;
|
||||
offset -= lines[line_idx].length();
|
||||
a = &lines[line_idx].a;
|
||||
b = &lines[line_idx].b;
|
||||
}
|
||||
|
||||
// We have walked far enough, too far maybe. Interpolate on the
|
||||
// last segment to find the end precisely.
|
||||
offset = std::min(0., offset); // In case that offset is still positive (we may have "wrapped around")
|
||||
double ratio = last_offset / (last_offset - offset);
|
||||
seam = (a->cast<double>() + ((b->cast<double>() - a->cast<double>()) * ratio)).cast<coord_t>();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// We should have a candidate ready from before. If not, use last_pos.
|
||||
if (m_plan_idx > 0 && m_plan[m_plan_idx - 1].precalculated)
|
||||
seam = m_plan[m_plan_idx - 1].pt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Split the loop at the point with a minium penalty.
|
||||
if (!loop.split_at_vertex(seam))
|
||||
// The point is not in the original loop. Insert it.
|
||||
loop.split_at(seam, true);
|
||||
|
||||
if (external_first && m_plan_idx+1<m_plan.size() && ! m_plan[m_plan_idx+1].external) {
|
||||
// Next perimeter should start near this one.
|
||||
const double dist_sqr = std::pow(double(scale_(seam_offset)), 2.);
|
||||
double running_sqr = 0.;
|
||||
double running_sqr_last = 0.;
|
||||
if (!loop.paths.empty() && loop.paths.back().polyline.points.size() > 1) {
|
||||
const ExtrusionPath& last = loop.paths.back();
|
||||
auto it = last.polyline.points.crbegin() + 1;
|
||||
for (; it != last.polyline.points.crend(); ++it) {
|
||||
running_sqr += (it->cast<double>() - (it - 1)->cast<double>()).squaredNorm();
|
||||
if (running_sqr > dist_sqr)
|
||||
break;
|
||||
running_sqr_last = running_sqr;
|
||||
}
|
||||
if (running_sqr <= dist_sqr)
|
||||
it = last.polyline.points.crend() - 1;
|
||||
// Now interpolate.
|
||||
double ratio = (std::sqrt(dist_sqr) - std::sqrt(running_sqr_last)) / (std::sqrt(running_sqr) - std::sqrt(running_sqr_last));
|
||||
m_plan[m_plan_idx + 1].pt = ((it - 1)->cast<double>() + (it->cast<double>() - (it - 1)->cast<double>()) * std::min(ratio, 1.)).cast<coord_t>();
|
||||
m_plan[m_plan_idx + 1].precalculated = true;
|
||||
}
|
||||
}
|
||||
|
||||
++m_plan_idx;
|
||||
}
|
||||
|
||||
|
||||
// Returns a seam for an EXTERNAL perimeter.
|
||||
Point SeamPlacer::calculate_seam(const Layer& layer, const SeamPosition seam_position,
|
||||
const ExtrusionLoop& loop, coordf_t nozzle_dmr, const PrintObject* po,
|
||||
const EdgeGrid::Grid* lower_layer_edge_grid, Point last_pos)
|
||||
{
|
||||
assert(loop.role() == erExternalPerimeter);
|
||||
Polygon polygon = loop.polygon();
|
||||
bool was_clockwise = polygon.make_counter_clockwise();
|
||||
BoundingBox polygon_bb = polygon.bounding_box();
|
||||
const coord_t nozzle_r = coord_t(scale_(0.5 * nozzle_dmr) + 0.5);
|
||||
|
||||
@ -438,7 +592,7 @@ Point SeamPlacer::get_seam(const Layer& layer, const SeamPosition seam_position,
|
||||
}
|
||||
}
|
||||
|
||||
if (seam_position == spAligned && loop.role() == erExternalPerimeter)
|
||||
if (seam_position == spAligned)
|
||||
m_seam_history.add_seam(po, polygon.points[idx_min], polygon_bb);
|
||||
|
||||
|
||||
@ -466,42 +620,8 @@ Point SeamPlacer::get_seam(const Layer& layer, const SeamPosition seam_position,
|
||||
#endif
|
||||
return polygon.points[idx_min];
|
||||
|
||||
} else { // spRandom
|
||||
if (po->print()->default_region_config().external_perimeters_first) {
|
||||
if (loop.role() == erExternalPerimeter)
|
||||
last_pos = this->get_random_seam(layer_idx, polygon, po_idx);
|
||||
else {
|
||||
// Internal perimeters will just use last_pos.
|
||||
}
|
||||
} else {
|
||||
if (loop.loop_role() == elrContourInternalPerimeter && loop.role() != erExternalPerimeter) {
|
||||
// This loop does not contain any other loop. Set a random position.
|
||||
// The other loops will get a seam close to the random point chosen
|
||||
// on the innermost contour.
|
||||
last_pos = this->get_random_seam(layer_idx, polygon, po_idx);
|
||||
m_last_loop_was_external = false;
|
||||
}
|
||||
if (loop.role() == erExternalPerimeter) {
|
||||
if (m_last_loop_was_external) {
|
||||
// There was no internal perimeter before this one.
|
||||
last_pos = this->get_random_seam(layer_idx, polygon, po_idx);
|
||||
} else {
|
||||
if (is_custom_seam_on_layer(layer_idx, po_idx)) {
|
||||
// There is a possibility that the loop will be influenced by custom
|
||||
// seam enforcer/blocker. In this case do not inherit the seam
|
||||
// from internal loops (which may conflict with the custom selection
|
||||
// and generate another random one.
|
||||
bool saw_custom = false;
|
||||
Point candidate = this->get_random_seam(layer_idx, polygon, po_idx, &saw_custom);
|
||||
if (saw_custom)
|
||||
last_pos = candidate;
|
||||
}
|
||||
}
|
||||
m_last_loop_was_external = true;
|
||||
}
|
||||
}
|
||||
return last_pos;
|
||||
}
|
||||
} else
|
||||
return this->get_random_seam(layer_idx, polygon, po_idx);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2,7 +2,9 @@
|
||||
#define libslic3r_SeamPlacer_hpp_
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "libslic3r/ExtrusionEntity.hpp"
|
||||
#include "libslic3r/Polygon.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
@ -41,16 +43,30 @@ class SeamPlacer {
|
||||
public:
|
||||
void init(const Print& print);
|
||||
|
||||
Point get_seam(const Layer& layer, const SeamPosition seam_position,
|
||||
const ExtrusionLoop& loop, Point last_pos,
|
||||
coordf_t nozzle_diameter, const PrintObject* po,
|
||||
bool was_clockwise, const EdgeGrid::Grid* lower_layer_edge_grid);
|
||||
// When perimeters are printed, first call this function with the respective
|
||||
// external perimeter. SeamPlacer will find a location for its seam and remember it.
|
||||
// Subsequent calls to get_seam will return this position.
|
||||
|
||||
|
||||
void plan_perimeters(const std::vector<const ExtrusionEntity*> perimeters,
|
||||
const Layer& layer, SeamPosition seam_position,
|
||||
Point last_pos, coordf_t nozzle_dmr, const PrintObject* po,
|
||||
const EdgeGrid::Grid* lower_layer_edge_grid);
|
||||
|
||||
void place_seam(ExtrusionLoop& loop, const Point& last_pos, bool external_first, double nozzle_diameter,
|
||||
const EdgeGrid::Grid* lower_layer_edge_grid);
|
||||
|
||||
|
||||
using TreeType = AABBTreeIndirect::Tree<2, coord_t>;
|
||||
using AlignedBoxType = Eigen::AlignedBox<TreeType::CoordType, TreeType::NumDimensions>;
|
||||
|
||||
private:
|
||||
|
||||
// When given an external perimeter (!), returns the seam.
|
||||
Point calculate_seam(const Layer& layer, const SeamPosition seam_position,
|
||||
const ExtrusionLoop& loop, coordf_t nozzle_dmr, const PrintObject* po,
|
||||
const EdgeGrid::Grid* lower_layer_edge_grid, Point last_pos);
|
||||
|
||||
struct CustomTrianglesPerLayer {
|
||||
Polygons polys;
|
||||
TreeType tree;
|
||||
@ -61,7 +77,16 @@ private:
|
||||
coordf_t m_last_print_z = -1.;
|
||||
const PrintObject* m_last_po = nullptr;
|
||||
|
||||
bool m_last_loop_was_external = true;
|
||||
struct SeamPoint {
|
||||
Point pt;
|
||||
bool precalculated = false;
|
||||
bool external = false;
|
||||
const Layer* layer = nullptr;
|
||||
SeamPosition seam_position;
|
||||
const PrintObject* po = nullptr;
|
||||
};
|
||||
std::vector<SeamPoint> m_plan;
|
||||
size_t m_plan_idx;
|
||||
|
||||
std::vector<std::vector<CustomTrianglesPerLayer>> m_enforcers;
|
||||
std::vector<std::vector<CustomTrianglesPerLayer>> m_blockers;
|
||||
|
@ -1671,7 +1671,7 @@ void visit_antipodals (Idx& ia, Idx &ib, Fn &&fn)
|
||||
|
||||
} // namespace rotcalip
|
||||
|
||||
bool intersects(const Polygon &A, const Polygon &B)
|
||||
bool convex_polygons_intersect(const Polygon &A, const Polygon &B)
|
||||
{
|
||||
using namespace rotcalip;
|
||||
|
||||
|
@ -563,7 +563,7 @@ inline bool is_rotation_ninety_degrees(const Vec3d &rotation)
|
||||
|
||||
// Returns true if the intersection of the two convex polygons A and B
|
||||
// is not an empty set.
|
||||
bool intersects(const Polygon &A, const Polygon &B);
|
||||
bool convex_polygons_intersect(const Polygon &A, const Polygon &B);
|
||||
|
||||
} } // namespace Slicer::Geometry
|
||||
|
||||
|
@ -63,11 +63,25 @@ bool Line::parallel_to(double angle) const
|
||||
return Slic3r::Geometry::directions_parallel(this->direction(), angle);
|
||||
}
|
||||
|
||||
bool Line::parallel_to(const Line& line) const
|
||||
{
|
||||
const Vec2d v1 = (this->b - this->a).cast<double>();
|
||||
const Vec2d v2 = (line.b - line.a).cast<double>();
|
||||
return sqr(cross2(v1, v2)) < sqr(EPSILON) * v1.squaredNorm() * v2.squaredNorm();
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool Line::perpendicular_to(double angle) const
|
||||
{
|
||||
return Slic3r::Geometry::directions_perpendicular(this->direction(), angle);
|
||||
}
|
||||
|
||||
bool Line::perpendicular_to(const Line& line) const
|
||||
{
|
||||
const Vec2d v1 = (this->b - this->a).cast<double>();
|
||||
const Vec2d v2 = (line.b - line.a).cast<double>();
|
||||
return sqr(v1.dot(v2)) < sqr(EPSILON) * v1.squaredNorm() * v2.squaredNorm();
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
bool Line::intersection(const Line &l2, Point *intersection) const
|
||||
|
@ -104,10 +104,10 @@ public:
|
||||
double distance_to(const Point &point) const { return distance_to(point, this->a, this->b); }
|
||||
double perp_distance_to(const Point &point) const;
|
||||
bool parallel_to(double angle) const;
|
||||
bool parallel_to(const Line &line) const { return this->parallel_to(line.direction()); }
|
||||
bool parallel_to(const Line& line) const;
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool perpendicular_to(double angle) const;
|
||||
bool perpendicular_to(const Line& line) const { return this->perpendicular_to(line.direction()); }
|
||||
bool perpendicular_to(const Line& line) const;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
double atan2_() const { return atan2(this->b(1) - this->a(1), this->b(0) - this->a(0)); }
|
||||
double orientation() const;
|
||||
|
@ -27,9 +27,10 @@
|
||||
namespace Slic3r {
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// Using rotating callipers to check for collision of two convex polygons. Thus both printbed_shape and obj_hull_2d are convex polygons.
|
||||
ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const Polygon& obj_hull_2d, double obj_min_z, double obj_max_z)
|
||||
{
|
||||
if (!Geometry::intersects(printbed_shape, obj_hull_2d))
|
||||
if (!Geometry::convex_polygons_intersect(printbed_shape, obj_hull_2d))
|
||||
return ModelInstancePVS_Fully_Outside;
|
||||
|
||||
bool contained_xy = true;
|
||||
@ -43,6 +44,7 @@ ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_
|
||||
return (contained_xy && contained_z) ? ModelInstancePVS_Inside : ModelInstancePVS_Partly_Outside;
|
||||
}
|
||||
|
||||
/*
|
||||
ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const BoundingBoxf3& box)
|
||||
{
|
||||
const Polygon box_hull_2d({
|
||||
@ -53,6 +55,7 @@ ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_
|
||||
});
|
||||
return printbed_collision_state(printbed_shape, print_volume_height, box_hull_2d, box.min.z(), box.max.z());
|
||||
}
|
||||
*/
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
Model& Model::assign_copy(const Model &rhs)
|
||||
@ -360,6 +363,7 @@ BoundingBoxf3 Model::bounding_box() const
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// printbed_shape is convex polygon
|
||||
unsigned int Model::update_print_volume_state(const Polygon& printbed_shape, double print_volume_height)
|
||||
{
|
||||
unsigned int num_printable = 0;
|
||||
@ -1569,6 +1573,7 @@ double ModelObject::get_instance_max_z(size_t instance_idx) const
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// printbed_shape is convex polygon
|
||||
unsigned int ModelObject::check_instances_print_volume_state(const Polygon& printbed_shape, double print_volume_height)
|
||||
{
|
||||
unsigned int num_printable = 0;
|
||||
|
@ -911,10 +911,12 @@ enum ModelInstanceEPrintVolumeState : unsigned char
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// return the state of the given object's volume (extrusion along z of obj_hull_2d from obj_min_z to obj_max_z)
|
||||
// with respect to the given print volume (extrusion along z of printbed_shape from zero to print_volume_height)
|
||||
// Using rotating callipers to check for collision of two convex polygons.
|
||||
ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const Polygon& obj_hull_2d, double obj_min_z, double obj_max_z);
|
||||
// return the state of the given box
|
||||
// with respect to the given print volume (extrusion along z of printbed_shape from zero to print_volume_height)
|
||||
ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const BoundingBoxf3& box);
|
||||
// Commented out, using rotating callipers is quite expensive for a bounding box test.
|
||||
//ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const BoundingBoxf3& box);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
// A single instance of a ModelObject.
|
||||
@ -1122,6 +1124,7 @@ public:
|
||||
// Set the print_volume_state of PrintObject::instances,
|
||||
// return total number of printable objects.
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// printbed_shape is convex polygon
|
||||
unsigned int update_print_volume_state(const Polygon& printbed_shape, double print_volume_height);
|
||||
#else
|
||||
unsigned int update_print_volume_state(const BoundingBoxf3 &print_volume);
|
||||
|
@ -1324,6 +1324,18 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
|
||||
}
|
||||
}
|
||||
|
||||
auto filter_out_small_polygons = [&num_extruders, &num_layers](std::vector<std::vector<Polygons>> &raw_surfaces, double min_area) -> void {
|
||||
for (size_t extruder_idx = 0; extruder_idx < num_extruders; ++extruder_idx)
|
||||
if (!raw_surfaces[extruder_idx].empty())
|
||||
for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx)
|
||||
if (!raw_surfaces[extruder_idx][layer_idx].empty())
|
||||
remove_small(raw_surfaces[extruder_idx][layer_idx], min_area);
|
||||
};
|
||||
|
||||
// Filter out polygons less than 0.1mm^2, because they are unprintable and causing dimples on outer primers (#7104)
|
||||
filter_out_small_polygons(top_raw, Slic3r::sqr(scale_(0.1f)));
|
||||
filter_out_small_polygons(bottom_raw, Slic3r::sqr(scale_(0.1f)));
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_TOP_BOTTOM
|
||||
{
|
||||
const char* colors[] = { "aqua", "black", "blue", "fuchsia", "gray", "green", "lime", "maroon", "navy", "olive", "purple", "red", "silver", "teal", "yellow" };
|
||||
|
@ -47,9 +47,9 @@ void MultiPoint::rotate(double angle, const Point ¢er)
|
||||
|
||||
double MultiPoint::length() const
|
||||
{
|
||||
Lines lines = this->lines();
|
||||
const Lines& lines = this->lines();
|
||||
double len = 0;
|
||||
for (Lines::iterator it = lines.begin(); it != lines.end(); ++it) {
|
||||
for (auto it = lines.cbegin(); it != lines.cend(); ++it) {
|
||||
len += it->length();
|
||||
}
|
||||
return len;
|
||||
|
@ -1244,8 +1244,8 @@ void PrintConfigDef::init_fff_params()
|
||||
def->enum_values.push_back("external");
|
||||
def->enum_values.push_back("all");
|
||||
def->enum_labels.push_back(L("None"));
|
||||
def->enum_labels.push_back(L("External perimeters"));
|
||||
def->enum_labels.push_back(L("All perimeters"));
|
||||
def->enum_labels.push_back(L("Outside walls"));
|
||||
def->enum_labels.push_back(L("All walls"));
|
||||
def->mode = comSimple;
|
||||
def->set_default_value(new ConfigOptionEnum<FuzzySkinType>(FuzzySkinType::None));
|
||||
|
||||
@ -3970,10 +3970,16 @@ std::string validate(const FullPrintConfig &cfg)
|
||||
if (em <= 0)
|
||||
return "Invalid value for --extrusion-multiplier";
|
||||
|
||||
// The following test was commented out after 482841b, see also https://github.com/prusa3d/PrusaSlicer/pull/6743.
|
||||
// The backend should now handle this case correctly. I.e., zero default_acceleration behaves as if all others
|
||||
// were zero too. This is now consistent with what the UI said would happen.
|
||||
// The UI already grays the fields out, there is no more reason to reject it here. This function validates the
|
||||
// config before exporting, leaving this check in would mean that config would be rejected before export
|
||||
// (although both the UI and the backend handle it).
|
||||
// --default-acceleration
|
||||
if ((cfg.perimeter_acceleration != 0. || cfg.infill_acceleration != 0. || cfg.bridge_acceleration != 0. || cfg.first_layer_acceleration != 0.) &&
|
||||
cfg.default_acceleration == 0.)
|
||||
return "Invalid zero value for --default-acceleration when using other acceleration settings";
|
||||
//if ((cfg.perimeter_acceleration != 0. || cfg.infill_acceleration != 0. || cfg.bridge_acceleration != 0. || cfg.first_layer_acceleration != 0.) &&
|
||||
// cfg.default_acceleration == 0.)
|
||||
// return "Invalid zero value for --default-acceleration when using other acceleration settings";
|
||||
|
||||
// --spiral-vase
|
||||
if (cfg.spiral_vase) {
|
||||
|
@ -40,36 +40,6 @@ Point ConcaveHull::centroid(const Points &pp)
|
||||
return c;
|
||||
}
|
||||
|
||||
// As it shows, the current offset_ex in ClipperUtils hangs if used in jtRound
|
||||
// mode
|
||||
template<typename PolygonsProvider>
|
||||
static ClipperLib::Paths fast_offset(PolygonsProvider &&paths,
|
||||
coord_t delta,
|
||||
ClipperLib::JoinType jointype)
|
||||
{
|
||||
using ClipperLib::ClipperOffset;
|
||||
using ClipperLib::etClosedPolygon;
|
||||
using ClipperLib::Paths;
|
||||
using ClipperLib::Path;
|
||||
|
||||
ClipperOffset offs;
|
||||
offs.ArcTolerance = scaled<double>(0.01);
|
||||
|
||||
for (auto &p : paths)
|
||||
// If the input is not at least a triangle, we can not do this algorithm
|
||||
if(p.size() < 3) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Invalid geometry for offsetting!";
|
||||
return {};
|
||||
}
|
||||
|
||||
offs.AddPaths(std::forward<PolygonsProvider>(paths), jointype, etClosedPolygon);
|
||||
|
||||
Paths result;
|
||||
offs.Execute(result, static_cast<double>(delta));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Points ConcaveHull::calculate_centroids() const
|
||||
{
|
||||
// We get the centroids of all the islands in the 2D slice
|
||||
@ -158,15 +128,18 @@ ExPolygons ConcaveHull::to_expolygons() const
|
||||
|
||||
ExPolygons offset_waffle_style_ex(const ConcaveHull &hull, coord_t delta)
|
||||
{
|
||||
ExPolygons ret = ClipperPaths_to_Slic3rExPolygons(
|
||||
fast_offset(fast_offset(ClipperUtils::PolygonsProvider(hull.polygons()), 2 * delta, ClipperLib::jtRound), -delta, ClipperLib::jtRound));
|
||||
for (ExPolygon &p : ret) p.holes.clear();
|
||||
return ret;
|
||||
return to_expolygons(offset_waffle_style(hull, delta));
|
||||
}
|
||||
|
||||
Polygons offset_waffle_style(const ConcaveHull &hull, coord_t delta)
|
||||
{
|
||||
return to_polygons(offset_waffle_style_ex(hull, delta));
|
||||
auto arc_tolerance = scaled<double>(0.01);
|
||||
Polygons res = closing(hull.polygons(), 2 * delta, delta, ClipperLib::jtRound, arc_tolerance);
|
||||
|
||||
auto it = std::remove_if(res.begin(), res.end(), [](Polygon &p) { return p.is_clockwise(); });
|
||||
res.erase(it, res.end());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::sla
|
||||
|
@ -1216,7 +1216,7 @@ DynamicConfig SLAPrintStatistics::config() const
|
||||
DynamicConfig SLAPrintStatistics::placeholders()
|
||||
{
|
||||
DynamicConfig config;
|
||||
for (const std::string &key : {
|
||||
for (const char *key : {
|
||||
"print_time", "total_cost", "total_weight",
|
||||
"objects_used_material", "support_used_material" })
|
||||
config.set_key_value(key, new ConfigOptionString(std::string("{") + key + "}"));
|
||||
|
@ -462,85 +462,9 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& trafo, d
|
||||
|
||||
TriangleMesh TriangleMesh::convex_hull_3d() const
|
||||
{
|
||||
// The qhull call:
|
||||
orgQhull::Qhull qhull;
|
||||
qhull.disableOutputStream(); // we want qhull to be quiet
|
||||
std::vector<realT> src_vertices;
|
||||
try
|
||||
{
|
||||
#if REALfloat
|
||||
qhull.runQhull("", 3, (int)this->its.vertices.size(), (const realT*)(this->its.vertices.front().data()), "Qt");
|
||||
#else
|
||||
src_vertices.reserve(this->its.vertices.size() * 3);
|
||||
// We will now fill the vector with input points for computation:
|
||||
for (const stl_vertex &v : this->its.vertices)
|
||||
for (int i = 0; i < 3; ++ i)
|
||||
src_vertices.emplace_back(v(i));
|
||||
qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt");
|
||||
#endif
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cout << "Unable to create convex hull" << std::endl;
|
||||
return TriangleMesh();
|
||||
}
|
||||
|
||||
// Let's collect results:
|
||||
std::vector<Vec3f> dst_vertices;
|
||||
std::vector<Vec3i> dst_facets;
|
||||
// Map of QHull's vertex ID to our own vertex ID (pointing to dst_vertices).
|
||||
std::vector<int> map_dst_vertices;
|
||||
#ifndef NDEBUG
|
||||
Vec3f centroid = Vec3f::Zero();
|
||||
for (const stl_vertex& pt : this->its.vertices)
|
||||
centroid += pt;
|
||||
centroid /= float(this->its.vertices.size());
|
||||
#endif // NDEBUG
|
||||
for (const orgQhull::QhullFacet facet : qhull.facetList()) {
|
||||
// Collect face vertices first, allocate unique vertices in dst_vertices based on QHull's vertex ID.
|
||||
Vec3i indices;
|
||||
int cnt = 0;
|
||||
for (const orgQhull::QhullVertex vertex : facet.vertices()) {
|
||||
int id = vertex.id();
|
||||
assert(id >= 0);
|
||||
if (id >= int(map_dst_vertices.size()))
|
||||
map_dst_vertices.resize(next_highest_power_of_2(size_t(id + 1)), -1);
|
||||
if (int i = map_dst_vertices[id]; i == -1) {
|
||||
// Allocate a new vertex.
|
||||
i = int(dst_vertices.size());
|
||||
map_dst_vertices[id] = i;
|
||||
orgQhull::QhullPoint pt(vertex.point());
|
||||
dst_vertices.emplace_back(pt[0], pt[1], pt[2]);
|
||||
indices[cnt] = i;
|
||||
} else {
|
||||
// Reuse existing vertex.
|
||||
indices[cnt] = i;
|
||||
}
|
||||
if (cnt ++ == 3)
|
||||
break;
|
||||
}
|
||||
assert(cnt == 3);
|
||||
if (cnt == 3) {
|
||||
// QHull sorts vertices of a face lexicographically by their IDs, not by face normals.
|
||||
// Calculate face normal based on the order of vertices.
|
||||
Vec3f n = (dst_vertices[indices(1)] - dst_vertices[indices(0)]).cross(dst_vertices[indices(2)] - dst_vertices[indices(1)]);
|
||||
auto *n2 = facet.getBaseT()->normal;
|
||||
auto d = n.x() * n2[0] + n.y() * n2[1] + n.z() * n2[2];
|
||||
#ifndef NDEBUG
|
||||
Vec3f n3 = (dst_vertices[indices(0)] - centroid);
|
||||
auto d3 = n.dot(n3);
|
||||
assert((d < 0.f) == (d3 < 0.f));
|
||||
#endif // NDEBUG
|
||||
// Get the face normal from QHull.
|
||||
if (d < 0.f)
|
||||
// Fix face orientation.
|
||||
std::swap(indices[1], indices[2]);
|
||||
dst_facets.emplace_back(indices);
|
||||
}
|
||||
}
|
||||
|
||||
TriangleMesh mesh{ std::move(dst_vertices), std::move(dst_facets) };
|
||||
assert(mesh.stats().manifold());
|
||||
TriangleMesh mesh(its_convex_hull(this->its));
|
||||
// Quite often qhull produces non-manifold mesh.
|
||||
// assert(mesh.stats().manifold());
|
||||
return mesh;
|
||||
}
|
||||
|
||||
@ -1108,6 +1032,90 @@ indexed_triangle_set its_make_sphere(double radius, double fa)
|
||||
return mesh;
|
||||
}
|
||||
|
||||
indexed_triangle_set its_convex_hull(const std::vector<Vec3f> &pts)
|
||||
{
|
||||
std::vector<Vec3f> dst_vertices;
|
||||
std::vector<Vec3i> dst_facets;
|
||||
|
||||
if (! pts.empty()) {
|
||||
// The qhull call:
|
||||
orgQhull::Qhull qhull;
|
||||
qhull.disableOutputStream(); // we want qhull to be quiet
|
||||
#if ! REALfloat
|
||||
std::vector<realT> src_vertices;
|
||||
#endif
|
||||
try {
|
||||
#if REALfloat
|
||||
qhull.runQhull("", 3, (int)pts.size(), (const realT*)(pts.front().data()), "Qt");
|
||||
#else
|
||||
src_vertices.reserve(pts.size() * 3);
|
||||
// We will now fill the vector with input points for computation:
|
||||
for (const stl_vertex &v : pts)
|
||||
for (int i = 0; i < 3; ++ i)
|
||||
src_vertices.emplace_back(v(i));
|
||||
qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt");
|
||||
#endif
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << "its_convex_hull: Unable to create convex hull";
|
||||
return {};
|
||||
}
|
||||
|
||||
// Let's collect results:
|
||||
// Map of QHull's vertex ID to our own vertex ID (pointing to dst_vertices).
|
||||
std::vector<int> map_dst_vertices;
|
||||
#ifndef NDEBUG
|
||||
Vec3f centroid = Vec3f::Zero();
|
||||
for (const stl_vertex& pt : pts)
|
||||
centroid += pt;
|
||||
centroid /= float(pts.size());
|
||||
#endif // NDEBUG
|
||||
for (const orgQhull::QhullFacet facet : qhull.facetList()) {
|
||||
// Collect face vertices first, allocate unique vertices in dst_vertices based on QHull's vertex ID.
|
||||
Vec3i indices;
|
||||
int cnt = 0;
|
||||
for (const orgQhull::QhullVertex vertex : facet.vertices()) {
|
||||
int id = vertex.id();
|
||||
assert(id >= 0);
|
||||
if (id >= int(map_dst_vertices.size()))
|
||||
map_dst_vertices.resize(next_highest_power_of_2(size_t(id + 1)), -1);
|
||||
if (int i = map_dst_vertices[id]; i == -1) {
|
||||
// Allocate a new vertex.
|
||||
i = int(dst_vertices.size());
|
||||
map_dst_vertices[id] = i;
|
||||
orgQhull::QhullPoint pt(vertex.point());
|
||||
dst_vertices.emplace_back(pt[0], pt[1], pt[2]);
|
||||
indices[cnt] = i;
|
||||
} else {
|
||||
// Reuse existing vertex.
|
||||
indices[cnt] = i;
|
||||
}
|
||||
if (cnt ++ == 3)
|
||||
break;
|
||||
}
|
||||
assert(cnt == 3);
|
||||
if (cnt == 3) {
|
||||
// QHull sorts vertices of a face lexicographically by their IDs, not by face normals.
|
||||
// Calculate face normal based on the order of vertices.
|
||||
Vec3f n = (dst_vertices[indices(1)] - dst_vertices[indices(0)]).cross(dst_vertices[indices(2)] - dst_vertices[indices(1)]);
|
||||
auto *n2 = facet.getBaseT()->normal;
|
||||
auto d = n.x() * n2[0] + n.y() * n2[1] + n.z() * n2[2];
|
||||
#ifndef NDEBUG
|
||||
Vec3f n3 = (dst_vertices[indices(0)] - centroid);
|
||||
auto d3 = n.dot(n3);
|
||||
assert((d < 0.f) == (d3 < 0.f));
|
||||
#endif // NDEBUG
|
||||
// Get the face normal from QHull.
|
||||
if (d < 0.f)
|
||||
// Fix face orientation.
|
||||
std::swap(indices[1], indices[2]);
|
||||
dst_facets.emplace_back(indices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { std::move(dst_facets), std::move(dst_vertices) };
|
||||
}
|
||||
|
||||
void its_reverse_all_facets(indexed_triangle_set &its)
|
||||
{
|
||||
for (stl_triangle_vertex_indices &face : its.indices)
|
||||
|
@ -301,6 +301,9 @@ indexed_triangle_set its_make_cone(double r, double h, double fa=(2*PI/360));
|
||||
indexed_triangle_set its_make_pyramid(float base, float height);
|
||||
indexed_triangle_set its_make_sphere(double radius, double fa);
|
||||
|
||||
indexed_triangle_set its_convex_hull(const std::vector<Vec3f> &pts);
|
||||
inline indexed_triangle_set its_convex_hull(const indexed_triangle_set &its) { return its_convex_hull(its.vertices); }
|
||||
|
||||
inline TriangleMesh make_cube(double x, double y, double z) { return TriangleMesh(its_make_cube(x, y, z)); }
|
||||
inline TriangleMesh make_prism(float width, float length, float height) { return TriangleMesh(its_make_prism(width, length, height)); }
|
||||
inline TriangleMesh make_cylinder(double r, double h, double fa=(2*PI/360)) { return TriangleMesh{its_make_cylinder(r, h, fa)}; }
|
||||
|
@ -1919,6 +1919,7 @@ void slice_mesh_slabs(
|
||||
#endif // EXPENSIVE_DEBUG_CHECKS
|
||||
|
||||
std::vector<stl_vertex> vertices_transformed = transform_mesh_vertices_for_slicing(mesh, trafo);
|
||||
const bool mirrored = trafo.matrix().determinant() < 0;
|
||||
|
||||
std::vector<FaceOrientation> face_orientation(mesh.indices.size(), FaceOrientation::Up);
|
||||
for (const stl_triangle_vertex_indices &tri : mesh.indices) {
|
||||
@ -1929,7 +1930,7 @@ void slice_mesh_slabs(
|
||||
const Point a = to_2d(fa).cast<coord_t>();
|
||||
const Point b = to_2d(fb).cast<coord_t>();
|
||||
const Point c = to_2d(fc).cast<coord_t>();
|
||||
const int64_t d = cross2((b - a).cast<int64_t>(), (c - b).cast<int64_t>());
|
||||
const int64_t d = cross2((b - a).cast<int64_t>(), (c - b).cast<int64_t>()) * (mirrored ? -1 : 1);
|
||||
FaceOrientation fo = FaceOrientation::Vertical;
|
||||
if (d > 0)
|
||||
fo = FaceOrientation::Up;
|
||||
|
@ -128,13 +128,13 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start,
|
||||
const Vec3f& source, float radius,
|
||||
CursorType cursor_type, EnforcerBlockerType new_state,
|
||||
const Transform3d& trafo, const Transform3d& trafo_no_translate,
|
||||
bool triangle_splitting, float highlight_by_angle_deg)
|
||||
bool triangle_splitting, const ClippingPlane &clp, float highlight_by_angle_deg)
|
||||
{
|
||||
assert(facet_start < m_orig_size_indices);
|
||||
|
||||
// Save current cursor center, squared radius and camera direction, so we don't
|
||||
// have to pass it around.
|
||||
m_cursor = Cursor(hit, source, radius, cursor_type, trafo);
|
||||
m_cursor = Cursor(hit, source, radius, cursor_type, trafo, clp);
|
||||
|
||||
// In case user changed cursor size since last time, update triangle edge limit.
|
||||
// It is necessary to compare the internal radius in m_cursor! radius is in
|
||||
@ -172,15 +172,23 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start,
|
||||
}
|
||||
}
|
||||
|
||||
void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_start,
|
||||
const Transform3d& trafo_no_translate,
|
||||
float seed_fill_angle, float highlight_by_angle_deg,
|
||||
bool TriangleSelector::is_facet_clipped(int facet_idx, const ClippingPlane &clp) const
|
||||
{
|
||||
for (int vert_idx : m_triangles[facet_idx].verts_idxs)
|
||||
if (clp.is_active() && clp.is_mesh_point_clipped(m_vertices[vert_idx].v))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_start, const Transform3d& trafo_no_translate,
|
||||
const ClippingPlane &clp, float seed_fill_angle, float highlight_by_angle_deg,
|
||||
bool force_reselection)
|
||||
{
|
||||
assert(facet_start < m_orig_size_indices);
|
||||
|
||||
// Recompute seed fill only if the cursor is pointing on facet unselected by seed fill.
|
||||
if (int start_facet_idx = select_unsplit_triangle(hit, facet_start); start_facet_idx >= 0 && m_triangles[start_facet_idx].is_selected_by_seed_fill() && !force_reselection)
|
||||
// Recompute seed fill only if the cursor is pointing on facet unselected by seed fill or a clipping plane is active.
|
||||
if (int start_facet_idx = select_unsplit_triangle(hit, facet_start); start_facet_idx >= 0 && m_triangles[start_facet_idx].is_selected_by_seed_fill() && !force_reselection && !clp.is_active())
|
||||
return;
|
||||
|
||||
this->seed_fill_unselect_all_triangles();
|
||||
@ -215,7 +223,7 @@ void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_st
|
||||
// Propagate over the original triangles.
|
||||
for (int neighbor_idx : m_neighbors[current_facet]) {
|
||||
assert(neighbor_idx >= -1);
|
||||
if (neighbor_idx >= 0 && !visited[neighbor_idx]) {
|
||||
if (neighbor_idx >= 0 && !visited[neighbor_idx] && !is_facet_clipped(neighbor_idx, clp)) {
|
||||
// Check if neighbour_facet_idx is satisfies angle in seed_fill_angle and append it to facet_queue if it do.
|
||||
const Vec3f &n1 = m_face_normals[m_triangles[neighbor_idx].source_triangle];
|
||||
const Vec3f &n2 = m_face_normals[m_triangles[current_facet].source_triangle];
|
||||
@ -331,12 +339,12 @@ void TriangleSelector::append_touching_edges(int itriangle, int vertexi, int ver
|
||||
process_subtriangle(touching.second, Partition::Second);
|
||||
}
|
||||
|
||||
void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_start, bool propagate, bool force_reselection)
|
||||
void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_start, const ClippingPlane &clp, bool propagate, bool force_reselection)
|
||||
{
|
||||
int start_facet_idx = select_unsplit_triangle(hit, facet_start);
|
||||
assert(start_facet_idx != -1);
|
||||
// Recompute bucket fill only if the cursor is pointing on facet unselected by bucket fill.
|
||||
if (start_facet_idx == -1 || (m_triangles[start_facet_idx].is_selected_by_seed_fill() && !force_reselection))
|
||||
// Recompute bucket fill only if the cursor is pointing on facet unselected by bucket fill or a clipping plane is active.
|
||||
if (start_facet_idx == -1 || (m_triangles[start_facet_idx].is_selected_by_seed_fill() && !force_reselection && !clp.is_active()))
|
||||
return;
|
||||
|
||||
assert(!m_triangles[start_facet_idx].is_split());
|
||||
@ -379,7 +387,7 @@ void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_
|
||||
|
||||
std::vector<int> touching_triangles = get_all_touching_triangles(current_facet, neighbors[current_facet], neighbors_propagated[current_facet]);
|
||||
for(const int tr_idx : touching_triangles) {
|
||||
if (tr_idx < 0 || visited[tr_idx] || m_triangles[tr_idx].get_state() != start_facet_state)
|
||||
if (tr_idx < 0 || visited[tr_idx] || m_triangles[tr_idx].get_state() != start_facet_state || is_facet_clipped(tr_idx, clp))
|
||||
continue;
|
||||
|
||||
assert(!m_triangles[tr_idx].is_split());
|
||||
@ -1687,11 +1695,12 @@ void TriangleSelector::seed_fill_apply_on_triangles(EnforcerBlockerType new_stat
|
||||
|
||||
TriangleSelector::Cursor::Cursor(
|
||||
const Vec3f& center_, const Vec3f& source_, float radius_world,
|
||||
CursorType type_, const Transform3d& trafo_)
|
||||
CursorType type_, const Transform3d& trafo_, const ClippingPlane &clipping_plane_)
|
||||
: center{center_},
|
||||
source{source_},
|
||||
type{type_},
|
||||
trafo{trafo_.cast<float>()}
|
||||
trafo{trafo_.cast<float>()},
|
||||
clipping_plane(clipping_plane_)
|
||||
{
|
||||
Vec3d sf = Geometry::Transformation(trafo_).get_scaling_factor();
|
||||
if (is_approx(sf(0), sf(1)) && is_approx(sf(1), sf(2))) {
|
||||
@ -1714,22 +1723,19 @@ TriangleSelector::Cursor::Cursor(
|
||||
dir = (center - source).normalized();
|
||||
}
|
||||
|
||||
|
||||
// Is a point (in mesh coords) inside a cursor?
|
||||
bool TriangleSelector::Cursor::is_mesh_point_inside(Vec3f point) const
|
||||
bool TriangleSelector::Cursor::is_mesh_point_inside(const Vec3f &point) const
|
||||
{
|
||||
if (! uniform_scaling)
|
||||
point = trafo * point;
|
||||
const Vec3f transformed_point = uniform_scaling ? point : Vec3f(trafo * point);
|
||||
const Vec3f diff = center - transformed_point;
|
||||
const bool is_point_inside = (type == CIRCLE ? (diff - diff.dot(dir) * dir).squaredNorm() : diff.squaredNorm()) < radius_sqr;
|
||||
|
||||
Vec3f diff = center - point;
|
||||
return (type == CIRCLE ?
|
||||
(diff - diff.dot(dir) * dir).squaredNorm() :
|
||||
diff.squaredNorm())
|
||||
< radius_sqr;
|
||||
if (is_point_inside && clipping_plane.is_active())
|
||||
return !clipping_plane.is_mesh_point_clipped(point);
|
||||
|
||||
return is_point_inside;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// p1, p2, p3 are in mesh coords!
|
||||
bool TriangleSelector::Cursor::is_pointer_in_triangle(const Vec3f& p1_,
|
||||
const Vec3f& p2_,
|
||||
|
@ -4,6 +4,7 @@
|
||||
// #define PRUSASLICER_TRIANGLE_SELECTOR_DEBUG
|
||||
|
||||
|
||||
#include <cfloat>
|
||||
#include "Point.hpp"
|
||||
#include "TriangleMesh.hpp"
|
||||
|
||||
@ -22,6 +23,18 @@ public:
|
||||
POINTER
|
||||
};
|
||||
|
||||
struct ClippingPlane
|
||||
{
|
||||
Vec3f normal;
|
||||
float offset;
|
||||
ClippingPlane() : normal{0.f, 0.f, 1.f}, offset{FLT_MAX} {};
|
||||
explicit ClippingPlane(const std::array<float, 4> &clp) : normal{clp[0], clp[1], clp[2]}, offset{clp[3]} {}
|
||||
|
||||
bool is_active() const { return offset != FLT_MAX; }
|
||||
|
||||
bool is_mesh_point_clipped(const Vec3f &point) const { return normal.dot(point) - offset > 0.f; }
|
||||
};
|
||||
|
||||
std::pair<std::vector<Vec3i>, std::vector<Vec3i>> precompute_all_neighbors() const;
|
||||
void precompute_all_neighbors_recursive(int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector<Vec3i> &neighbors_out, std::vector<Vec3i> &neighbors_normal_out) const;
|
||||
|
||||
@ -47,19 +60,22 @@ public:
|
||||
const Transform3d &trafo, // matrix to get from mesh to world
|
||||
const Transform3d &trafo_no_translate, // matrix to get from mesh to world without translation
|
||||
bool triangle_splitting, // If triangles will be split base on the cursor or not
|
||||
const ClippingPlane &clp, // Clipping plane to limit painting to not clipped facets only
|
||||
float highlight_by_angle_deg = 0.f); // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees.
|
||||
|
||||
void seed_fill_select_triangles(const Vec3f &hit, // point where to start
|
||||
int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
|
||||
const Transform3d &trafo_no_translate, // matrix to get from mesh to world without translation
|
||||
float seed_fill_angle, // the maximal angle between two facets to be painted by the same color
|
||||
float highlight_by_angle_deg = 0.f, // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees.
|
||||
bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle
|
||||
void seed_fill_select_triangles(const Vec3f &hit, // point where to start
|
||||
int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
|
||||
const Transform3d &trafo_no_translate, // matrix to get from mesh to world without translation
|
||||
const ClippingPlane &clp, // Clipping plane to limit painting to not clipped facets only
|
||||
float seed_fill_angle, // the maximal angle between two facets to be painted by the same color
|
||||
float highlight_by_angle_deg = 0.f, // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees.
|
||||
bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle
|
||||
|
||||
void bucket_fill_select_triangles(const Vec3f &hit, // point where to start
|
||||
int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
|
||||
bool propagate, // if bucket fill is propagated to neighbor faces or if it fills the only facet of the modified mesh that the hit point belongs to.
|
||||
bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle
|
||||
void bucket_fill_select_triangles(const Vec3f &hit, // point where to start
|
||||
int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
|
||||
const ClippingPlane &clp, // Clipping plane to limit painting to not clipped facets only
|
||||
bool propagate, // if bucket fill is propagated to neighbor faces or if it fills the only facet of the modified mesh that the hit point belongs to.
|
||||
bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle
|
||||
|
||||
bool has_facets(EnforcerBlockerType state) const;
|
||||
static bool has_facets(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> &data, EnforcerBlockerType test_state);
|
||||
@ -183,8 +199,8 @@ protected:
|
||||
struct Cursor {
|
||||
Cursor() = default;
|
||||
Cursor(const Vec3f& center_, const Vec3f& source_, float radius_world,
|
||||
CursorType type_, const Transform3d& trafo_);
|
||||
bool is_mesh_point_inside(Vec3f pt) const;
|
||||
CursorType type_, const Transform3d& trafo_, const ClippingPlane &clipping_plane_);
|
||||
bool is_mesh_point_inside(const Vec3f &pt) const;
|
||||
bool is_pointer_in_triangle(const Vec3f& p1, const Vec3f& p2, const Vec3f& p3) const;
|
||||
|
||||
Vec3f center;
|
||||
@ -195,6 +211,7 @@ protected:
|
||||
Transform3f trafo;
|
||||
Transform3f trafo_normal;
|
||||
bool uniform_scaling;
|
||||
ClippingPlane clipping_plane;
|
||||
};
|
||||
|
||||
Cursor m_cursor;
|
||||
@ -211,6 +228,7 @@ private:
|
||||
void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant.
|
||||
bool is_pointer_in_triangle(int facet_idx) const;
|
||||
bool is_edge_inside_cursor(int facet_idx) const;
|
||||
bool is_facet_clipped(int facet_idx, const ClippingPlane &clp) const;
|
||||
int push_triangle(int a, int b, int c, int source_triangle, EnforcerBlockerType state = EnforcerBlockerType{0});
|
||||
void perform_split(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType old_state);
|
||||
Vec3i child_neighbors(const Triangle &tr, const Vec3i &neighbors, int child_idx) const;
|
||||
|
@ -271,7 +271,8 @@ bool Bed3D::is_circle(const Pointfs& shape, Vec2d* center, double* radius)
|
||||
|
||||
// Analyze the array of points.
|
||||
// Do they reside on a circle ?
|
||||
const Vec2d box_center = BoundingBoxf(shape).center();
|
||||
const Vec2d box_center = Geometry::circle_center_taubin_newton(shape);
|
||||
|
||||
std::vector<double> vertex_distances;
|
||||
double avg_dist = 0.0;
|
||||
for (const Vec2d& pt : shape) {
|
||||
|
@ -40,12 +40,6 @@
|
||||
|
||||
#include <Eigen/Dense>
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
#include <libqhullcpp/Qhull.h>
|
||||
#include <libqhullcpp/QhullFacetList.h>
|
||||
#include <libqhullcpp/QhullVertexSet.h>
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
#ifdef HAS_GLSAFE
|
||||
void glAssertRecentCallImpl(const char* file_name, unsigned int line, const char* function_name)
|
||||
{
|
||||
@ -626,100 +620,16 @@ void GLVolume::render_sinking_contours()
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
void GLVolume::calc_convex_hull_3d()
|
||||
{
|
||||
if (this->indexed_vertex_array.vertices_and_normals_interleaved.empty())
|
||||
return;
|
||||
|
||||
TriangleMesh mesh;
|
||||
for (size_t i = 0; i < this->indexed_vertex_array.vertices_and_normals_interleaved.size(); i += 6) {
|
||||
const size_t v_id = 3 + i;
|
||||
mesh.its.vertices.push_back({ this->indexed_vertex_array.vertices_and_normals_interleaved[v_id + 0],
|
||||
this->indexed_vertex_array.vertices_and_normals_interleaved[v_id + 1],
|
||||
this->indexed_vertex_array.vertices_and_normals_interleaved[v_id + 2]
|
||||
});
|
||||
const std::vector<float> &src = this->indexed_vertex_array.vertices_and_normals_interleaved;
|
||||
std::vector<Vec3f> pts;
|
||||
assert(src.size() % 6 == 0);
|
||||
pts.reserve(src.size() / 6);
|
||||
for (auto it = src.begin(); it != src.end(); ) {
|
||||
it += 3;
|
||||
pts.push_back({ *it, *(it + 1), *(it + 2) });
|
||||
it += 3;
|
||||
}
|
||||
|
||||
const std::vector<Vec3f>& vertices = mesh.its.vertices;
|
||||
|
||||
// The qhull call:
|
||||
orgQhull::Qhull qhull;
|
||||
qhull.disableOutputStream(); // we want qhull to be quiet
|
||||
std::vector<realT> src_vertices;
|
||||
try
|
||||
{
|
||||
#if REALfloat
|
||||
qhull.runQhull("", 3, (int)vertices.size(), (const realT*)(vertices.front().data()), "Qt");
|
||||
#else
|
||||
src_vertices.reserve(vertices.size() * 3);
|
||||
// We will now fill the vector with input points for computation:
|
||||
for (const stl_vertex& v : vertices)
|
||||
for (int i = 0; i < 3; ++i)
|
||||
src_vertices.emplace_back(v(i));
|
||||
qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt");
|
||||
#endif
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cout << "GLVolume::calc_convex_hull_3d() - Unable to create convex hull" << std::endl;
|
||||
return ;
|
||||
}
|
||||
|
||||
// Let's collect results:
|
||||
std::vector<Vec3f> dst_vertices;
|
||||
std::vector<Vec3i> dst_facets;
|
||||
// Map of QHull's vertex ID to our own vertex ID (pointing to dst_vertices).
|
||||
std::vector<int> map_dst_vertices;
|
||||
#ifndef NDEBUG
|
||||
Vec3f centroid = Vec3f::Zero();
|
||||
for (const auto& pt : vertices)
|
||||
centroid += pt;
|
||||
centroid /= float(vertices.size());
|
||||
#endif // NDEBUG
|
||||
for (const orgQhull::QhullFacet& facet : qhull.facetList()) {
|
||||
// Collect face vertices first, allocate unique vertices in dst_vertices based on QHull's vertex ID.
|
||||
Vec3i indices;
|
||||
int cnt = 0;
|
||||
for (const orgQhull::QhullVertex vertex : facet.vertices()) {
|
||||
const int id = vertex.id();
|
||||
assert(id >= 0);
|
||||
if (id >= int(map_dst_vertices.size()))
|
||||
map_dst_vertices.resize(next_highest_power_of_2(size_t(id + 1)), -1);
|
||||
if (int i = map_dst_vertices[id]; i == -1) {
|
||||
// Allocate a new vertex.
|
||||
i = int(dst_vertices.size());
|
||||
map_dst_vertices[id] = i;
|
||||
orgQhull::QhullPoint pt(vertex.point());
|
||||
dst_vertices.emplace_back(pt[0], pt[1], pt[2]);
|
||||
indices[cnt] = i;
|
||||
}
|
||||
else
|
||||
// Reuse existing vertex.
|
||||
indices[cnt] = i;
|
||||
|
||||
if (cnt++ == 3)
|
||||
break;
|
||||
}
|
||||
assert(cnt == 3);
|
||||
if (cnt == 3) {
|
||||
// QHull sorts vertices of a face lexicographically by their IDs, not by face normals.
|
||||
// Calculate face normal based on the order of vertices.
|
||||
const Vec3f n = (dst_vertices[indices(1)] - dst_vertices[indices(0)]).cross(dst_vertices[indices(2)] - dst_vertices[indices(1)]);
|
||||
auto* n2 = facet.getBaseT()->normal;
|
||||
const auto d = n.x() * n2[0] + n.y() * n2[1] + n.z() * n2[2];
|
||||
#ifndef NDEBUG
|
||||
const Vec3f n3 = (dst_vertices[indices(0)] - centroid);
|
||||
const auto d3 = n.dot(n3);
|
||||
assert((d < 0.f) == (d3 < 0.f));
|
||||
#endif // NDEBUG
|
||||
// Get the face normal from QHull.
|
||||
if (d < 0.f)
|
||||
// Fix face orientation.
|
||||
std::swap(indices[1], indices[2]);
|
||||
dst_facets.emplace_back(indices);
|
||||
}
|
||||
}
|
||||
|
||||
TriangleMesh out_mesh{ std::move(dst_vertices), std::move(dst_facets) };
|
||||
this->set_convex_hull(out_mesh);
|
||||
this->set_convex_hull(TriangleMesh(its_convex_hull(pts)));
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
@ -1070,7 +980,7 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
|
||||
{ unscale<double>(bed_box_2D.max.x()), unscale<double>(bed_box_2D.max.y()), bed_height });
|
||||
|
||||
auto check_against_rectangular_bed = [&print_volume](GLVolume& volume, ModelInstanceEPrintVolumeState& state) {
|
||||
const BoundingBoxf3* const bb = volume.is_sinking() ? &volume.transformed_non_sinking_bounding_box() : &volume.transformed_convex_hull_bounding_box();
|
||||
const BoundingBoxf3* const bb = (volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1) ? &volume.transformed_non_sinking_bounding_box() : &volume.transformed_convex_hull_bounding_box();
|
||||
volume.is_outside = !print_volume.contains(*bb);
|
||||
if (volume.printable) {
|
||||
if (state == ModelInstancePVS_Inside && volume.is_outside)
|
||||
@ -1081,7 +991,8 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
|
||||
};
|
||||
|
||||
auto check_against_circular_bed = [](GLVolume& volume, ModelInstanceEPrintVolumeState& state, const Vec2d& center, double radius) {
|
||||
const TriangleMesh* mesh = volume.is_sinking() ? &GUI::wxGetApp().plater()->model().objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : volume.convex_hull();
|
||||
const TriangleMesh* mesh = (volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1)? &GUI::wxGetApp().plater()->model().objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : volume.convex_hull();
|
||||
//FIXME 2D convex hull is O(n log n), while testing the 2D points against 2D circle is O(n).
|
||||
const Polygon volume_hull_2d = its_convex_hull_2d_above(mesh->its, volume.world_matrix().cast<float>(), 0.0f);
|
||||
size_t outside_count = 0;
|
||||
const double sq_radius = sqr(radius);
|
||||
@ -1100,9 +1011,10 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
|
||||
};
|
||||
|
||||
auto check_against_convex_bed = [&bed_poly, bed_height](GLVolume& volume, ModelInstanceEPrintVolumeState& state) {
|
||||
const TriangleMesh* mesh = volume.is_sinking() ? &GUI::wxGetApp().plater()->model().objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : volume.convex_hull();
|
||||
const TriangleMesh* mesh = (volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1) ? &GUI::wxGetApp().plater()->model().objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : volume.convex_hull();
|
||||
const Polygon volume_hull_2d = its_convex_hull_2d_above(mesh->its, volume.world_matrix().cast<float>(), 0.0f);
|
||||
const BoundingBoxf3* const bb = volume.is_sinking() ? &volume.transformed_non_sinking_bounding_box() : &volume.transformed_convex_hull_bounding_box();
|
||||
const BoundingBoxf3* const bb = (volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1) ? &volume.transformed_non_sinking_bounding_box() : &volume.transformed_convex_hull_bounding_box();
|
||||
// Using rotating callipers to check for collision of two convex polygons.
|
||||
ModelInstanceEPrintVolumeState volume_state = printbed_collision_state(bed_poly, bed_height, volume_hull_2d, bb->min.z(), bb->max.z());
|
||||
bool contained = (volume_state == ModelInstancePVS_Inside);
|
||||
bool intersects = (volume_state == ModelInstancePVS_Partly_Outside);
|
||||
@ -1131,6 +1043,14 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
|
||||
ModelInstanceEPrintVolumeState overall_state = ModelInstancePVS_Inside;
|
||||
bool contained_min_one = false;
|
||||
|
||||
enum class BedShape { Rectangle, Circle, Convex, NonConvex };
|
||||
Vec2d center;
|
||||
double radius;
|
||||
BedShape bed_shape =
|
||||
GUI::Bed3D::is_rectangle(opt->values) ? BedShape::Rectangle :
|
||||
GUI::Bed3D::is_circle(opt->values, ¢er, &radius) ? BedShape::Circle :
|
||||
GUI::Bed3D::is_convex(opt->values) ? BedShape::Convex : BedShape::NonConvex;
|
||||
|
||||
for (GLVolume* volume : this->volumes) {
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (as_toolpaths && !volume->is_extrusion_path)
|
||||
@ -1138,15 +1058,11 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
|
||||
else if (!as_toolpaths && (volume->is_modifier || (!volume->shader_outside_printer_detection_enabled && (volume->is_wipe_tower || volume->composite_id.volume_id < 0))))
|
||||
continue;
|
||||
|
||||
if (GUI::Bed3D::is_rectangle(opt->values))
|
||||
check_against_rectangular_bed(*volume, overall_state);
|
||||
else {
|
||||
Vec2d center;
|
||||
double radius;
|
||||
if (GUI::Bed3D::is_circle(opt->values, ¢er, &radius))
|
||||
check_against_circular_bed(*volume, overall_state, center, radius);
|
||||
else if (GUI::Bed3D::is_convex(opt->values))
|
||||
check_against_convex_bed(*volume, overall_state);
|
||||
switch (bed_shape) {
|
||||
case BedShape::Rectangle: check_against_rectangular_bed(*volume, overall_state); break;
|
||||
case BedShape::Circle: check_against_circular_bed(*volume, overall_state, center, radius); break;
|
||||
case BedShape::Convex: check_against_convex_bed(*volume, overall_state); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
contained_min_one |= !volume->is_outside;
|
||||
|
@ -2080,7 +2080,7 @@ void Control::auto_color_change()
|
||||
}
|
||||
|
||||
int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt();
|
||||
int extruder = 2;
|
||||
// int extruder = 2;
|
||||
|
||||
const Print& print = GUI::wxGetApp().plater()->fff_print();
|
||||
for (auto object : print.objects()) {
|
||||
|
@ -2581,12 +2581,15 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
|
||||
if (camera_space) {
|
||||
Eigen::Matrix<double, 3, 3, Eigen::DontAlign> inv_view_3x3 = wxGetApp().plater()->get_camera().get_view_matrix().inverse().matrix().block(0, 0, 3, 3);
|
||||
displacement = multiplier * (inv_view_3x3 * direction);
|
||||
displacement(2) = 0.0;
|
||||
displacement.z() = 0.0;
|
||||
}
|
||||
else
|
||||
displacement = multiplier * direction;
|
||||
|
||||
m_selection.translate(displacement);
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_selection.stop_dragging();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_dirty = true;
|
||||
}
|
||||
);
|
||||
@ -2685,6 +2688,9 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
|
||||
auto do_rotate = [this](double angle_z_rad) {
|
||||
m_selection.start_dragging();
|
||||
m_selection.rotate(Vec3d(0.0, 0.0, angle_z_rad), TransformationType(TransformationType::World_Relative_Joint));
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_selection.stop_dragging();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_dirty = true;
|
||||
// wxGetApp().obj_manipul()->set_dirty();
|
||||
};
|
||||
|
@ -673,6 +673,7 @@ void GUI_App::post_init()
|
||||
// to popup a modal dialog on start without screwing combo boxes.
|
||||
// This is ugly but I honestly found no better way to do it.
|
||||
// Neither wxShowEvent nor wxWindowCreateEvent work reliably.
|
||||
assert(this->preset_updater); // FIXME Following condition is probably not neccessary.
|
||||
if (this->preset_updater) {
|
||||
this->check_updates(false);
|
||||
CallAfter([this] {
|
||||
@ -720,9 +721,11 @@ GUI_App::~GUI_App()
|
||||
delete preset_updater;
|
||||
}
|
||||
|
||||
std::string GUI_App::get_gl_info(bool format_as_html, bool extensions)
|
||||
// If formatted for github, plaintext with OpenGL extensions enclosed into <details>.
|
||||
// Otherwise HTML formatted for the system info dialog.
|
||||
std::string GUI_App::get_gl_info(bool for_github)
|
||||
{
|
||||
return OpenGLManager::get_gl_info().to_string(format_as_html, extensions);
|
||||
return OpenGLManager::get_gl_info().to_string(for_github);
|
||||
}
|
||||
|
||||
wxGLContext* GUI_App::init_glcontext(wxGLCanvas& canvas)
|
||||
@ -745,7 +748,8 @@ void GUI_App::init_app_config()
|
||||
{
|
||||
// Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release.
|
||||
// SetAppName(SLIC3R_APP_KEY);
|
||||
SetAppName(SLIC3R_APP_KEY "-alpha");
|
||||
// SetAppName(SLIC3R_APP_KEY "-alpha");
|
||||
SetAppName(SLIC3R_APP_KEY "-beta");
|
||||
// SetAppDisplayName(SLIC3R_APP_NAME);
|
||||
|
||||
// Set the Slic3r data directory at the Slic3r XS module.
|
||||
|
@ -172,7 +172,9 @@ public:
|
||||
// Process command line parameters cached in this->init_params,
|
||||
// load configs, STLs etc.
|
||||
void post_init();
|
||||
static std::string get_gl_info(bool format_as_html, bool extensions);
|
||||
// If formatted for github, plaintext with OpenGL extensions enclosed into <details>.
|
||||
// Otherwise HTML formatted for the system info dialog.
|
||||
static std::string get_gl_info(bool for_github);
|
||||
wxGLContext* init_glcontext(wxGLCanvas& canvas);
|
||||
bool init_opengl();
|
||||
|
||||
|
@ -1433,7 +1433,7 @@ void ObjectList::load_subobject(ModelVolumeType type, bool from_galery/* = false
|
||||
selection_changed();
|
||||
}
|
||||
/*
|
||||
void ObjectList::load_part(ModelObject& model_object, std::vector<ModelVolume*>& added_volumes, ModelVolumeType type, bool from_galery/* = false* /)
|
||||
void ObjectList::load_part(ModelObject& model_object, std::vector<ModelVolume*>& added_volumes, ModelVolumeType type, bool from_galery = false)
|
||||
{
|
||||
if (type != ModelVolumeType::MODEL_PART)
|
||||
return;
|
||||
|
@ -820,6 +820,9 @@ void ObjectManipulation::change_position_value(int axis, double value)
|
||||
Selection& selection = canvas->get_selection();
|
||||
selection.start_dragging();
|
||||
selection.translate(position - m_cache.position, selection.requires_local_axes());
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
selection.stop_dragging();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
canvas->do_move(L("Set Position"));
|
||||
|
||||
m_cache.position = position;
|
||||
@ -851,6 +854,9 @@ void ObjectManipulation::change_rotation_value(int axis, double value)
|
||||
selection.rotate(
|
||||
(M_PI / 180.0) * (transformation_type.absolute() ? rotation : rotation - m_cache.rotation),
|
||||
transformation_type);
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
selection.stop_dragging();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
canvas->do_rotate(L("Set Orientation"));
|
||||
|
||||
m_cache.rotation = rotation;
|
||||
@ -923,6 +929,9 @@ void ObjectManipulation::do_scale(int axis, const Vec3d &scale) const
|
||||
|
||||
selection.start_dragging();
|
||||
selection.scale(scaling_factor, transformation_type);
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
selection.stop_dragging();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
wxGetApp().plater()->canvas3D()->do_scale(L("Set Scale"));
|
||||
}
|
||||
|
||||
|
@ -508,20 +508,23 @@ RENDER_AGAIN:
|
||||
m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
|
||||
const float settings_sliders_left =
|
||||
std::max({m_imgui->calc_text_size(m_desc.at("offset")).x,
|
||||
m_imgui->calc_text_size(m_desc.at("quality")).x,
|
||||
m_imgui->calc_text_size(m_desc.at("closing_distance")).x,
|
||||
m_imgui->calc_text_size(m_desc.at("hole_diameter")).x,
|
||||
m_imgui->calc_text_size(m_desc.at("hole_depth")).x})
|
||||
+ m_imgui->scaled(1.f);
|
||||
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x,
|
||||
m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(0.5f);
|
||||
|
||||
const float settings_sliders_left =
|
||||
std::max(std::max({m_imgui->calc_text_size(m_desc.at("offset")).x,
|
||||
m_imgui->calc_text_size(m_desc.at("quality")).x,
|
||||
m_imgui->calc_text_size(m_desc.at("closing_distance")).x,
|
||||
m_imgui->calc_text_size(m_desc.at("hole_diameter")).x,
|
||||
m_imgui->calc_text_size(m_desc.at("hole_depth")).x}) + m_imgui->scaled(0.5f), clipping_slider_left);
|
||||
|
||||
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f);
|
||||
const float diameter_slider_left = settings_sliders_left; //m_imgui->calc_text_size(m_desc.at("hole_diameter")).x + m_imgui->scaled(1.f);
|
||||
const float minimal_slider_width = m_imgui->scaled(4.f);
|
||||
|
||||
const float button_preview_width = m_imgui->calc_button_size(m_desc.at("preview")).x;
|
||||
|
||||
float window_width = minimal_slider_width + std::max({settings_sliders_left, clipping_slider_left, diameter_slider_left});
|
||||
window_width = std::max(window_width, m_imgui->calc_text_size(m_desc.at("preview")).x);
|
||||
window_width = std::max(window_width, button_preview_width);
|
||||
|
||||
if (m_imgui->button(m_desc["preview"]))
|
||||
hollow_mesh();
|
||||
@ -544,9 +547,9 @@ RENDER_AGAIN:
|
||||
float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc.at("offset"));
|
||||
ImGui::SameLine(settings_sliders_left);
|
||||
ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x);
|
||||
ImGui::PushItemWidth(window_width - settings_sliders_left);
|
||||
m_imgui->slider_float(" ", &offset, offset_min, offset_max, "%.1f mm");
|
||||
m_imgui->slider_float("##offset", &offset, offset_min, offset_max, "%.1f mm");
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip((_utf8(opts[0].second->tooltip)).c_str(), max_tooltip_width);
|
||||
|
||||
@ -557,8 +560,8 @@ RENDER_AGAIN:
|
||||
if (current_mode >= quality_mode) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc.at("quality"));
|
||||
ImGui::SameLine(settings_sliders_left);
|
||||
m_imgui->slider_float(" ", &quality, quality_min, quality_max, "%.1f");
|
||||
ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x);
|
||||
m_imgui->slider_float("##quality", &quality, quality_min, quality_max, "%.1f");
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip((_utf8(opts[1].second->tooltip)).c_str(), max_tooltip_width);
|
||||
|
||||
@ -570,8 +573,8 @@ RENDER_AGAIN:
|
||||
if (current_mode >= closing_d_mode) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc.at("closing_distance"));
|
||||
ImGui::SameLine(settings_sliders_left);
|
||||
m_imgui->slider_float(" ", &closing_d, closing_d_min, closing_d_max, "%.1f mm");
|
||||
ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x);
|
||||
m_imgui->slider_float("##closing_distance", &closing_d, closing_d_min, closing_d_max, "%.1f mm");
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip((_utf8(opts[2].second->tooltip)).c_str(), max_tooltip_width);
|
||||
|
||||
@ -614,11 +617,11 @@ RENDER_AGAIN:
|
||||
m_new_hole_radius = diameter_upper_cap / 2.f;
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc.at("hole_diameter"));
|
||||
ImGui::SameLine(diameter_slider_left);
|
||||
ImGui::SameLine(diameter_slider_left, m_imgui->get_item_spacing().x);
|
||||
ImGui::PushItemWidth(window_width - diameter_slider_left);
|
||||
|
||||
float diam = 2.f * m_new_hole_radius;
|
||||
m_imgui->slider_float("", &diam, 1.f, 15.f, "%.1f mm", 1.f, false);
|
||||
m_imgui->slider_float("##hole_diameter", &diam, 1.f, 25.f, "%.1f mm", 1.f, false);
|
||||
// Let's clamp the value (which could have been entered by keyboard) to a larger range
|
||||
// than the slider. This allows entering off-scale values and still protects against
|
||||
//complete non-sense.
|
||||
@ -630,8 +633,8 @@ RENDER_AGAIN:
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc["hole_depth"]);
|
||||
ImGui::SameLine(diameter_slider_left);
|
||||
m_imgui->slider_float(" ", &m_new_hole_height, 0.f, 10.f, "%.1f mm", 1.f, false);
|
||||
ImGui::SameLine(diameter_slider_left, m_imgui->get_item_spacing().x);
|
||||
m_imgui->slider_float("##hole_depth", &m_new_hole_height, 0.f, 10.f, "%.1f mm", 1.f, false);
|
||||
// Same as above:
|
||||
m_new_hole_height = std::clamp(m_new_hole_height, 0.f, 100.f);
|
||||
|
||||
@ -697,10 +700,10 @@ RENDER_AGAIN:
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine(clipping_slider_left);
|
||||
ImGui::PushItemWidth(window_width - clipping_slider_left);
|
||||
ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x);
|
||||
ImGui::PushItemWidth(window_width - settings_sliders_left);
|
||||
float clp_dist = m_c->object_clipper()->get_position();
|
||||
if (m_imgui->slider_float(" ", &clp_dist, 0.f, 1.f, "%.2f"))
|
||||
if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f"))
|
||||
m_c->object_clipper()->set_position(clp_dist, true);
|
||||
|
||||
// make sure supports are shown/hidden as appropriate
|
||||
@ -732,7 +735,7 @@ RENDER_AGAIN:
|
||||
|
||||
if (force_refresh)
|
||||
m_parent.set_as_dirty();
|
||||
|
||||
|
||||
if (config_changed)
|
||||
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE));
|
||||
}
|
||||
|
@ -203,6 +203,7 @@ void GLGizmoMmuSegmentation::render_triangles(const Selection &selection) const
|
||||
glsafe(::glMultMatrixd(trafo_matrix.data()));
|
||||
|
||||
shader->set_uniform("volume_world_matrix", trafo_matrix);
|
||||
shader->set_uniform("volume_mirrored", is_left_handed);
|
||||
m_triangle_selectors[mesh_id]->render(m_imgui);
|
||||
|
||||
glsafe(::glPopMatrix());
|
||||
|
@ -267,7 +267,8 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
||||
const ModelObject *mo = m_c->selection_info()->model_object();
|
||||
const ModelInstance *mi = mo->instances[selection.get_instance_idx()];
|
||||
const Transform3d trafo_matrix_not_translate = mi->get_transformation().get_matrix(true) * mo->volumes[m_rr.mesh_id]->get_matrix(true);
|
||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, m_smart_fill_angle,
|
||||
const Transform3d trafo_matrix = mi->get_transformation().get_matrix() * mo->volumes[m_rr.mesh_id]->get_matrix();
|
||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, this->get_clipping_plane_in_volume_coordinates(trafo_matrix), m_smart_fill_angle,
|
||||
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, true);
|
||||
m_triangle_selectors[m_rr.mesh_id]->request_update_render_data();
|
||||
m_seed_fill_last_mesh_id = m_rr.mesh_id;
|
||||
@ -364,22 +365,22 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
||||
Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast<float>();
|
||||
|
||||
assert(m_rr.mesh_id < int(m_triangle_selectors.size()));
|
||||
const TriangleSelector::ClippingPlane &clp = this->get_clipping_plane_in_volume_coordinates(trafo_matrix);
|
||||
if (m_tool_type == ToolType::SMART_FILL || m_tool_type == ToolType::BUCKET_FILL || (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)) {
|
||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_apply_on_triangles(new_state);
|
||||
if (m_tool_type == ToolType::SMART_FILL)
|
||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, m_smart_fill_angle,
|
||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, clp, m_smart_fill_angle,
|
||||
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, true);
|
||||
else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)
|
||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), false, true);
|
||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, false, true);
|
||||
else if (m_tool_type == ToolType::BUCKET_FILL)
|
||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), true, true);
|
||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, true, true);
|
||||
|
||||
m_seed_fill_last_mesh_id = -1;
|
||||
} else if (m_tool_type == ToolType::BRUSH)
|
||||
m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, int(m_rr.facet), camera_pos, m_cursor_radius, m_cursor_type,
|
||||
new_state, trafo_matrix, trafo_matrix_not_translate, m_triangle_splitting_enabled,
|
||||
new_state, trafo_matrix, trafo_matrix_not_translate, m_triangle_splitting_enabled, clp,
|
||||
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f);
|
||||
|
||||
m_triangle_selectors[m_rr.mesh_id]->request_update_render_data();
|
||||
m_last_mouse_click = mouse_position;
|
||||
}
|
||||
@ -430,16 +431,18 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
||||
if(m_rr.mesh_id != m_seed_fill_last_mesh_id)
|
||||
seed_fill_unselect_all();
|
||||
|
||||
const Transform3d &trafo_matrix = trafo_matrices[m_rr.mesh_id];
|
||||
const Transform3d &trafo_matrix_not_translate = trafo_matrices_not_translate[m_rr.mesh_id];
|
||||
|
||||
assert(m_rr.mesh_id < int(m_triangle_selectors.size()));
|
||||
const TriangleSelector::ClippingPlane &clp = this->get_clipping_plane_in_volume_coordinates(trafo_matrix);
|
||||
if (m_tool_type == ToolType::SMART_FILL)
|
||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, m_smart_fill_angle,
|
||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, clp, m_smart_fill_angle,
|
||||
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f);
|
||||
else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)
|
||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), false);
|
||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, false);
|
||||
else if (m_tool_type == ToolType::BUCKET_FILL)
|
||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), true);
|
||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, true);
|
||||
m_triangle_selectors[m_rr.mesh_id]->request_update_render_data();
|
||||
m_seed_fill_last_mesh_id = m_rr.mesh_id;
|
||||
return true;
|
||||
@ -569,6 +572,25 @@ void GLGizmoPainterBase::on_load(cereal::BinaryInputArchive&)
|
||||
m_schedule_update = true;
|
||||
}
|
||||
|
||||
TriangleSelector::ClippingPlane GLGizmoPainterBase::get_clipping_plane_in_volume_coordinates(const Transform3d &trafo) const {
|
||||
const ::Slic3r::GUI::ClippingPlane *const clipping_plane = m_c->object_clipper()->get_clipping_plane();
|
||||
if (clipping_plane == nullptr || !clipping_plane->is_active())
|
||||
return {};
|
||||
|
||||
const Vec3d clp_normal = clipping_plane->get_normal();
|
||||
const double clp_offset = clipping_plane->get_offset();
|
||||
|
||||
const Transform3d trafo_normal = Transform3d(trafo.linear().transpose());
|
||||
const Transform3d trafo_inv = trafo.inverse();
|
||||
|
||||
Vec3d point_on_plane = clp_normal * clp_offset;
|
||||
Vec3d point_on_plane_transformed = trafo_inv * point_on_plane;
|
||||
Vec3d normal_transformed = trafo_normal * clp_normal;
|
||||
auto offset_transformed = float(point_on_plane_transformed.dot(normal_transformed));
|
||||
|
||||
return TriangleSelector::ClippingPlane({float(normal_transformed.x()), float(normal_transformed.y()), float(normal_transformed.z()), offset_transformed});
|
||||
}
|
||||
|
||||
std::array<float, 4> TriangleSelectorGUI::get_seed_fill_color(const std::array<float, 4> &base_color)
|
||||
{
|
||||
return {base_color[0] * 0.75f, base_color[1] * 0.75f, base_color[2] * 0.75f, 1.f};
|
||||
|
@ -184,6 +184,8 @@ protected:
|
||||
|
||||
ClippingPlaneDataWrapper get_clipping_plane_data() const;
|
||||
|
||||
TriangleSelector::ClippingPlane get_clipping_plane_in_volume_coordinates(const Transform3d &trafo) const;
|
||||
|
||||
private:
|
||||
bool is_mesh_point_clipped(const Vec3d& point, const Transform3d& trafo) const;
|
||||
void update_raycast_cache(const Vec2d& mouse_position,
|
||||
|
@ -24,6 +24,7 @@ GLGizmoSimplify::GLGizmoSimplify(GLCanvas3D & parent,
|
||||
, m_obj_index(0)
|
||||
, m_need_reload(false)
|
||||
, m_show_wireframe(false)
|
||||
, m_move_to_center(false)
|
||||
// translation for GUI size
|
||||
, tr_mesh_name(_u8L("Mesh name"))
|
||||
, tr_triangles(_u8L("Triangles"))
|
||||
@ -50,6 +51,62 @@ bool GLGizmoSimplify::on_esc_key_down() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// while opening needs GLGizmoSimplify to set window position
|
||||
void GLGizmoSimplify::add_simplify_suggestion_notification(
|
||||
const std::vector<size_t> &object_ids,
|
||||
const ModelObjectPtrs & objects,
|
||||
NotificationManager & manager)
|
||||
{
|
||||
std::vector<size_t> big_ids;
|
||||
big_ids.reserve(object_ids.size());
|
||||
auto is_big_object = [&objects](size_t object_id) {
|
||||
const uint32_t triangles_to_suggest_simplify = 1000000;
|
||||
if (object_id >= objects.size()) return false; // out of object index
|
||||
ModelVolumePtrs &volumes = objects[object_id]->volumes;
|
||||
if (volumes.size() != 1) return false; // not only one volume
|
||||
size_t triangle_count = volumes.front()->mesh().its.indices.size();
|
||||
if (triangle_count < triangles_to_suggest_simplify)
|
||||
return false; // small volume
|
||||
return true;
|
||||
};
|
||||
std::copy_if(object_ids.begin(), object_ids.end(),
|
||||
std::back_inserter(big_ids), is_big_object);
|
||||
if (big_ids.empty()) return;
|
||||
|
||||
for (size_t object_id : big_ids) {
|
||||
std::string t = _u8L(
|
||||
"Processing model '@object_name' with more than 1M triangles "
|
||||
"could be slow. It is highly recommend to reduce "
|
||||
"amount of triangles.");
|
||||
t.replace(t.find("@object_name"), sizeof("@object_name") - 1,
|
||||
objects[object_id]->name);
|
||||
// std::stringstream text;
|
||||
// text << t << "\n";
|
||||
std::string hypertext = _u8L("Simplify model");
|
||||
|
||||
std::function<bool(wxEvtHandler *)> open_simplify =
|
||||
[object_id](wxEvtHandler *) {
|
||||
auto plater = wxGetApp().plater();
|
||||
if (object_id >= plater->model().objects.size()) return true;
|
||||
|
||||
Selection &selection = plater->canvas3D()->get_selection();
|
||||
selection.clear();
|
||||
selection.add_object((unsigned int) object_id);
|
||||
|
||||
auto &manager = plater->canvas3D()->get_gizmos_manager();
|
||||
bool close_notification = true;
|
||||
if(!manager.open_gizmo(GLGizmosManager::Simplify))
|
||||
return close_notification;
|
||||
GLGizmoSimplify* simplify = dynamic_cast<GLGizmoSimplify*>(manager.get_current());
|
||||
if (simplify == nullptr) return close_notification;
|
||||
simplify->set_center_position();
|
||||
return close_notification;
|
||||
};
|
||||
manager.push_simplify_suggestion_notification(
|
||||
t, objects[object_id]->id(), hypertext, open_simplify);
|
||||
}
|
||||
}
|
||||
|
||||
std::string GLGizmoSimplify::on_get_name() const
|
||||
{
|
||||
return _u8L("Simplify");
|
||||
@ -91,8 +148,16 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
||||
m_is_valid_result = false;
|
||||
m_exist_preview = false;
|
||||
init_wireframe();
|
||||
|
||||
if (change_window_position) {
|
||||
live_preview();
|
||||
|
||||
// set window position
|
||||
if (m_move_to_center && change_window_position) {
|
||||
m_move_to_center = false;
|
||||
auto parent_size = m_parent.get_canvas_size();
|
||||
ImVec2 pos(parent_size.get_width() / 2 - m_gui_cfg->window_offset_x,
|
||||
parent_size.get_height() / 2 - m_gui_cfg->window_offset_y);
|
||||
ImGui::SetNextWindowPos(pos, ImGuiCond_Always);
|
||||
}else if (change_window_position) {
|
||||
ImVec2 pos = ImGui::GetMousePos();
|
||||
pos.x -= m_gui_cfg->window_offset_x;
|
||||
pos.y -= m_gui_cfg->window_offset_y;
|
||||
@ -202,7 +267,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::SameLine(m_gui_cfg->bottom_left_width);
|
||||
ImGui::Text(_L("%d triangles").c_str(), m_configuration.wanted_count);
|
||||
ImGui::Text(_u8L("%d triangles").c_str(), m_configuration.wanted_count);
|
||||
m_imgui->disabled_end(); // use_count
|
||||
|
||||
if (ImGui::Checkbox(_u8L("Show wireframe").c_str(), &m_show_wireframe)) {
|
||||
@ -212,7 +277,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
||||
|
||||
bool is_canceling = m_state == State::canceling;
|
||||
m_imgui->disabled_begin(is_canceling);
|
||||
if (m_imgui->button(_L("Cancel"))) {
|
||||
if (m_imgui->button(_u8L("Cancel"))) {
|
||||
if (m_state == State::settings) {
|
||||
if (m_original_its.has_value()) {
|
||||
set_its(*m_original_its);
|
||||
@ -231,7 +296,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
||||
|
||||
bool is_processing = m_state != State::settings;
|
||||
m_imgui->disabled_begin(is_processing);
|
||||
if (m_imgui->button(_L("Apply"))) {
|
||||
if (m_imgui->button(_u8L("Apply"))) {
|
||||
if (!m_is_valid_result) {
|
||||
m_state = State::close_on_end;
|
||||
process();
|
||||
@ -294,6 +359,7 @@ void GLGizmoSimplify::live_preview() {
|
||||
// wait until cancel
|
||||
if (m_worker.joinable()) {
|
||||
m_state = State::canceling;
|
||||
m_dealy_process_cv.notify_one();
|
||||
m_worker.join();
|
||||
}
|
||||
}
|
||||
@ -304,20 +370,52 @@ void GLGizmoSimplify::live_preview() {
|
||||
|
||||
void GLGizmoSimplify::process()
|
||||
{
|
||||
class SimplifyCanceledException : public std::exception {
|
||||
public:
|
||||
const char* what() const throw() { return L("Model simplification has been canceled"); }
|
||||
};
|
||||
if (m_volume == nullptr) return;
|
||||
if (m_volume->mesh().its.indices.empty()) return;
|
||||
size_t count_triangles = m_volume->mesh().its.indices.size();
|
||||
// Is neccessary simplification
|
||||
if ((m_configuration.use_count && m_configuration.wanted_count >= count_triangles) ||
|
||||
(!m_configuration.use_count && m_configuration.max_error <= 0.f)) {
|
||||
|
||||
// Exist different original volume?
|
||||
if (m_original_its.has_value() &&
|
||||
m_original_its->indices.size() != count_triangles) {
|
||||
indexed_triangle_set its = *m_original_its; // copy
|
||||
set_its(its);
|
||||
}
|
||||
m_is_valid_result = true;
|
||||
|
||||
if (!m_original_its.has_value())
|
||||
// re-render bargraph
|
||||
set_dirty();
|
||||
m_parent.schedule_extra_frame(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// when not store original volume store it for cancelation
|
||||
if (!m_original_its.has_value()) {
|
||||
m_original_its = m_volume->mesh().its; // copy
|
||||
|
||||
auto plater = wxGetApp().plater();
|
||||
plater->take_snapshot(_L("Simplify ") + m_volume->name);
|
||||
plater->clear_before_change_mesh(m_obj_index);
|
||||
// store previous state
|
||||
auto plater = wxGetApp().plater();
|
||||
plater->take_snapshot(_u8L("Simplify ") + m_volume->name);
|
||||
plater->clear_before_change_mesh(m_obj_index);
|
||||
}
|
||||
|
||||
m_progress = 0;
|
||||
if (m_worker.joinable()) m_worker.join();
|
||||
m_worker = std::thread([this]() {
|
||||
|
||||
m_worker = std::thread([this]() {
|
||||
{// delay before process
|
||||
std::unique_lock<std::mutex> lk(m_state_mutex);
|
||||
auto is_modify = [this]() { return m_state == State::canceling; };
|
||||
if (m_dealy_process_cv.wait_for(lk, m_gui_cfg->prcess_delay, is_modify)) {
|
||||
// exist modification
|
||||
m_state = State::settings;
|
||||
request_rerender();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// store original triangles
|
||||
uint32_t triangle_count = (m_configuration.use_count) ? m_configuration.wanted_count : 0;
|
||||
float max_error = (!m_configuration.use_count) ? m_configuration.max_error : std::numeric_limits<float>::max();
|
||||
@ -357,6 +455,7 @@ void GLGizmoSimplify::process()
|
||||
}
|
||||
|
||||
void GLGizmoSimplify::set_its(indexed_triangle_set &its) {
|
||||
if (m_volume == nullptr) return; // could appear after process
|
||||
m_volume->set_mesh(its);
|
||||
m_volume->calculate_convex_hull();
|
||||
m_volume->set_new_unique_id();
|
||||
@ -440,6 +539,10 @@ void GLGizmoSimplify::request_rerender() {
|
||||
});
|
||||
}
|
||||
|
||||
void GLGizmoSimplify::set_center_position() {
|
||||
m_move_to_center = true;
|
||||
}
|
||||
|
||||
bool GLGizmoSimplify::exist_volume(ModelVolume *volume) {
|
||||
auto objs = wxGetApp().plater()->model().objects;
|
||||
for (const auto &obj : objs) {
|
||||
|
@ -7,17 +7,22 @@
|
||||
#include "GLGizmoPainterBase.hpp" // for render wireframe
|
||||
#include "admesh/stl.h" // indexed_triangle_set
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
#include <atomic>
|
||||
|
||||
#include <GL/glew.h> // GLUint
|
||||
|
||||
namespace Slic3r {
|
||||
// for simplify suggestion
|
||||
class ModelObjectPtrs; // std::vector<ModelObject*>
|
||||
|
||||
namespace Slic3r {
|
||||
class ModelVolume;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
class NotificationManager; // for simplify suggestion
|
||||
|
||||
class GLGizmoSimplify: public GLGizmoBase, public GLGizmoTransparentRender // GLGizmoBase
|
||||
{
|
||||
@ -25,6 +30,10 @@ public:
|
||||
GLGizmoSimplify(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
|
||||
virtual ~GLGizmoSimplify();
|
||||
bool on_esc_key_down();
|
||||
static void add_simplify_suggestion_notification(
|
||||
const std::vector<size_t> &object_ids,
|
||||
const ModelObjectPtrs & objects,
|
||||
NotificationManager & manager);
|
||||
protected:
|
||||
virtual std::string on_get_name() const override;
|
||||
virtual void on_render_input_window(float x, float y, float bottom_limit) override;
|
||||
@ -47,6 +56,8 @@ private:
|
||||
void set_its(indexed_triangle_set &its);
|
||||
void create_gui_cfg();
|
||||
void request_rerender();
|
||||
|
||||
void set_center_position();
|
||||
// move to global functions
|
||||
static ModelVolume *get_volume(const Selection &selection, Model &model);
|
||||
static const ModelVolume *get_volume(const GLVolume::CompositeID &cid, const Model &model);
|
||||
@ -57,15 +68,21 @@ private:
|
||||
std::atomic_bool m_is_valid_result; // differ what to do in apply
|
||||
std::atomic_bool m_exist_preview; // set when process end
|
||||
|
||||
bool m_move_to_center; // opening gizmo
|
||||
|
||||
volatile int m_progress; // percent of done work
|
||||
ModelVolume *m_volume; // keep pointer to actual working volume
|
||||
size_t m_obj_index;
|
||||
|
||||
std::optional<indexed_triangle_set> m_original_its;
|
||||
bool m_show_wireframe;
|
||||
bool m_show_wireframe;
|
||||
|
||||
volatile bool m_need_reload; // after simplify, glReload must be on main thread
|
||||
|
||||
std::thread m_worker;
|
||||
// wait before process
|
||||
std::mutex m_state_mutex;
|
||||
std::condition_variable m_dealy_process_cv;
|
||||
|
||||
enum class State {
|
||||
settings,
|
||||
@ -87,8 +104,13 @@ private:
|
||||
|
||||
void fix_count_by_ratio(size_t triangle_count)
|
||||
{
|
||||
wanted_count = static_cast<uint32_t>(
|
||||
std::round(triangle_count * (100.f-decimate_ratio) / 100.f));
|
||||
if (decimate_ratio <= 0.f)
|
||||
wanted_count = static_cast<uint32_t>(triangle_count);
|
||||
else if (decimate_ratio >= 100.f)
|
||||
wanted_count = 0;
|
||||
else
|
||||
wanted_count = static_cast<uint32_t>(std::round(
|
||||
triangle_count * (100.f - decimate_ratio) / 100.f));
|
||||
}
|
||||
} m_configuration;
|
||||
|
||||
@ -106,6 +128,10 @@ private:
|
||||
|
||||
// trunc model name when longer
|
||||
size_t max_char_in_name = 30;
|
||||
|
||||
// to prevent freezing when move in gui
|
||||
// delay before process in [ms]
|
||||
std::chrono::duration<long int, std::milli> prcess_delay = std::chrono::milliseconds(250);
|
||||
};
|
||||
std::optional<GuiCfg> m_gui_cfg;
|
||||
|
||||
@ -122,6 +148,16 @@ private:
|
||||
void free_gpu();
|
||||
GLuint m_wireframe_VBO_id, m_wireframe_IBO_id;
|
||||
size_t m_wireframe_IBO_size;
|
||||
|
||||
// cancel exception
|
||||
class SimplifyCanceledException: public std::exception
|
||||
{
|
||||
public:
|
||||
const char *what() const throw()
|
||||
{
|
||||
return L("Model simplification has been canceled");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
|
@ -673,7 +673,7 @@ RENDER_AGAIN:
|
||||
// - keep updating the head radius during sliding so it is continuosly refreshed in 3D scene
|
||||
// - take correct undo/redo snapshot after the user is done with moving the slider
|
||||
float initial_value = m_new_point_head_diameter;
|
||||
m_imgui->slider_float("", &m_new_point_head_diameter, 0.1f, diameter_upper_cap, "%.1f");
|
||||
m_imgui->slider_float("##head_diameter", &m_new_point_head_diameter, 0.1f, diameter_upper_cap, "%.1f");
|
||||
if (ImGui::IsItemClicked()) {
|
||||
if (m_old_point_head_diameter == 0.f)
|
||||
m_old_point_head_diameter = initial_value;
|
||||
@ -733,7 +733,7 @@ RENDER_AGAIN:
|
||||
float density = static_cast<const ConfigOptionInt*>(opts[0])->value;
|
||||
float minimal_point_distance = static_cast<const ConfigOptionFloat*>(opts[1])->value;
|
||||
|
||||
m_imgui->slider_float("", &minimal_point_distance, 0.f, 20.f, "%.f mm");
|
||||
m_imgui->slider_float("##minimal_point_distance", &minimal_point_distance, 0.f, 20.f, "%.f mm");
|
||||
bool slider_clicked = ImGui::IsItemClicked(); // someone clicked the slider
|
||||
bool slider_edited = ImGui::IsItemEdited(); // someone is dragging the slider
|
||||
bool slider_released = ImGui::IsItemDeactivatedAfterEdit(); // someone has just released the slider
|
||||
@ -742,7 +742,7 @@ RENDER_AGAIN:
|
||||
m_imgui->text(m_desc.at("points_density"));
|
||||
ImGui::SameLine(settings_sliders_left);
|
||||
|
||||
m_imgui->slider_float(" ", &density, 0.f, 200.f, "%.f %%");
|
||||
m_imgui->slider_float("##points_density", &density, 0.f, 200.f, "%.f %%");
|
||||
slider_clicked |= ImGui::IsItemClicked();
|
||||
slider_edited |= ImGui::IsItemEdited();
|
||||
slider_released |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
@ -802,7 +802,7 @@ RENDER_AGAIN:
|
||||
ImGui::SameLine(clipping_slider_left);
|
||||
ImGui::PushItemWidth(window_width - clipping_slider_left);
|
||||
float clp_dist = m_c->object_clipper()->get_position();
|
||||
if (m_imgui->slider_float(" ", &clp_dist, 0.f, 1.f, "%.2f"))
|
||||
if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f"))
|
||||
m_c->object_clipper()->set_position(clp_dist, true);
|
||||
|
||||
|
||||
|
@ -715,12 +715,22 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
|
||||
}
|
||||
else if (evt.LeftUp() && m_current == Flatten && m_gizmos[m_current]->get_hover_id() != -1) {
|
||||
// to avoid to loose the selection when user clicks an the white faces of a different object while the Flatten gizmo is active
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
selection.stop_dragging();
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
processed = true;
|
||||
}
|
||||
else if (evt.RightUp() && (m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) && !m_parent.is_mouse_dragging()) {
|
||||
gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down);
|
||||
processed = true;
|
||||
}
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
else if (evt.LeftUp()) {
|
||||
selection.stop_dragging();
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
}
|
||||
else {
|
||||
// mouse inside toolbar
|
||||
|
@ -280,7 +280,7 @@ void ImGuiWrapper::render()
|
||||
m_new_frame_open = false;
|
||||
}
|
||||
|
||||
ImVec2 ImGuiWrapper::calc_text_size(const wxString &text, float wrap_width)
|
||||
ImVec2 ImGuiWrapper::calc_text_size(const wxString &text, float wrap_width) const
|
||||
{
|
||||
auto text_utf8 = into_u8(text);
|
||||
ImVec2 size = ImGui::CalcTextSize(text_utf8.c_str(), NULL, false, wrap_width);
|
||||
@ -293,6 +293,22 @@ ImVec2 ImGuiWrapper::calc_text_size(const wxString &text, float wrap_width)
|
||||
return size;
|
||||
}
|
||||
|
||||
ImVec2 ImGuiWrapper::calc_button_size(const wxString &text, const ImVec2 &button_size) const
|
||||
{
|
||||
const ImVec2 text_size = this->calc_text_size(text);
|
||||
const ImGuiContext &g = *GImGui;
|
||||
const ImGuiStyle &style = g.Style;
|
||||
|
||||
return ImGui::CalcItemSize(button_size, text_size.x + style.FramePadding.x * 2.0f, text_size.y + style.FramePadding.y * 2.0f);
|
||||
}
|
||||
|
||||
ImVec2 ImGuiWrapper::get_item_spacing() const
|
||||
{
|
||||
const ImGuiContext &g = *GImGui;
|
||||
const ImGuiStyle &style = g.Style;
|
||||
return style.ItemSpacing;
|
||||
}
|
||||
|
||||
float ImGuiWrapper::get_slider_float_height() const
|
||||
{
|
||||
const ImGuiContext& g = *GImGui;
|
||||
|
@ -56,9 +56,11 @@ public:
|
||||
|
||||
float scaled(float x) const { return x * m_font_size; }
|
||||
ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); }
|
||||
ImVec2 calc_text_size(const wxString &text, float wrap_width = -1.0f);
|
||||
ImVec2 calc_text_size(const wxString &text, float wrap_width = -1.0f) const;
|
||||
ImVec2 calc_button_size(const wxString &text, const ImVec2 &button_size = ImVec2(0, 0)) const;
|
||||
|
||||
float get_slider_float_height() const;
|
||||
ImVec2 get_item_spacing() const;
|
||||
float get_slider_float_height() const;
|
||||
|
||||
void set_next_window_pos(float x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f);
|
||||
void set_next_window_bg_alpha(float alpha);
|
||||
|
@ -193,7 +193,7 @@ void KBShortcutsDialog::fill_shortcuts()
|
||||
m_full_shortcuts.push_back({ { _L("Gizmos"), _L("The following shortcuts are applicable when the specified gizmo is active") }, gizmos_shortcuts });
|
||||
|
||||
Shortcuts object_list_shortcuts = {
|
||||
{ "P", L("Set selected items as Ptrintable/Unprintable") },
|
||||
{ "P", L("Set selected items as Printable/Unprintable") },
|
||||
{ "0", L("Set default extruder for the selected items") },
|
||||
{ "1-9", L("Set extruder number for the selected items") },
|
||||
};
|
||||
|
@ -53,6 +53,7 @@ public:
|
||||
m_data[2] = norm_dir.z();
|
||||
}
|
||||
void set_offset(double offset) { m_data[3] = offset; }
|
||||
double get_offset() const { return m_data[3]; }
|
||||
Vec3d get_normal() const { return Vec3d(m_data[0], m_data[1], m_data[2]); }
|
||||
bool is_active() const { return m_data[3] != DBL_MAX; }
|
||||
static ClippingPlane ClipsNothing() { return ClippingPlane(Vec3d(0., 0., 1.), DBL_MAX); }
|
||||
|
@ -218,47 +218,12 @@ InfoDialog::InfoDialog(wxWindow* parent, const wxString &title, const wxString&
|
||||
: MsgDialog(parent, wxString::Format(_L("%s information"), SLIC3R_APP_NAME), title)
|
||||
, msg(msg)
|
||||
{
|
||||
this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
|
||||
// Text shown as HTML, so that mouse selection and Ctrl-V to copy will work.
|
||||
wxHtmlWindow* html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO);
|
||||
{
|
||||
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
wxFont monospace = wxGetApp().code_font();
|
||||
wxColour text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue());
|
||||
auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue());
|
||||
const int font_size = font.GetPointSize() - 1;
|
||||
int size[] = { font_size, font_size, font_size, font_size, font_size, font_size, font_size };
|
||||
html->SetFonts(font.GetFaceName(), monospace.GetFaceName(), size);
|
||||
html->SetBorders(2);
|
||||
|
||||
// calculate html page size from text
|
||||
int lines = msg.Freq('\n');
|
||||
|
||||
if (msg.Contains("<tr>")) {
|
||||
int pos = 0;
|
||||
while (pos < (int)msg.Len() && pos != wxNOT_FOUND) {
|
||||
pos = msg.find("<tr>", pos + 1);
|
||||
lines+=2;
|
||||
}
|
||||
}
|
||||
int page_height = std::min((font.GetPixelSize().y + 1) * lines, 68 * wxGetApp().em_unit());
|
||||
wxSize page_size(68 * wxGetApp().em_unit(), page_height);
|
||||
|
||||
html->SetMinSize(page_size);
|
||||
|
||||
std::string msg_escaped = xml_escape(msg.ToUTF8().data(), true);
|
||||
boost::replace_all(msg_escaped, "\r\n", "<br>");
|
||||
boost::replace_all(msg_escaped, "\n", "<br>");
|
||||
html->SetPage("<html><body bgcolor=\"" + bgr_clr_str + "\"><font color=\"" + text_clr_str + "\">" + wxString::FromUTF8(msg_escaped.data()) + "</font></body></html>");
|
||||
content_sizer->Add(html, 1, wxEXPAND);
|
||||
}
|
||||
|
||||
add_msg_content(this, content_sizer, msg);
|
||||
// Set info bitmap
|
||||
logo->SetBitmap(create_scaled_bitmap("info", this, 84));
|
||||
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
|
||||
Fit();
|
||||
}
|
||||
|
||||
|
@ -1509,7 +1509,7 @@ void NotificationManager::push_notification(NotificationType type,
|
||||
std::function<bool(wxEvtHandler*)> callback,
|
||||
int timestamp)
|
||||
{
|
||||
int duration = get_standart_duration(level);
|
||||
int duration = get_standard_duration(level);
|
||||
push_notification_data({ type, level, duration, text, hypertext, callback }, timestamp);
|
||||
}
|
||||
|
||||
|
@ -713,7 +713,7 @@ private:
|
||||
void sort_notifications();
|
||||
// If there is some error notification active, then the "Export G-code" notification after the slicing is finished is suppressed.
|
||||
bool has_slicing_error_notification();
|
||||
size_t get_standart_duration(NotificationLevel level)
|
||||
size_t get_standard_duration(NotificationLevel level)
|
||||
{
|
||||
switch (level) {
|
||||
|
||||
|
@ -157,13 +157,16 @@ bool OpenGLManager::GLInfo::is_glsl_version_greater_or_equal_to(unsigned int maj
|
||||
return version_greater_or_equal_to(m_glsl_version, major, minor);
|
||||
}
|
||||
|
||||
std::string OpenGLManager::GLInfo::to_string(bool format_as_html, bool extensions) const
|
||||
// If formatted for github, plaintext with OpenGL extensions enclosed into <details>.
|
||||
// Otherwise HTML formatted for the system info dialog.
|
||||
std::string OpenGLManager::GLInfo::to_string(bool for_github) const
|
||||
{
|
||||
if (!m_detected)
|
||||
detect();
|
||||
|
||||
std::stringstream out;
|
||||
|
||||
const bool format_as_html = ! for_github;
|
||||
std::string h2_start = format_as_html ? "<b>" : "";
|
||||
std::string h2_end = format_as_html ? "</b>" : "";
|
||||
std::string b_start = format_as_html ? "<b>" : "";
|
||||
@ -176,18 +179,24 @@ std::string OpenGLManager::GLInfo::to_string(bool format_as_html, bool extension
|
||||
out << b_start << "Renderer: " << b_end << m_renderer << line_end;
|
||||
out << b_start << "GLSL version: " << b_end << m_glsl_version << line_end;
|
||||
|
||||
if (extensions) {
|
||||
{
|
||||
std::vector<std::string> extensions_list;
|
||||
std::string extensions_str = gl_get_string_safe(GL_EXTENSIONS, "");
|
||||
boost::split(extensions_list, extensions_str, boost::is_any_of(" "), boost::token_compress_off);
|
||||
boost::split(extensions_list, extensions_str, boost::is_any_of(" "), boost::token_compress_on);
|
||||
|
||||
if (!extensions_list.empty()) {
|
||||
out << h2_start << "Installed extensions:" << h2_end << line_end;
|
||||
if (for_github)
|
||||
out << "<details>\n<summary>Installed extensions:</summary>\n";
|
||||
else
|
||||
out << h2_start << "Installed extensions:" << h2_end << line_end;
|
||||
|
||||
std::sort(extensions_list.begin(), extensions_list.end());
|
||||
for (const std::string& ext : extensions_list) {
|
||||
out << ext << line_end;
|
||||
}
|
||||
for (const std::string& ext : extensions_list)
|
||||
if (! ext.empty())
|
||||
out << ext << line_end;
|
||||
|
||||
if (for_github)
|
||||
out << "</details>\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,9 @@ public:
|
||||
bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const;
|
||||
bool is_glsl_version_greater_or_equal_to(unsigned int major, unsigned int minor) const;
|
||||
|
||||
std::string to_string(bool format_as_html, bool extensions) const;
|
||||
// If formatted for github, plaintext with OpenGL extensions enclosed into <details>.
|
||||
// Otherwise HTML formatted for the system info dialog.
|
||||
std::string to_string(bool for_github) const;
|
||||
|
||||
private:
|
||||
void detect() const;
|
||||
|
@ -201,7 +201,7 @@ void OptionsGroup::activate_line(Line& line)
|
||||
if (line.is_separator())
|
||||
return;
|
||||
|
||||
m_use_custom_ctrl_as_parent = false;
|
||||
m_use_custom_ctrl_as_parent = false;
|
||||
|
||||
if (line.full_width && (
|
||||
line.widget != nullptr ||
|
||||
|
@ -89,6 +89,7 @@
|
||||
#include "PresetComboBoxes.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "ProjectDirtyStateManager.hpp"
|
||||
#include "Gizmos/GLGizmoSimplify.hpp" // create suggestion notification
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "Gizmos/GLGizmosManager.hpp"
|
||||
@ -204,7 +205,7 @@ void ObjectInfo::msw_rescale()
|
||||
|
||||
void ObjectInfo::update_warning_icon(const std::string& warning_icon_name)
|
||||
{
|
||||
if (showing_manifold_warning_icon = !warning_icon_name.empty()) {
|
||||
if ((showing_manifold_warning_icon = !warning_icon_name.empty())) {
|
||||
m_warning_icon_name = warning_icon_name;
|
||||
manifold_warning_icon->SetBitmap(create_scaled_bitmap(m_warning_icon_name));
|
||||
}
|
||||
@ -1786,7 +1787,6 @@ struct Plater::priv
|
||||
#endif // ENABLE_RELOAD_FROM_DISK_REPLACE_FILE
|
||||
void replace_with_stl();
|
||||
void reload_all_from_disk();
|
||||
void create_simplify_notification(const std::vector<size_t>& obj_ids);
|
||||
void set_current_panel(wxPanel* panel);
|
||||
|
||||
void on_select_preset(wxCommandEvent&);
|
||||
@ -2571,8 +2571,9 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
// this is required because the selected object changed and the flatten on face an sla support gizmos need to be updated accordingly
|
||||
view3D->get_canvas3d()->update_gizmos_on_off_state();
|
||||
}
|
||||
|
||||
create_simplify_notification(obj_idxs);
|
||||
|
||||
GLGizmoSimplify::add_simplify_suggestion_notification(
|
||||
obj_idxs, model.objects, *notification_manager);
|
||||
|
||||
return obj_idxs;
|
||||
}
|
||||
@ -3017,9 +3018,9 @@ void Plater::priv::update_print_volume_state()
|
||||
{
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(this->config->option("bed_shape"));
|
||||
const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast<float>(scale_(BedEpsilon))).front();
|
||||
const Polygon bed_poly_convex = offset(Geometry::convex_hull(Polygon::new_scale(opt->values).points), static_cast<float>(scale_(BedEpsilon))).front();
|
||||
const float bed_height = this->config->opt_float("max_print_height");
|
||||
this->q->model().update_print_volume_state(bed_poly, bed_height);
|
||||
this->q->model().update_print_volume_state(bed_poly_convex, bed_height);
|
||||
#else
|
||||
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(this->config->opt<ConfigOptionPoints>("bed_shape")->values));
|
||||
BoundingBoxf3 print_volume(unscale(bed_box_2D.min(0), bed_box_2D.min(1), 0.0), unscale(bed_box_2D.max(0), bed_box_2D.max(1), scale_(this->config->opt_float("max_print_height"))));
|
||||
@ -3762,53 +3763,6 @@ void Plater::priv::reload_all_from_disk()
|
||||
}
|
||||
}
|
||||
|
||||
void Plater::priv::create_simplify_notification(const std::vector<size_t>& obj_ids) {
|
||||
const uint32_t triangles_to_suggest_simplify = 1000000;
|
||||
|
||||
std::vector<size_t> big_ids;
|
||||
big_ids.reserve(obj_ids.size());
|
||||
std::copy_if(obj_ids.begin(), obj_ids.end(), std::back_inserter(big_ids),
|
||||
[this, triangles_to_suggest_simplify](size_t object_id) {
|
||||
if (object_id >= model.objects.size()) return false; // out of object index
|
||||
ModelVolumePtrs& volumes = model.objects[object_id]->volumes;
|
||||
if (volumes.size() != 1) return false; // not only one volume
|
||||
size_t triangle_count = volumes.front()->mesh().its.indices.size();
|
||||
if (triangle_count < triangles_to_suggest_simplify) return false; // small volume
|
||||
return true;
|
||||
});
|
||||
|
||||
if (big_ids.empty()) return;
|
||||
|
||||
for (size_t object_id : big_ids) {
|
||||
std::string t = _u8L(
|
||||
"Processing model '@object_name' with more than 1M triangles "
|
||||
"could be slow. It is highly recommend to reduce "
|
||||
"amount of triangles.");
|
||||
t.replace(t.find("@object_name"), sizeof("@object_name") - 1,
|
||||
model.objects[object_id]->name);
|
||||
//std::stringstream text;
|
||||
//text << t << "\n";
|
||||
std::string hypertext = _u8L("Simplify model");
|
||||
|
||||
std::function<bool(wxEvtHandler *)> open_simplify = [object_id](wxEvtHandler *) {
|
||||
auto plater = wxGetApp().plater();
|
||||
if (object_id >= plater->model().objects.size()) return true;
|
||||
|
||||
Selection &selection = plater->canvas3D()->get_selection();
|
||||
selection.clear();
|
||||
selection.add_object((unsigned int) object_id);
|
||||
|
||||
auto &manager = plater->canvas3D()->get_gizmos_manager();
|
||||
manager.open_gizmo(GLGizmosManager::EType::Simplify);
|
||||
return true;
|
||||
};
|
||||
notification_manager->push_simplify_suggestion_notification(t,
|
||||
model.objects[object_id]->id(),
|
||||
hypertext,
|
||||
open_simplify);
|
||||
}
|
||||
}
|
||||
|
||||
void Plater::priv::set_current_panel(wxPanel* panel)
|
||||
{
|
||||
if (std::find(panels.begin(), panels.end(), panel) == panels.end())
|
||||
@ -4231,8 +4185,11 @@ void Plater::priv::on_right_click(RBtnEvent& evt)
|
||||
wxMenu* menu = nullptr;
|
||||
|
||||
if (obj_idx == -1) { // no one or several object are selected
|
||||
if (evt.data.second) // right button was clicked on empty space
|
||||
if (evt.data.second) { // right button was clicked on empty space
|
||||
if (!get_selection().is_empty()) // several objects are selected in 3DScene
|
||||
return;
|
||||
menu = menus.default_menu();
|
||||
}
|
||||
else
|
||||
menu = menus.multi_selection_menu();
|
||||
}
|
||||
|
@ -34,11 +34,17 @@ std::string PresetHints::cooling_description(const Preset &preset)
|
||||
"so that no less than %3%s are spent on that layer "
|
||||
"(however, speed will never be reduced below %4%mm/s)."),
|
||||
slowdown_below_layer_time, max_fan_speed, slowdown_below_layer_time, min_print_speed);
|
||||
if (fan_below_layer_time > slowdown_below_layer_time)
|
||||
out += "\n" +
|
||||
GUI::format(_L("If estimated layer time is greater, but still below ~%1%s, "
|
||||
if (fan_below_layer_time > slowdown_below_layer_time) {
|
||||
out += "\n";
|
||||
if (min_fan_speed != max_fan_speed)
|
||||
out += GUI::format(_L("If estimated layer time is greater, but still below ~%1%s, "
|
||||
"fan will run at a proportionally decreasing speed between %2%%% and %3%%%."),
|
||||
fan_below_layer_time, max_fan_speed, min_fan_speed);
|
||||
else
|
||||
out += GUI::format(_L("If estimated layer time is greater, but still below ~%1%s, "
|
||||
"fan will run at %2%%%"),
|
||||
fan_below_layer_time, min_fan_speed);
|
||||
}
|
||||
out += "\n";
|
||||
}
|
||||
if (preset.config.opt_bool("fan_always_on", 0)) {
|
||||
|
@ -67,8 +67,9 @@ public:
|
||||
SendSystemInfoDialog(wxWindow* parent);
|
||||
|
||||
private:
|
||||
bool send_info();
|
||||
wxString send_info();
|
||||
const std::string m_system_info_json;
|
||||
wxButton* m_btn_show_data;
|
||||
wxButton* m_btn_send;
|
||||
wxButton* m_btn_dont_send;
|
||||
wxButton* m_btn_ask_later;
|
||||
@ -89,7 +90,7 @@ public:
|
||||
auto* btn = new wxButton(this, wxID_CANCEL, _L("Cancel"));
|
||||
auto* vsizer = new wxBoxSizer(wxVERTICAL);
|
||||
auto *top_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
vsizer->Add(text, 1, wxEXPAND|wxALIGN_CENTER_HORIZONTAL);
|
||||
vsizer->Add(text, 1, wxEXPAND);
|
||||
vsizer->AddSpacer(5);
|
||||
vsizer->Add(btn, 0, wxALIGN_CENTER_HORIZONTAL);
|
||||
top_sizer->Add(vsizer, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT | wxBOTTOM, 10);
|
||||
@ -548,7 +549,6 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent)
|
||||
"installation are sent. PrusaSlicer is open source, if you want to "
|
||||
"inspect the code actually performing the communication, see %1%."),
|
||||
std::string("<i>") + filename + "</i>");
|
||||
wxString label3 = _L("Show verbatim data that will be sent");
|
||||
|
||||
auto* html_window = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxSize(70*em, 34*em), wxHW_SCROLLBAR_NEVER);
|
||||
wxString html = GUI::format_wxstr(
|
||||
@ -560,17 +560,14 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent)
|
||||
+ text1 + "<br /><br />"
|
||||
"</td></tr></table>"
|
||||
+ "<b>" + label2 + "</b><br />"
|
||||
+ text2 + "<br /><br />"
|
||||
+ "<b><a href=\"show\">" + label3 + "</a></b><br />"
|
||||
+ text2
|
||||
+ "</font></body></html>", bgr_clr_str, text_clr_str);
|
||||
html_window->SetPage(html);
|
||||
html_window->Bind(wxEVT_HTML_LINK_CLICKED, [this](wxHtmlLinkEvent&) {
|
||||
ShowJsonDialog dlg(this, m_system_info_json, GetSize().Scale(0.9, 0.7));
|
||||
dlg.ShowModal();
|
||||
});
|
||||
|
||||
vsizer->Add(html_window, 1, wxEXPAND);
|
||||
|
||||
m_btn_show_data = new wxButton(this, wxID_ANY, _L("Show verbatim data that will be sent"));
|
||||
|
||||
m_btn_ask_later = new wxButton(this, wxID_ANY, _L("Ask me next time"));
|
||||
m_btn_dont_send = new wxButton(this, wxID_ANY, _L("Do not send anything"));
|
||||
m_btn_send = new wxButton(this, wxID_ANY, _L("Send system info"));
|
||||
@ -582,6 +579,7 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent)
|
||||
hsizer->AddSpacer(em);
|
||||
hsizer->Add(m_btn_send);
|
||||
|
||||
vsizer->Add(m_btn_show_data, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 20);
|
||||
vsizer->Add(hsizer, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 10);
|
||||
topSizer->Add(vsizer, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 10);
|
||||
|
||||
@ -599,9 +597,15 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent)
|
||||
|
||||
CenterOnParent();
|
||||
|
||||
m_btn_show_data->Bind(wxEVT_BUTTON, [this](wxEvent&) {
|
||||
ShowJsonDialog dlg(this, m_system_info_json, GetSize().Scale(0.9, 0.7));
|
||||
dlg.ShowModal();
|
||||
});
|
||||
|
||||
m_btn_send->Bind(wxEVT_BUTTON, [this](const wxEvent&)
|
||||
{
|
||||
if (send_info()) {
|
||||
if (wxString out = send_info(); !out.IsEmpty()) {
|
||||
InfoDialog(nullptr, wxEmptyString, out).ShowModal();
|
||||
save_version();
|
||||
EndModal(0);
|
||||
}
|
||||
@ -630,7 +634,7 @@ void SendSystemInfoDialog::on_dpi_changed(const wxRect&)
|
||||
|
||||
|
||||
// This actually sends the info.
|
||||
bool SendSystemInfoDialog::send_info()
|
||||
wxString SendSystemInfoDialog::send_info()
|
||||
{
|
||||
std::atomic<int> job_done = false; // Flag to communicate between threads.
|
||||
struct Result {
|
||||
@ -674,11 +678,9 @@ bool SendSystemInfoDialog::send_info()
|
||||
job_done = true; // In case the user closed the dialog, let the other thread know
|
||||
sending_thread.join(); // and wait until it terminates.
|
||||
|
||||
if (result.value != Result::Cancelled) { // user knows he cancelled, no need to tell him.
|
||||
InfoDialog info_dlg(wxGetApp().mainframe, wxEmptyString, result.str);
|
||||
info_dlg.ShowModal();
|
||||
}
|
||||
return result.value == Result::Success;
|
||||
if (result.value == Result::Cancelled)
|
||||
return "";
|
||||
return result.str;
|
||||
}
|
||||
|
||||
|
||||
|
@ -158,7 +158,7 @@ SysInfoDialog::SysInfoDialog()
|
||||
"</body>"
|
||||
"</html>", bgr_clr_str, text_clr_str, text_clr_str,
|
||||
blacklisted_libraries_message,
|
||||
get_mem_info(true), wxGetApp().get_gl_info(true, true),
|
||||
get_mem_info(true), wxGetApp().get_gl_info(false),
|
||||
"<b>" + _L("Eigen vectorization supported:") + "</b> " + Eigen::SimdInstructionSetsInUse());
|
||||
|
||||
m_opengl_info_html->SetPage(text);
|
||||
@ -215,7 +215,7 @@ void SysInfoDialog::on_dpi_changed(const wxRect &suggested_rect)
|
||||
void SysInfoDialog::onCopyToClipboard(wxEvent &)
|
||||
{
|
||||
wxTheClipboard->Open();
|
||||
const auto text = get_main_info(false) + "\n" + wxGetApp().get_gl_info(false, true);
|
||||
const auto text = get_main_info(false) + "\n" + wxGetApp().get_gl_info(true);
|
||||
wxTheClipboard->SetData(new wxTextDataObject(text));
|
||||
wxTheClipboard->Close();
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ class AppConfig;
|
||||
class PresetBundle;
|
||||
class Semver;
|
||||
|
||||
const int SLIC3R_VERSION_BODY_MAX = 256;
|
||||
static constexpr const int SLIC3R_VERSION_BODY_MAX = 256;
|
||||
|
||||
class PresetUpdater
|
||||
{
|
||||
|
@ -220,7 +220,7 @@ SCENARIO("DynamicPrintConfig serialization", "[Config]") {
|
||||
cereal::BinaryOutputArchive oarchive(ss);
|
||||
oarchive(cfg);
|
||||
serialized = ss.str();
|
||||
} catch (std::runtime_error e) {
|
||||
} catch (std::runtime_error &e) {
|
||||
e.what();
|
||||
}
|
||||
|
||||
@ -230,7 +230,7 @@ SCENARIO("DynamicPrintConfig serialization", "[Config]") {
|
||||
std::stringstream ss(serialized);
|
||||
cereal::BinaryInputArchive iarchive(ss);
|
||||
iarchive(cfg2);
|
||||
} catch (std::runtime_error e) {
|
||||
} catch (std::runtime_error &e) {
|
||||
e.what();
|
||||
}
|
||||
REQUIRE(cfg == cfg2);
|
||||
|
@ -19,6 +19,66 @@
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
TEST_CASE("Line::parallel_to", "[Geometry]"){
|
||||
Line l{ { 100000, 0 }, { 0, 0 } };
|
||||
Line l2{ { 200000, 0 }, { 0, 0 } };
|
||||
REQUIRE(l.parallel_to(l));
|
||||
REQUIRE(l.parallel_to(l2));
|
||||
|
||||
Line l3(l2);
|
||||
l3.rotate(0.9 * EPSILON, { 0, 0 });
|
||||
REQUIRE(l.parallel_to(l3));
|
||||
|
||||
Line l4(l2);
|
||||
l4.rotate(1.1 * EPSILON, { 0, 0 });
|
||||
REQUIRE(! l.parallel_to(l4));
|
||||
|
||||
// The angle epsilon is so low that vectors shorter than 100um rotated by epsilon radians are not rotated at all.
|
||||
Line l5{ { 20000, 0 }, { 0, 0 } };
|
||||
l5.rotate(1.1 * EPSILON, { 0, 0 });
|
||||
REQUIRE(l.parallel_to(l5));
|
||||
|
||||
l.rotate(1., { 0, 0 });
|
||||
Point offset{ 342876, 97636249 };
|
||||
l.translate(offset);
|
||||
l3.rotate(1., { 0, 0 });
|
||||
l3.translate(offset);
|
||||
l4.rotate(1., { 0, 0 });
|
||||
l4.translate(offset);
|
||||
REQUIRE(l.parallel_to(l3));
|
||||
REQUIRE(!l.parallel_to(l4));
|
||||
}
|
||||
|
||||
TEST_CASE("Line::perpendicular_to", "[Geometry]") {
|
||||
Line l{ { 100000, 0 }, { 0, 0 } };
|
||||
Line l2{ { 0, 200000 }, { 0, 0 } };
|
||||
REQUIRE(! l.perpendicular_to(l));
|
||||
REQUIRE(l.perpendicular_to(l2));
|
||||
|
||||
Line l3(l2);
|
||||
l3.rotate(0.9 * EPSILON, { 0, 0 });
|
||||
REQUIRE(l.perpendicular_to(l3));
|
||||
|
||||
Line l4(l2);
|
||||
l4.rotate(1.1 * EPSILON, { 0, 0 });
|
||||
REQUIRE(! l.perpendicular_to(l4));
|
||||
|
||||
// The angle epsilon is so low that vectors shorter than 100um rotated by epsilon radians are not rotated at all.
|
||||
Line l5{ { 0, 20000 }, { 0, 0 } };
|
||||
l5.rotate(1.1 * EPSILON, { 0, 0 });
|
||||
REQUIRE(l.perpendicular_to(l5));
|
||||
|
||||
l.rotate(1., { 0, 0 });
|
||||
Point offset{ 342876, 97636249 };
|
||||
l.translate(offset);
|
||||
l3.rotate(1., { 0, 0 });
|
||||
l3.translate(offset);
|
||||
l4.rotate(1., { 0, 0 });
|
||||
l4.translate(offset);
|
||||
REQUIRE(l.perpendicular_to(l3));
|
||||
REQUIRE(! l.perpendicular_to(l4));
|
||||
}
|
||||
|
||||
TEST_CASE("Polygon::contains works properly", "[Geometry]"){
|
||||
// this test was failing on Windows (GH #1950)
|
||||
Slic3r::Polygon polygon(std::vector<Point>({
|
||||
@ -468,7 +528,7 @@ TEST_CASE("Convex polygon intersection on two disjoint squares", "[Geometry][Rot
|
||||
Polygon B = A;
|
||||
B.translate(20 / SCALING_FACTOR, 0);
|
||||
|
||||
bool is_inters = Geometry::intersects(A, B);
|
||||
bool is_inters = Geometry::convex_polygons_intersect(A, B);
|
||||
|
||||
REQUIRE(is_inters == false);
|
||||
}
|
||||
@ -480,7 +540,7 @@ TEST_CASE("Convex polygon intersection on two intersecting squares", "[Geometry]
|
||||
Polygon B = A;
|
||||
B.translate(5 / SCALING_FACTOR, 5 / SCALING_FACTOR);
|
||||
|
||||
bool is_inters = Geometry::intersects(A, B);
|
||||
bool is_inters = Geometry::convex_polygons_intersect(A, B);
|
||||
|
||||
REQUIRE(is_inters == true);
|
||||
}
|
||||
@ -492,7 +552,7 @@ TEST_CASE("Convex polygon intersection on two squares touching one edge", "[Geom
|
||||
Polygon B = A;
|
||||
B.translate(10 / SCALING_FACTOR, 0);
|
||||
|
||||
bool is_inters = Geometry::intersects(A, B);
|
||||
bool is_inters = Geometry::convex_polygons_intersect(A, B);
|
||||
|
||||
REQUIRE(is_inters == false);
|
||||
}
|
||||
@ -509,7 +569,7 @@ TEST_CASE("Convex polygon intersection on two squares touching one vertex", "[Ge
|
||||
svg.draw(B, "green");
|
||||
svg.Close();
|
||||
|
||||
bool is_inters = Geometry::intersects(A, B);
|
||||
bool is_inters = Geometry::convex_polygons_intersect(A, B);
|
||||
|
||||
REQUIRE(is_inters == false);
|
||||
}
|
||||
@ -520,7 +580,7 @@ TEST_CASE("Convex polygon intersection on two overlapping squares", "[Geometry][
|
||||
|
||||
Polygon B = A;
|
||||
|
||||
bool is_inters = Geometry::intersects(A, B);
|
||||
bool is_inters = Geometry::convex_polygons_intersect(A, B);
|
||||
|
||||
REQUIRE(is_inters == true);
|
||||
}
|
||||
@ -560,7 +620,7 @@ TEST_CASE("Convex polygon intersection on two overlapping squares", "[Geometry][
|
||||
|
||||
// bench.start();
|
||||
// for (const auto &test : tests)
|
||||
// results.emplace_back(Geometry::intersects(test.first, test.second));
|
||||
// results.emplace_back(Geometry::convex_polygons_intersect(test.first, test.second));
|
||||
// bench.stop();
|
||||
|
||||
// std::cout << "Test time: " << bench.getElapsedSec() << std::endl;
|
||||
@ -611,7 +671,7 @@ TEST_CASE("Convex polygon intersection test prusa polygons", "[Geometry][Rotcali
|
||||
for (size_t i = 0; i < PRINTER_PART_POLYGONS.size(); ++i) {
|
||||
Polygon P = PRINTER_PART_POLYGONS[i];
|
||||
P = Geometry::convex_hull(P.points);
|
||||
bool res = Geometry::intersects(P, P);
|
||||
bool res = Geometry::convex_polygons_intersect(P, P);
|
||||
if (!res) {
|
||||
SVG svg{std::string("fail_self") + std::to_string(i) + ".svg"};
|
||||
svg.draw(P, "green");
|
||||
@ -644,7 +704,7 @@ TEST_CASE("Convex polygon intersection test prusa polygons", "[Geometry][Rotcali
|
||||
|
||||
B.translate(bba.size() + bbb.size());
|
||||
|
||||
bool res = Geometry::intersects(A, B);
|
||||
bool res = Geometry::convex_polygons_intersect(A, B);
|
||||
bool ref = !intersection(A, B).empty();
|
||||
|
||||
if (res != ref) {
|
||||
@ -669,7 +729,7 @@ TEST_CASE("Convex polygon intersection test prusa polygons", "[Geometry][Rotcali
|
||||
A.translate(-bba.center());
|
||||
B.translate(-bbb.center());
|
||||
|
||||
bool res = Geometry::intersects(A, B);
|
||||
bool res = Geometry::convex_polygons_intersect(A, B);
|
||||
bool ref = !intersection(A, B).empty();
|
||||
|
||||
if (res != ref) {
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
set(SLIC3R_APP_NAME "PrusaSlicer")
|
||||
set(SLIC3R_APP_KEY "PrusaSlicer")
|
||||
set(SLIC3R_VERSION "2.4.0-alpha3")
|
||||
set(SLIC3R_VERSION "2.4.0-beta1")
|
||||
set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN")
|
||||
set(SLIC3R_RC_VERSION "2,4,0,0")
|
||||
set(SLIC3R_RC_VERSION_DOTS "2.4.0.0")
|
||||
|
Loading…
Reference in New Issue
Block a user