diff options
179 files changed, 5810 insertions, 2663 deletions
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index df77f81..4999274 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -6,6 +6,8 @@ labels: enhancement assignees: sn4k3 --- +Also look at: https://github.com/sn4k3/UVtools/discussions/categories/ideas + <span style="color:red">**! Before continue, please look/search on closed topics if your case was already been discussed, if yes and related, comment there instead.**</span> ## Is your feature request related to a problem? Please describe diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 8c574bd..9c1fc42 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -6,7 +6,7 @@ labels: question assignees: sn4k3 --- -<span style="color:red">**! Before continue, please look/search on closed topics if your case was already been discussed, if yes and related, comment there instead.**</span> - -## Describe the question -A clear and concise description of your question.
\ No newline at end of file +## READ THIS +Do not use issues to post questions! Use discussions instead!: +https://github.com/sn4k3/UVtools/discussions/categories/q-a +## READ THIS
\ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/script.md b/.github/ISSUE_TEMPLATE/script.md index 3975cd6..73965f7 100644 --- a/.github/ISSUE_TEMPLATE/script.md +++ b/.github/ISSUE_TEMPLATE/script.md @@ -6,10 +6,7 @@ labels: script assignees: sn4k3 --- -## Describe what your script does -A clear and concise description of your script, and all it steps. - - -```Powershell -#Paste your code here or attach the .ps1 file -```
\ No newline at end of file +## READ THIS +Do not use issues to post scripts! Use discussions instead!: +https://github.com/sn4k3/UVtools/discussions/categories/scripts +## READ THIS
\ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 66ef747..42d7f3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,42 @@ # Changelog +## 06/02/2021 - v2.4.0 + +* (Upgrade) EmguCV/OpenCV to v4.5.1 +* (Upgrade) AvaloniaUI to 1.0 +* (Improvement) GUI re-touched +* (Improvement) Make pixel editor tab to disappear when pixel editor is disabled +* (Improvement) Simplify the output filename from PrusaSlicer profiles +* (Improvement) All operations require a slicer file at constructor rather than on execute, this allow exposure the open file to the operation before run it +* (Improvement) Calibrations: Auto set "Mirror Output" if open file have MirrorDisplay set +* (Change) Tool - Redraw model/supports icon +* (Change) photon and cbddlp to use version 3 by default +* (Add) Tool - Dynamic layer height: Analyze and optimize the model with dynamic layer heights, larger angles will slice at lower layer height + while more straight angles will slice larger layer height. (#131) +* (Add) Calibration - Exposure time finder: Generates test models with various strategies and increments to verify the best exposure time for a given layer height +* (Add) File load checks, trigger error when a file have critical errors and attempt to fix non-critical errors + * Layers must have an valid image, otherwise trigger an error + * Layers must have a incremental or equal position Z than it previous, otherwise trigger an error + * If layer 0 starts at 0mm it will auto fix all layers, it will add Layer Height to the current z at every layer +* (Add) Tool - Edit print parameters: Allow set parameters to each x layers and skip n layers inside the given range. + This allow the use of optimizations in a layer pattern, for example, to set 3s for a layer but 2.5s for the next. +* (Add) Layer height property to "Layer Data" table: Shows layer height for the slice +* (Fix) When automations applied and file is saved, it will not warn user about file overwrite for the first time save +* (Fix) Tool - Redraw model/supports: Disable apply button when no file selected +* (Fix) Tool - Infill: Lack of equality member to test if same infill profile already exists +* (Fix) Auto converted files from SL1 where clipping filename at first dot (.), now it only strips known extensions +* (Fix) SL1 encoded files wasn't generating the right information and lead to printer crash +* (Fix) PrusaSlicer printer "Anycubic Photon S" LiftSpeed was missing and contains a typo (#135) +* (Fix) PrusaSlicer profile manager wasnt marking missing profiles to be installed (#135) +* (Fix) PrusaSlicer folder search on linux to also look at %HOME%/.config/PrusaSlicer (#135, #136) +* (Fix) Operations were revised and some bug fixed, most about can't cancel the progress +* (Fix) Some typos on tooltips +* (Fix) Prevent PhotonS from enconding, it will trigger error now as this format is read-only +* **(Fix) Ctrl + Shift + Z to redo the last operation:** + * The layer range is reseted instead of pull the used values + * Tool - Arithmetic always disabled + * Action - Layer import didn't generate info and always disabled + ## 22/01/2021 - v2.3.2 * (Add) Settings - Automations: Change only light-off delay if value is zero (Enabled by default) diff --git a/CreateRelease.WPF.ps1 b/CreateRelease.WPF.ps1 index fc0a1d4..607d0f4 100644 --- a/CreateRelease.WPF.ps1 +++ b/CreateRelease.WPF.ps1 @@ -30,6 +30,8 @@ class FixedEncoder : System.Text.UTF8Encoding { ### Configuration ### #################################### $enableMSI = $true +$buildOnly = $null +#$buildOnly = ""#"win-x64" # Profilling $stopWatch = New-Object -TypeName System.Diagnostics.Stopwatch $deployStopWatch = New-Object -TypeName System.Diagnostics.Stopwatch @@ -83,22 +85,22 @@ $runtimes = @{ "win-x64" = @{ "extraCmd" = "-p:PublishReadyToRun=true" - "exclude" = @("libcvextern.so", "libcvextern.dylib", "UVtools.sh") + "exclude" = @("UVtools.sh") "include" = @() } "linux-x64" = @{ "extraCmd" = "-p:PublishReadyToRun=true" - "exclude" = @("x86", "x64", "libcvextern.dylib") + "exclude" = @() "include" = @("libcvextern.so") } "arch-x64" = @{ "extraCmd" = "-p:PublishReadyToRun=true" - "exclude" = @("x86", "x64", "libcvextern.dylib", "libcvextern.so") + "exclude" = @() "include" = @("libcvextern.so") } "rhel-x64" = @{ "extraCmd" = "-p:PublishReadyToRun=true" - "exclude" = @("x86", "x64", "libcvextern.dylib") + "exclude" = @() "include" = @("libcvextern.so") } #"unix-x64" = @{ @@ -107,12 +109,13 @@ $runtimes = #} "osx-x64" = @{ "extraCmd" = "-p:PublishReadyToRun=true" - "exclude" = @("x86", "x64", "libcvextern.so") + "exclude" = @() "include" = @("libcvextern.dylib") } } foreach ($obj in $runtimes.GetEnumerator()) { + if(![string]::IsNullOrWhiteSpace($buildOnly) -and !$buildOnly.Equals($obj.Name)) {continue} # Configuration $deployStopWatch.Restart() $runtime = $obj.Name; # runtime name diff --git a/PrusaSlicer/printer/AnyCubic Photon S.ini b/PrusaSlicer/printer/AnyCubic Photon S.ini index c598345..6af11a5 100644 --- a/PrusaSlicer/printer/AnyCubic Photon S.ini +++ b/PrusaSlicer/printer/AnyCubic Photon S.ini @@ -26,7 +26,7 @@ min_exposure_time = 1 min_initial_exposure_time = 1 print_host = printer_model = SL1 -printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_ANYCUBIC\nPRINTER_MODEL_PHOTON_S\nFILEFORMAT_PWS\n\nSTART_CUSTOM_VALUES\nLightOffDelay_1\nLiftHeight_6\nLiftingSpeed_60\nRetractSpeed_150\nEND_CUSTOM_VALUES +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_ANYCUBIC\nPRINTER_MODEL_PHOTON_S\nFILEFORMAT_PWS\n\nSTART_CUSTOM_VALUES\nLightOffDelay_1\nLiftHeight_6\nLiftSpeed_60\nRetractSpeed_150\nEND_CUSTOM_VALUES printer_settings_id = printer_technology = SLA printer_variant = default diff --git a/PrusaSlicer/printer/QIDI I-Box Mono.ini b/PrusaSlicer/printer/QIDI I-Box Mono.ini index 9075d01..bd20e34 100644 --- a/PrusaSlicer/printer/QIDI I-Box Mono.ini +++ b/PrusaSlicer/printer/QIDI I-Box Mono.ini @@ -1,4 +1,4 @@ -# generated by PrusaSlicer 2.3.0+win64 on 2021-01-13 at 02:30:16 UTC +# generated by PrusaSlicer 2.3.0+win64 on 2021-02-06 at 03:35:53 UTC absolute_correction = 0 area_fill = 50 bed_custom_model = @@ -26,7 +26,7 @@ min_exposure_time = 1 min_initial_exposure_time = 1 print_host = printer_model = SL1 -printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_QIDI\nPRINTER_MODEL_I-BOX_MONO\nFILEFORMAT_CTB\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_6\nLiftHeight_6\nBottomLiftSpeed_65\nLiftSpeed_65\nRetractSpeed_100\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_QIDI\nPRINTER_MODEL_I-BOX_MONO\nFILEFORMAT_CTB\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_7\nLiftHeight_7\nBottomLiftSpeed_80\nLiftSpeed_80\nRetractSpeed_120\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES printer_settings_id = printer_technology = SLA printer_variant = default diff --git a/PrusaSlicer/sla_print/Universal 0.01 - Heavy Supports.ini b/PrusaSlicer/sla_print/Universal 0.01 - Heavy Supports.ini index 54a964b..330e5b4 100644 --- a/PrusaSlicer/sla_print/Universal 0.01 - Heavy Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.01 - Heavy Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.025 UltraDetail layer_height = 0.01 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.01 - Light Supports.ini b/PrusaSlicer/sla_print/Universal 0.01 - Light Supports.ini index 60e45f2..639cc29 100644 --- a/PrusaSlicer/sla_print/Universal 0.01 - Light Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.01 - Light Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.025 UltraDetail layer_height = 0.01 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.01 - Medium Supports.ini b/PrusaSlicer/sla_print/Universal 0.01 - Medium Supports.ini index 031f5e2..9ad3b32 100644 --- a/PrusaSlicer/sla_print/Universal 0.01 - Medium Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.01 - Medium Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.025 UltraDetail layer_height = 0.01 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.02 - Heavy Supports.ini b/PrusaSlicer/sla_print/Universal 0.02 - Heavy Supports.ini index a05af1b..c77c787 100644 --- a/PrusaSlicer/sla_print/Universal 0.02 - Heavy Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.02 - Heavy Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.025 UltraDetail layer_height = 0.02 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.02 - Light Supports.ini b/PrusaSlicer/sla_print/Universal 0.02 - Light Supports.ini index 4b31a2f..dd7fad1 100644 --- a/PrusaSlicer/sla_print/Universal 0.02 - Light Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.02 - Light Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.025 UltraDetail layer_height = 0.02 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.02 - Medium Supports.ini b/PrusaSlicer/sla_print/Universal 0.02 - Medium Supports.ini index 4abdf3e..ec58cf6 100644 --- a/PrusaSlicer/sla_print/Universal 0.02 - Medium Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.02 - Medium Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.025 UltraDetail layer_height = 0.02 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.03 - Heavy Supports.ini b/PrusaSlicer/sla_print/Universal 0.03 - Heavy Supports.ini index a742d6e..5f059ee 100644 --- a/PrusaSlicer/sla_print/Universal 0.03 - Heavy Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.03 - Heavy Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.035 Detail layer_height = 0.03 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.03 - Light Supports.ini b/PrusaSlicer/sla_print/Universal 0.03 - Light Supports.ini index d4ecc3c..f6cf26b 100644 --- a/PrusaSlicer/sla_print/Universal 0.03 - Light Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.03 - Light Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.035 Detail layer_height = 0.03 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.03 - Medium Supports.ini b/PrusaSlicer/sla_print/Universal 0.03 - Medium Supports.ini index f76de22..fa95d0d 100644 --- a/PrusaSlicer/sla_print/Universal 0.03 - Medium Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.03 - Medium Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.035 Detail layer_height = 0.03 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.04 - Heavy Supports.ini b/PrusaSlicer/sla_print/Universal 0.04 - Heavy Supports.ini index 0b95e6a..596f540 100644 --- a/PrusaSlicer/sla_print/Universal 0.04 - Heavy Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.04 - Heavy Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.05 Normal layer_height = 0.04 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.04 - Light Supports.ini b/PrusaSlicer/sla_print/Universal 0.04 - Light Supports.ini index 8cb802c..f384ae1 100644 --- a/PrusaSlicer/sla_print/Universal 0.04 - Light Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.04 - Light Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.05 Normal layer_height = 0.04 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.04 - Medium Supports.ini b/PrusaSlicer/sla_print/Universal 0.04 - Medium Supports.ini index f85f2bb..49cc824 100644 --- a/PrusaSlicer/sla_print/Universal 0.04 - Medium Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.04 - Medium Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.05 Normal layer_height = 0.04 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.05 - Heavy Supports.ini b/PrusaSlicer/sla_print/Universal 0.05 - Heavy Supports.ini index 51f9874..7ae04f5 100644 --- a/PrusaSlicer/sla_print/Universal 0.05 - Heavy Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.05 - Heavy Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.05 Normal layer_height = 0.05 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.05 - Light Supports.ini b/PrusaSlicer/sla_print/Universal 0.05 - Light Supports.ini index 42c4012..fd821fc 100644 --- a/PrusaSlicer/sla_print/Universal 0.05 - Light Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.05 - Light Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.05 Normal layer_height = 0.05 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.05 - Medium Supports.ini b/PrusaSlicer/sla_print/Universal 0.05 - Medium Supports.ini index e73f429..34c70d1 100644 --- a/PrusaSlicer/sla_print/Universal 0.05 - Medium Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.05 - Medium Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.05 Normal layer_height = 0.05 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.10 - Heavy Supports.ini b/PrusaSlicer/sla_print/Universal 0.10 - Heavy Supports.ini index ce007b3..993832f 100644 --- a/PrusaSlicer/sla_print/Universal 0.10 - Heavy Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.10 - Heavy Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.1 Fast layer_height = 0.1 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.10 - Light Supports.ini b/PrusaSlicer/sla_print/Universal 0.10 - Light Supports.ini index 3d85901..60c151b 100644 --- a/PrusaSlicer/sla_print/Universal 0.10 - Light Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.10 - Light Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.1 Fast layer_height = 0.1 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.10 - Medium Supports.ini b/PrusaSlicer/sla_print/Universal 0.10 - Medium Supports.ini index 86cc2f4..65dc102 100644 --- a/PrusaSlicer/sla_print/Universal 0.10 - Medium Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.10 - Medium Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.1 Fast layer_height = 0.1 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.15 - Heavy Supports.ini b/PrusaSlicer/sla_print/Universal 0.15 - Heavy Supports.ini index 87c1089..8beb433 100644 --- a/PrusaSlicer/sla_print/Universal 0.15 - Heavy Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.15 - Heavy Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.1 Fast layer_height = 0.15 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.15 - Light Supports.ini b/PrusaSlicer/sla_print/Universal 0.15 - Light Supports.ini index befb278..3c34639 100644 --- a/PrusaSlicer/sla_print/Universal 0.15 - Light Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.15 - Light Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.1 Fast layer_height = 0.15 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.15 - Medium Supports.ini b/PrusaSlicer/sla_print/Universal 0.15 - Medium Supports.ini index 40e5b19..3b0061a 100644 --- a/PrusaSlicer/sla_print/Universal 0.15 - Medium Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.15 - Medium Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.1 Fast layer_height = 0.15 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.20 - Heavy Supports.ini b/PrusaSlicer/sla_print/Universal 0.20 - Heavy Supports.ini index f0600c1..abce98c 100644 --- a/PrusaSlicer/sla_print/Universal 0.20 - Heavy Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.20 - Heavy Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.1 Fast layer_height = 0.2 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.20 - Light Supports.ini b/PrusaSlicer/sla_print/Universal 0.20 - Light Supports.ini index f9d51d3..8e81454 100644 --- a/PrusaSlicer/sla_print/Universal 0.20 - Light Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.20 - Light Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.1 Fast layer_height = 0.2 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/PrusaSlicer/sla_print/Universal 0.20 - Medium Supports.ini b/PrusaSlicer/sla_print/Universal 0.20 - Medium Supports.ini index 92593d0..00cced8 100644 --- a/PrusaSlicer/sla_print/Universal 0.20 - Medium Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.20 - Medium Supports.ini @@ -9,7 +9,7 @@ hollowing_min_thickness = 3 hollowing_quality = 0.5 inherits = 0.1 Fast layer_height = 0.2 -output_filename_format = {input_filename_base}_{material_type}{layer_height}mm_{printer_model}_{print_time}.sl1 +output_filename_format = {input_filename_base}_{layer_height}mm_{print_time}.sl1 pad_around_object = 0 pad_around_object_everywhere = 0 pad_brim_size = 1.6 diff --git a/UVtools.CAD/UVtools_demo_file.sl1 b/UVtools.CAD/UVtools_demo_file.sl1 Binary files differindex 233a187..5d00568 100644 --- a/UVtools.CAD/UVtools_demo_file.sl1 +++ b/UVtools.CAD/UVtools_demo_file.sl1 diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs index 64b2dc5..b5ed369 100644 --- a/UVtools.Core/Extensions/EmguExtensions.cs +++ b/UVtools.Core/Extensions/EmguExtensions.cs @@ -63,10 +63,27 @@ namespace UVtools.Core.Extensions public static unsafe Span<T> GetPixelRowSpan<T>(this Mat mat, int y, int length = 0, int offset = 0) { - return new Span<T>(IntPtr.Add(mat.DataPointer, y * mat.Step + offset).ToPointer(), length == 0 ? mat.Step : length); + return new(IntPtr.Add(mat.DataPointer, y * mat.Step + offset).ToPointer(), length == 0 ? mat.Step : length); //return mat.GetPixelSpan<T>().Slice(offset, mat.Step); } + /// <summary> + /// Gets if a <see cref="Mat"/> is all zeroed + /// </summary> + /// <param name="mat"></param> + /// <param name="threshold">Pixel brightness threshold</param> + /// <returns></returns> + public static unsafe bool IsZeroed(this Mat mat, byte threshold = 0) + { + var ptr = mat.GetBytePointer(); + for (int i = 0; i < mat.GetLength(); i++) + { + if (ptr[i] > threshold) return false; + } + return true; + } + + public static void Transform(this Mat src, double xScale, double yScale, double xTrans = 0, double yTrans = 0, Size dstSize = default, Inter interpolation = Inter.Linear) { //var dst = new Mat(src.Size, src.Depth, src.NumberOfChannels); diff --git a/UVtools.Core/Extensions/MathExtensions.cs b/UVtools.Core/Extensions/MathExtensions.cs index 2020744..4bb6c92 100644 --- a/UVtools.Core/Extensions/MathExtensions.cs +++ b/UVtools.Core/Extensions/MathExtensions.cs @@ -103,12 +103,16 @@ namespace UVtools.Core.Extensions public static uint DecimalDigits(this decimal val) { var valStr = val.ToString(CultureInfo.InvariantCulture).TrimEnd('0'); - if (string.IsNullOrEmpty(valStr) || valStr[valStr.Length-1] == '.') return 0; + if (string.IsNullOrEmpty(valStr) || valStr[^1] == '.') return 0; var index = valStr.IndexOf('.'); if (index < 0) return 0; return (uint)(valStr.Substring(index).Length - 1); } + + public static bool IsInteger(this float val, float tolerance = 0.0001f) => Math.Abs(val - Math.Floor(val)) < tolerance; + public static bool IsInteger(this double val, double tolerance = 0.0001) => Math.Abs(val - Math.Floor(val)) < tolerance; + public static bool IsInteger(this decimal val) => val == Math.Floor(val); } } diff --git a/UVtools.Core/Extensions/PathExtensions.cs b/UVtools.Core/Extensions/PathExtensions.cs index 846c4e1..73937bb 100644 --- a/UVtools.Core/Extensions/PathExtensions.cs +++ b/UVtools.Core/Extensions/PathExtensions.cs @@ -6,6 +6,7 @@ * of this license document, but changing it is not allowed. */ using System; +using System.Collections.Generic; using System.IO; namespace UVtools.Core.Extensions @@ -19,5 +20,18 @@ namespace UVtools.Core.Extensions var splitPath = path.Split('.', 2, StringSplitOptions.TrimEntries); return splitPath.Length == 0 ? string.Empty : splitPath[0]; } + + public static string GetFileNameStripExtensions(string path, List<string> extensions) + { + path = Path.GetFileName(path); + if (string.IsNullOrEmpty(path)) return string.Empty; + foreach (var extension in extensions) + { + var dotExtension = $".{extension}"; + if (path.EndsWith(dotExtension)) return path.Remove(path.Length - dotExtension.Length); + } + + return path; + } } } diff --git a/UVtools.Core/Extensions/SizeExtensions.cs b/UVtools.Core/Extensions/SizeExtensions.cs index 8e164ec..455dfee 100644 --- a/UVtools.Core/Extensions/SizeExtensions.cs +++ b/UVtools.Core/Extensions/SizeExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.Text; namespace UVtools.Core.Extensions @@ -34,5 +35,8 @@ namespace UVtools.Core.Extensions adjustedSize, SizeSuffixes[mag]); } + + public static Size Inflate(this Size size, int pixels) => new (size.Width + pixels, size.Height + pixels); + public static Size Inflate(this Size size, int width, int height) => new (size.Width + width, size.Height + height); } } diff --git a/UVtools.Core/FileFormats/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs index 0578202..2312168 100644 --- a/UVtools.Core/FileFormats/CWSFile.cs +++ b/UVtools.Core/FileFormats/CWSFile.cs @@ -540,12 +540,8 @@ namespace UVtools.Core.FileFormats GCode = null; } - public override void Encode(string fileFullPath, OperationProgress progress = null) + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); - base.Encode(fileFullPath, progress); - //var filename = fileFullPath.EndsWith(TemporaryFileAppend) ? Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(fileFullPath)) : Path.GetFileNameWithoutExtension(fileFullPath); if (Printer == PrinterType.Unknown) @@ -662,20 +658,14 @@ namespace UVtools.Core.FileFormats RebuildGCode(); outputFile.PutFileContent($"{filename}.gcode", GCode.ToString(), ZipArchiveMode.Create); } - - AfterEncode(); } - public override void Decode(string fileFullPath, OperationProgress progress = null) + protected override void DecodeInternally(string fileFullPath, OperationProgress progress) { - base.Decode(fileFullPath, progress); - if(progress is null) progress = new OperationProgress(OperationProgress.StatusGatherLayers, LayerCount); - - FileFullPath = fileFullPath; - using (var inputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Read)) + using (var inputFile = ZipFile.Open(fileFullPath, ZipArchiveMode.Read)) { var entry = inputFile.GetEntry("manifest.xml"); - if (!ReferenceEquals(entry, null)) // Wanhao + if (entry is not null) // Wanhao { //DecodeXML(fileFullPath, inputFile, progress); Printer = PrinterType.Wanhao; diff --git a/UVtools.Core/FileFormats/ChituboxFile.cs b/UVtools.Core/FileFormats/ChituboxFile.cs index 242bc3d..3bb7648 100644 --- a/UVtools.Core/FileFormats/ChituboxFile.cs +++ b/UVtools.Core/FileFormats/ChituboxFile.cs @@ -1336,17 +1336,13 @@ namespace UVtools.Core.FileFormats LayerDefinitions = null; } - public override void Encode(string fileFullPath, OperationProgress progress = null) + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); - base.Encode(fileFullPath, progress); LayersHash.Clear(); HeaderSettings.Magic = fileFullPath.EndsWith(".ctb") || fileFullPath.EndsWith($".ctb{TemporaryFileAppend}") ? MAGIC_CBT : MAGIC_CBDDLP; HeaderSettings.PrintParametersSize = (uint)Helpers.Serializer.SizeOf(PrintParametersSettings); - if (IsCbtFile) { if (SlicerInfoSettings.AntiAliasLevel <= 1) @@ -1380,8 +1376,8 @@ namespace UVtools.Core.FileFormats } else { - HeaderSettings.Version = 2; - HeaderSettings.EncryptionKey = 0; + //HeaderSettings.Version = 2; + HeaderSettings.EncryptionKey = 0; // Force disable encryption SlicerInfoSettings.EncryptionMode = ENCRYPTYION_MODE_CBDDLP; } @@ -1511,8 +1507,6 @@ namespace UVtools.Core.FileFormats outputFile.Seek(0, SeekOrigin.Begin); Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - AfterEncode(); - Debug.WriteLine("Encode Results:"); Debug.WriteLine(HeaderSettings); Debug.WriteLine(Previews[0]); @@ -1523,14 +1517,9 @@ namespace UVtools.Core.FileFormats } } - - public override void Decode(string fileFullPath, OperationProgress progress = null) + protected override void DecodeInternally(string fileFullPath, OperationProgress progress) { - base.Decode(fileFullPath, progress); - if(progress is null) progress = new OperationProgress(); - progress.Reset(OperationProgress.StatusGatherLayers, LayerCount); - using (var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read)) { //HeaderSettings = Helpers.ByteToType<CbddlpFile.Header>(InputFile); @@ -1659,38 +1648,34 @@ namespace UVtools.Core.FileFormats return; } - using (var image = LayerDefinitions[0, layerIndex].Decode((uint) layerIndex)) + using var image = LayerDefinitions[0, layerIndex].Decode((uint) layerIndex); + var layer = new Layer((uint) layerIndex, image, LayerManager) { - var layer = new Layer((uint) layerIndex, image, LayerManager) - { - PositionZ = LayerDefinitions[0, layerIndex].LayerPositionZ, - ExposureTime = LayerDefinitions[0, layerIndex].LayerExposure, - LightOffDelay = LayerDefinitions[0, layerIndex].LightOffSeconds, - }; + PositionZ = LayerDefinitions[0, layerIndex].LayerPositionZ, + ExposureTime = LayerDefinitions[0, layerIndex].LayerExposure, + LightOffDelay = LayerDefinitions[0, layerIndex].LightOffSeconds, + }; - if (LayerDefinitionsEx is not null) + if (LayerDefinitionsEx is not null) + { + if (layerIndex == 0) { - if (layerIndex == 0) - { - } - layer.LiftHeight = LayerDefinitionsEx[layerIndex].LiftHeight; - layer.LiftSpeed = LayerDefinitionsEx[layerIndex].LiftSpeed; - layer.RetractSpeed = LayerDefinitionsEx[layerIndex].RetractSpeed; - layer.LightPWM = (byte) LayerDefinitionsEx[layerIndex].LightPWM; } + layer.LiftHeight = LayerDefinitionsEx[layerIndex].LiftHeight; + layer.LiftSpeed = LayerDefinitionsEx[layerIndex].LiftSpeed; + layer.RetractSpeed = LayerDefinitionsEx[layerIndex].RetractSpeed; + layer.LightPWM = (byte) LayerDefinitionsEx[layerIndex].LightPWM; + } - this[layerIndex] = layer; + this[layerIndex] = layer; - lock (progress.Mutex) - { - progress++; - } + lock (progress.Mutex) + { + progress++; } }); } - - progress.Token.ThrowIfCancellationRequested(); } public override void SaveAs(string filePath = null, OperationProgress progress = null) diff --git a/UVtools.Core/FileFormats/ChituboxZipFile.cs b/UVtools.Core/FileFormats/ChituboxZipFile.cs index d9cf337..fc011a0 100644 --- a/UVtools.Core/FileFormats/ChituboxZipFile.cs +++ b/UVtools.Core/FileFormats/ChituboxZipFile.cs @@ -400,11 +400,8 @@ namespace UVtools.Core.FileFormats #region Methods - public override void Encode(string fileFullPath, OperationProgress progress = null) + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); - base.Encode(fileFullPath, progress); using (ZipArchive outputFile = ZipFile.Open(fileFullPath, ZipArchiveMode.Create)) { if (Thumbnails.Length > 0 && !ReferenceEquals(Thumbnails[0], null)) @@ -448,21 +445,14 @@ namespace UVtools.Core.FileFormats progress++; } } - - AfterEncode(); } - public override void Decode(string fileFullPath, OperationProgress progress = null) + protected override void DecodeInternally(string fileFullPath, OperationProgress progress) { - base.Decode(fileFullPath, progress); - if(progress is null) progress = new OperationProgress(); - progress.Reset(OperationProgress.StatusGatherLayers, LayerCount); - - FileFullPath = fileFullPath; using (var inputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Read)) { var entry = inputFile.GetEntry("run.gcode"); - if (!ReferenceEquals(entry, null)) + if (entry is not null) { //Clear(); //throw new FileLoadException("run.gcode not found", fileFullPath); diff --git a/UVtools.Core/FileFormats/FDGFile.cs b/UVtools.Core/FileFormats/FDGFile.cs index d2ed39d..53d5bd1 100644 --- a/UVtools.Core/FileFormats/FDGFile.cs +++ b/UVtools.Core/FileFormats/FDGFile.cs @@ -999,11 +999,8 @@ namespace UVtools.Core.FileFormats LayersDefinitions = null; } - public override void Encode(string fileFullPath, OperationProgress progress = null) + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); - base.Encode(fileFullPath, progress); LayersHash.Clear(); /*if (HeaderSettings.EncryptionKey == 0) @@ -1118,8 +1115,6 @@ namespace UVtools.Core.FileFormats outputFile.Seek(0, SeekOrigin.Begin); Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - AfterEncode(); - Debug.WriteLine("Encode Results:"); Debug.WriteLine(HeaderSettings); Debug.WriteLine(Previews[0]); @@ -1128,11 +1123,8 @@ namespace UVtools.Core.FileFormats } } - public override void Decode(string fileFullPath, OperationProgress progress = null) + protected override void DecodeInternally(string fileFullPath, OperationProgress progress) { - base.Decode(fileFullPath, progress); - if(progress is null) progress = new OperationProgress(OperationProgress.StatusGatherLayers, LayerCount); - using (var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read)) { @@ -1235,8 +1227,6 @@ namespace UVtools.Core.FileFormats } }); } - - progress.Token.ThrowIfCancellationRequested(); } public override void SaveAs(string filePath = null, OperationProgress progress = null) diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index 462ffd1..3bd1f15 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -26,7 +26,7 @@ namespace UVtools.Core.FileFormats /// <summary> /// Slicer <see cref="FileFormat"/> representation /// </summary> - public abstract class FileFormat : BindableBase, IFileFormat, IDisposable, IEquatable<FileFormat>, IEnumerable<Layer> + public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFormat>, IEnumerable<Layer> { #region Constants public const string TemporaryFileAppend = ".tmp"; @@ -44,6 +44,9 @@ namespace UVtools.Core.FileFormats public const float DefaultLightOffDelay = 0; public const byte DefaultBottomLightPWM = 255; public const byte DefaultLightPWM = 255; + + public const float MinimumLayerHeight = 0.01f; + public const float MaximumLayerHeight = 0.20f; #endregion #region Enums @@ -190,11 +193,11 @@ namespace UVtools.Core.FileFormats public static FileFormat[] AvailableFormats { get; } = { new SL1File(), // Prusa SL1 - new ChituboxZipFile(), // Zip + new ChituboxZipFile(), // Zip new ChituboxFile(), // cbddlp, cbt, photon + new PhotonSFile(), // photons new PHZFile(), // phz new FDGFile(), // fdg - new PhotonSFile(), // photons new PhotonWorkshopFile(), // PSW new ZCodexFile(), // zcodex new CWSFile(), // CWS @@ -243,19 +246,28 @@ namespace UVtools.Core.FileFormats } } - - /// <summary> - /// Gets the count of available file extensions - /// </summary> - public static byte FileExtensionsCount + public static List<FileExtension> AllFileExtensions { get { - return AvailableFormats.Aggregate<FileFormat, byte>(0, (current, fileFormat) => (byte) (current + fileFormat.FileExtensions.Length)); + List<FileExtension> extensions = new(); + foreach (var slicerFile in AvailableFormats) + { + extensions.AddRange(slicerFile.FileExtensions); + } + return extensions; } } + public static List<string> AllFileExtensionsString => (from slicerFile in AvailableFormats from extension in slicerFile.FileExtensions select extension.Extension).ToList(); + + + /// <summary> + /// Gets the count of available file extensions + /// </summary> + public static byte FileExtensionsCount => AvailableFormats.Aggregate<FileFormat, byte>(0, (current, fileFormat) => (byte) (current + fileFormat.FileExtensions.Length)); + /// <summary> /// Find <see cref="FileFormat"/> by an extension /// </summary> @@ -285,14 +297,54 @@ namespace UVtools.Core.FileFormats } #endregion + #region Members + private bool _haveModifiedLayers; + private LayerManager _layerManager; + private float _printTime; + private float _maxPrintHeight; + #endregion + #region Properties + /// <summary> + /// Gets the file format type + /// </summary> public abstract FileFormatType FileType { get; } + /// <summary> + /// Gets the valid file extensions for this <see cref="FileFormat"/> + /// </summary> public abstract FileExtension[] FileExtensions { get; } + + /// <summary> + /// Gets the available <see cref="FileFormat.PrintParameterModifier"/> + /// </summary> public abstract PrintParameterModifier[] PrintParameterModifiers { get; } + + /// <summary> + /// Gets the available <see cref="FileFormat.PrintParameterModifier"/> per layer + /// </summary> public virtual PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = null; + /// <summary> + /// Checks if a <see cref="PrintParameterModifier"/> exists on print parameters + /// </summary> + /// <param name="modifier"></param> + /// <returns>True if exists, otherwise false</returns> + public bool HavePrintParameterModifier(PrintParameterModifier modifier) => + PrintParameterModifiers is not null && PrintParameterModifiers.Contains(modifier); + + /// <summary> + /// Checks if a <see cref="PrintParameterModifier"/> exists on print parameters + /// </summary> + /// <param name="modifier"></param> + /// <returns>True if exists, otherwise false</returns> + public bool HavePrintParameterPerLayerModifier(PrintParameterModifier modifier) => + PrintParameterPerLayerModifiers is not null && PrintParameterPerLayerModifiers.Contains(modifier); + + /// <summary> + /// Gets the file filter for open and save dialogs + /// </summary> public string FileFilter { get { @@ -311,9 +363,15 @@ namespace UVtools.Core.FileFormats } } + /// <summary> + /// Gets all valid file extensions for Avalonia file dialog + /// </summary> public List<KeyValuePair<string, List<string>>> FileFilterAvalonia => FileExtensions.Select(fileExt => new KeyValuePair<string, List<string>>(fileExt.Description, new List<string> {fileExt.Extension})).ToList(); + /// <summary> + /// Gets all valid file extensions in "*.extension1;*.extension2" format + /// </summary> public string FileFilterExtensionsOnly { get @@ -333,12 +391,24 @@ namespace UVtools.Core.FileFormats } } + /// <summary> + /// Gets or sets if change a global property should rebuild every layer data based on them + /// </summary> public bool SuppressRebuildProperties { get; set; } + /// <summary> + /// Gets the input file path loaded into this <see cref="FileFormat"/> + /// </summary> public string FileFullPath { get; set; } + /// <summary> + /// Gets the thumbnails count present in this file format + /// </summary> public abstract byte ThumbnailsCount { get; } + /// <summary> + /// Gets the number of created thumbnails + /// </summary> public byte CreatedThumbnailsCount { get { @@ -355,10 +425,19 @@ namespace UVtools.Core.FileFormats } } + /// <summary> + /// Gets the original thumbnail sizes + /// </summary> public abstract Size[] ThumbnailsOriginalSize { get; } + /// <summary> + /// Gets the thumbnails for this <see cref="FileFormat"/> + /// </summary> public Mat[] Thumbnails { get; set; } + /// <summary> + /// Gets the cached layers into compressed bytes + /// </summary> public LayerManager LayerManager { get => _layerManager; @@ -373,10 +452,10 @@ namespace UVtools.Core.FileFormats } } - private bool _haveModifiedLayers; - private LayerManager _layerManager; - private float _printTime; - private float _maxPrintHeight; + /// <summary> + /// Gets the bounding rectangle of the object + /// </summary> + public Rectangle BoundingRectangle => _layerManager?.BoundingRectangle ?? Rectangle.Empty; /// <summary> /// Gets or sets if modifications require a full encode to save @@ -387,6 +466,9 @@ namespace UVtools.Core.FileFormats set => _haveModifiedLayers = value; } // => LayerManager.IsModified; + /// <summary> + /// Gets the image width resolution + /// </summary> public Size Resolution { get => new((int)ResolutionX, (int)ResolutionY); @@ -396,12 +478,21 @@ namespace UVtools.Core.FileFormats ResolutionY = (uint) value.Height; RaisePropertyChanged(); } - } - + } + + /// <summary> + /// Gets the image width resolution + /// </summary> public abstract uint ResolutionX { get; set; } + /// <summary> + /// Gets the image height resolution + /// </summary> public abstract uint ResolutionY { get; set; } + /// <summary> + /// Gets the size of display in millimeters + /// </summary> public SizeF Display { get => new(DisplayWidth, DisplayHeight); @@ -413,16 +504,33 @@ namespace UVtools.Core.FileFormats } } + /// <summary> + /// Gets or sets the display width in millimeters + /// </summary> public abstract float DisplayWidth { get; set; } + + /// <summary> + /// Gets or sets the display height in millimeters + /// </summary> public abstract float DisplayHeight { get; set; } + + /// <summary> + /// Gets or sets if images need to be mirrored on lcd to print on the correct orientation + /// </summary> public abstract bool MirrorDisplay { get; set; } + /// <summary> + /// Gets or sets the maximum printer build Z volume + /// </summary> public virtual float MaxPrintHeight { get => _maxPrintHeight > 0 ? _maxPrintHeight : PrintHeight; set => RaiseAndSetIfChanged(ref _maxPrintHeight, value); } + /// <summary> + /// Gets or sets the pixels per mm on X direction + /// </summary> public virtual float Xppmm { get => DisplayWidth > 0 ? ResolutionX / DisplayWidth : 0; @@ -433,6 +541,9 @@ namespace UVtools.Core.FileFormats } } + /// <summary> + /// Gets or sets the pixels per mm on Y direction + /// </summary> public virtual float Yppmm { get => DisplayHeight > 0 ? ResolutionY / DisplayHeight : 0; @@ -443,6 +554,9 @@ namespace UVtools.Core.FileFormats } } + /// <summary> + /// Gets or sets the pixels per mm + /// </summary> public SizeF Ppmm { get => new(Xppmm, Yppmm); @@ -454,6 +568,9 @@ namespace UVtools.Core.FileFormats } + /// <summary> + /// Gets the printer XY pixel resolution + /// </summary> public decimal XYResolution => DisplayWidth > 0 || DisplayHeight > 0 ? (decimal) Math.Round(Math.Max( DisplayWidth / ResolutionX, @@ -461,6 +578,9 @@ namespace UVtools.Core.FileFormats ), 3) : 0; + /// <summary> + /// Gets the printer XY pixel resolution in microns + /// </summary> public decimal XYResolutionUm => DisplayWidth > 0 || DisplayHeight > 0 ? (decimal)Math.Round(Math.Max( DisplayWidth / ResolutionX, @@ -468,11 +588,30 @@ namespace UVtools.Core.FileFormats ), 3) * 1000 : 0; + /// <summary> + /// Checks if this file have AntiAliasing + /// </summary> public bool HaveAntiAliasing => AntiAliasing > 1; + + /// <summary> + /// Gets or sets the AntiAliasing level + /// </summary> public abstract byte AntiAliasing { get; set; } + /// <summary> + /// Gets Layer Height in mm + /// </summary> public abstract float LayerHeight { get; set; } + /// <summary> + /// Gets Layer Height in um + /// </summary> + public ushort LayerHeightUm => (ushort) (LayerHeight * 1000); + + + /// <summary> + /// Gets or sets the print height in mm + /// </summary> public virtual float PrintHeight { get => LayerCount == 0 ? 0 : this[LayerCount - 1]?.PositionZ ?? 0; @@ -482,30 +621,97 @@ namespace UVtools.Core.FileFormats } } + /// <summary> + /// Gets the last layer index + /// </summary> public uint LastLayerIndex => LayerCount - 1; + + /// <summary> + /// Checks if this file format supports per layer settings + /// </summary> public virtual bool SupportPerLayerSettings => !(PrintParameterPerLayerModifiers is null || PrintParameterPerLayerModifiers.Length == 0); + /// <summary> + /// Gets or sets the layer count + /// </summary> public virtual uint LayerCount { get => LayerManager?.Count ?? 0; set { } } + #region Universal Properties + + /// <summary> + /// Gets or sets the number of initial layer count + /// </summary> public virtual ushort BottomLayerCount { get; set; } + + /// <summary> + /// Gets the number of normal layer count + /// </summary> public uint NormalLayerCount => LayerCount - BottomLayerCount; + + /// <summary> + /// Gets or sets the initial exposure time for <see cref="BottomLayerCount"/> in seconds + /// </summary> public virtual float BottomExposureTime { get; set; } + + /// <summary> + /// Gets or sets the normal layer exposure time in seconds + /// </summary> public virtual float ExposureTime { get; set; } + + /// <summary> + /// Gets or sets the bottom layer off time in seconds + /// </summary> public virtual float BottomLightOffDelay { get; set; } = DefaultBottomLightOffDelay; + + /// <summary> + /// Gets or sets the layer off time in seconds + /// </summary> public virtual float LightOffDelay { get; set; } = DefaultLightOffDelay; + + /// <summary> + /// Gets or sets the bottom lift height in mm + /// </summary> public virtual float BottomLiftHeight { get; set; } = DefaultBottomLiftHeight; + + /// <summary> + /// Gets or sets the lift height in mm + /// </summary> public virtual float LiftHeight { get; set; } = DefaultLiftHeight; + + /// <summary> + /// Gets or sets the bottom lift speed in mm/min + /// </summary> public virtual float BottomLiftSpeed { get; set; } = DefaultBottomLiftSpeed; + + /// <summary> + /// Gets or sets the speed in mm/min + /// </summary> public virtual float LiftSpeed { get; set; } = DefaultLiftSpeed; + + /// <summary> + /// Gets the speed in mm/min for the retracts + /// </summary> public virtual float RetractSpeed { get; set; } = DefaultRetractSpeed; + + /// <summary> + /// Gets or sets the bottom pwm value from 0 to 255 + /// </summary> public virtual byte BottomLightPWM { get; set; } = DefaultBottomLightPWM; + + /// <summary> + /// Gets or sets the pwm value from 0 to 255 + /// </summary> public virtual byte LightPWM { get; set; } = DefaultLightPWM; + #endregion + /// <summary> + /// Gets the estimate print time in seconds + /// </summary> public virtual float PrintTime { get => _printTime; @@ -520,9 +726,14 @@ namespace UVtools.Core.FileFormats } } - //(header.numberOfLayers - header.bottomLayers) * (double) header.exposureTimeSeconds + (double) header.bottomLayers * (double) header.exposureBottomTimeSeconds + (double) header.offTimeSeconds * (double) header.numberOfLayers); + /// <summary> + /// Gets the estimate print time in seconds, if print doesn't support it it will be calculated + /// </summary> public float PrintTimeOrComputed => PrintTime > 0 ? PrintTime : PrintTimeComputed; + /// <summary> + /// Gets the calculated estimate print time in seconds + /// </summary> public float PrintTimeComputed { get @@ -562,27 +773,65 @@ namespace UVtools.Core.FileFormats } } + /// <summary> + /// Gets the estimate print time in hours + /// </summary> public float PrintTimeHours => (float) Math.Round(PrintTimeOrComputed / 3600, 2); + /// <summary> + /// Gets the estimate print time in hours and minutes formatted + /// </summary> public string PrintTimeString => TimeSpan.FromSeconds(PrintTimeOrComputed).ToString("hh\\hmm\\m"); + /// <summary> + /// Gets the estimate used material in ml + /// </summary> public virtual float MaterialMilliliters { get; set; } + + /// <summary> + /// Gets the estimate material in grams + /// </summary> public virtual float MaterialGrams { get; set; } + /// <summary> + /// Gets the estimate material cost + /// </summary> public virtual float MaterialCost { get; set; } + /// <summary> + /// Gets the material name + /// </summary> public virtual string MaterialName { get; set; } + /// <summary> + /// Gets the machine name + /// </summary> public virtual string MachineName { get; set; } = "Unknown"; + /// <summary> + /// Gets the GCode, returns null if not supported + /// </summary> public StringBuilder GCode { get; set; } + /// <summary> + /// Gets the GCode, returns null if not supported + /// </summary> public string GCodeStr => GCode?.ToString(); - public bool HaveGCode => !(GCode is null); + /// <summary> + /// Gets if this file have available gcode + /// </summary> + public bool HaveGCode => GCode is not null; + + /// <summary> + /// Get all configuration objects with properties and values + /// </summary> public abstract object[] Configs { get; } - public bool IsValid => !ReferenceEquals(FileFullPath, null); + /// <summary> + /// Gets if this file is valid to read + /// </summary> + public bool IsValid => FileFullPath is not null; #endregion #region Constructor @@ -675,7 +924,7 @@ namespace UVtools.Core.FileFormats { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; - return FileFullPath.Equals(other.FileFullPath); + return FileFullPath == other.FileFullPath; } public override int GetHashCode() @@ -691,26 +940,31 @@ namespace UVtools.Core.FileFormats #endregion #region Methods + /// <summary> + /// Clears all definitions and properties, it also dispose valid candidates + /// </summary> public virtual void Clear() { FileFullPath = null; LayerManager = null; GCode = null; - if (!ReferenceEquals(Thumbnails, null)) + if (Thumbnails is not null) { for (int i = 0; i < ThumbnailsCount; i++) { Thumbnails[i]?.Dispose(); } } - - } + /// <summary> + /// Validate if a file is a valid <see cref="FileFormat"/> + /// </summary> + /// <param name="fileFullPath">Full file path</param> public void FileValidation(string fileFullPath) { - if (ReferenceEquals(fileFullPath, null)) throw new ArgumentNullException(nameof(FileFullPath), "fullFilePath can't be null."); + if (fileFullPath is null) throw new ArgumentNullException(nameof(FileFullPath), "fullFilePath can't be null."); if (!File.Exists(fileFullPath)) throw new FileNotFoundException("The specified file does not exists.", fileFullPath); if (IsExtensionValid(fileFullPath, true)) @@ -721,12 +975,21 @@ namespace UVtools.Core.FileFormats throw new FileLoadException($"The specified file is not valid.", fileFullPath); } + /// <summary> + /// Checks if a extension is valid under the <see cref="FileFormat"/> + /// </summary> + /// <param name="extension">Extension to check</param> + /// <param name="isFilePath">True if <see cref="extension"/> is a full file path, otherwise false for extension only</param> + /// <returns>True if valid, otherwise false</returns> public bool IsExtensionValid(string extension, bool isFilePath = false) { extension = isFilePath ? Path.GetExtension(extension)?.Remove(0, 1) : extension; return FileExtensions.Any(fileExtension => fileExtension.Equals(extension)); } + /// <summary> + /// Gets all valid file extensions in a specified format + /// </summary> public string GetFileExtensions(string prepend = ".", string separator = ", ") { var result = string.Empty; @@ -743,6 +1006,11 @@ namespace UVtools.Core.FileFormats return result; } + /// <summary> + /// Gets a thumbnail by it height or lower + /// </summary> + /// <param name="maxHeight">Max height allowed</param> + /// <returns></returns> public Mat GetThumbnail(uint maxHeight = 400) { for (int i = 0; i < ThumbnailsCount; i++) @@ -754,6 +1022,11 @@ namespace UVtools.Core.FileFormats return null; } + /// <summary> + /// Gets a thumbnail by the largest or smallest + /// </summary> + /// <param name="largest">True to get the largest, otherwise false</param> + /// <returns></returns> public Mat GetThumbnail(bool largest) { switch (CreatedThumbnailsCount) @@ -774,6 +1047,10 @@ namespace UVtools.Core.FileFormats } } + /// <summary> + /// Sets thumbnails from a list of thumbnails and clone them + /// </summary> + /// <param name="images"></param> public void SetThumbnails(Mat[] images) { for (var i = 0; i < ThumbnailsCount; i++) @@ -786,6 +1063,10 @@ namespace UVtools.Core.FileFormats } } + /// <summary> + /// Sets all thumbnails the same image + /// </summary> + /// <param name="images">Image to set</param> public void SetThumbnails(Mat image) { for (var i = 0; i < ThumbnailsCount; i++) @@ -798,6 +1079,11 @@ namespace UVtools.Core.FileFormats } } + /// <summary> + /// Sets a thumbnail from a disk file + /// </summary> + /// <param name="index">Thumbnail index</param> + /// <param name="filePath"></param> public void SetThumbnail(int index, string filePath) { Thumbnails[index] = CvInvoke.Imread(filePath, ImreadModes.AnyColor); @@ -807,8 +1093,23 @@ namespace UVtools.Core.FileFormats } } - public virtual void Encode(string fileFullPath, OperationProgress progress = null) + /// <summary> + /// Encode to an output file + /// </summary> + /// <param name="fileFullPath">Output file</param> + /// <param name="progress"></param> + protected abstract void EncodeInternally(string fileFullPath, OperationProgress progress); + + /// <summary> + /// Encode to an output file + /// </summary> + /// <param name="fileFullPath">Output file</param> + /// <param name="progress"></param> + public void Encode(string fileFullPath, OperationProgress progress = null) { + progress ??= new OperationProgress(); + progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); + FileFullPath = fileFullPath; if (File.Exists(fileFullPath)) @@ -822,25 +1123,69 @@ namespace UVtools.Core.FileFormats if(Thumbnails[i].Size == ThumbnailsOriginalSize[i]) continue; CvInvoke.Resize(Thumbnails[i], Thumbnails[i], new Size(ThumbnailsOriginalSize[i].Width, ThumbnailsOriginalSize[i].Height)); } - } - public void AfterEncode() - { + EncodeInternally(fileFullPath, progress); + LayerManager.Desmodify(); RequireFullEncode = false; } - public virtual void Decode(string fileFullPath, OperationProgress progress = null) + /// <summary> + /// Decode a slicer file + /// </summary> + /// <param name="fileFullPath"></param> + /// <param name="progress"></param> + protected abstract void DecodeInternally(string fileFullPath, OperationProgress progress); + + /// <summary> + /// Decode a slicer file + /// </summary> + /// <param name="fileFullPath"></param> + /// <param name="progress"></param> + public void Decode(string fileFullPath, OperationProgress progress = null) { Clear(); FileValidation(fileFullPath); FileFullPath = fileFullPath; + progress ??= new OperationProgress(); + progress.Reset(OperationProgress.StatusGatherLayers, LayerCount); + + DecodeInternally(fileFullPath, progress); + + progress.Token.ThrowIfCancellationRequested(); + + // Sanitize + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + // Check for null layers + if(this[layerIndex] is null) throw new FileLoadException($"Layer {layerIndex} was defined but doesn't contain a valid image.", fileFullPath); + if(layerIndex <= 0) continue; + // Check for bigger position z than it successor + if(this[layerIndex-1].PositionZ > this[layerIndex].PositionZ) throw new FileLoadException($"Layer {layerIndex-1} ({this[layerIndex - 1].PositionZ}mm) have a higher Z position than the successor layer {layerIndex} ({this[layerIndex].PositionZ}mm).\n", fileFullPath); + } + + // Fix 0mm positions at layer 0 + if(this[0].PositionZ == 0) + { + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + this[layerIndex].PositionZ += LayerHeight; + } + Save(progress); + } } + /// <summary> + /// Extract contents to a folder + /// </summary> + /// <param name="path">Path to folder where content will be extracted</param> + /// <param name="genericConfigExtract"></param> + /// <param name="genericLayersExtract"></param> + /// <param name="progress"></param> public virtual void Extract(string path, bool genericConfigExtract = true, bool genericLayersExtract = true, OperationProgress progress = null) { - if (ReferenceEquals(progress, null)) progress = new OperationProgress(); + progress ??= new OperationProgress(); progress.ItemName = OperationProgress.StatusExtracting; /*if (emptyFirst) { @@ -955,16 +1300,33 @@ namespace UVtools.Core.FileFormats } } + /// <summary> + /// Get height in mm from layer height + /// </summary> + /// <param name="layerIndex"></param> + /// <param name="realHeight"></param> + /// <returns>The height in mm</returns> public float GetHeightFromLayer(uint layerIndex, bool realHeight = true) { return (float)Math.Round((layerIndex+(realHeight ? 1 : 0)) * LayerHeight, 2); } + /// <summary> + /// Gets the value for initial layer or normal layers based on layer index + /// </summary> + /// <typeparam name="T">Type of value</typeparam> + /// <param name="layerIndex">Layer index</param> + /// <param name="initialLayerValue">Initial value</param> + /// <param name="normalLayerValue">Normal value</param> + /// <returns></returns> public T GetInitialLayerValueOrNormal<T>(uint layerIndex, T initialLayerValue, T normalLayerValue) { return layerIndex < BottomLayerCount ? initialLayerValue : normalLayerValue; } + /// <summary> + /// Refresh print parameters globals with this file settings + /// </summary> public void RefreshPrintParametersModifiersValues() { if (PrintParameterModifiers is null) return; @@ -1029,6 +1391,9 @@ namespace UVtools.Core.FileFormats } } + /// <summary> + /// Refresh print parameters per layer globals with this file settings + /// </summary> public void RefreshPrintParametersPerLayerModifiersValues(uint layerIndex) { if (PrintParameterPerLayerModifiers is null) return; @@ -1065,6 +1430,11 @@ namespace UVtools.Core.FileFormats } } + /// <summary> + /// Gets the value attributed to <see cref="FileFormat.PrintParameterModifier"/> + /// </summary> + /// <param name="modifier">Modifier to use</param> + /// <returns>A value</returns> public object GetValueFromPrintParameterModifier(PrintParameterModifier modifier) { if (ReferenceEquals(modifier, PrintParameterModifier.BottomLayerCount)) @@ -1098,6 +1468,12 @@ namespace UVtools.Core.FileFormats return null; } + /// <summary> + /// Sets a property value attributed to <see cref="modifier"/> + /// </summary> + /// <param name="modifier">Modifier to use</param> + /// <param name="value">Value to set</param> + /// <returns>True if set, otherwise false = <see cref="modifier"/> not found</returns> public bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, decimal value) { if (ReferenceEquals(modifier, PrintParameterModifier.BottomLayerCount)) @@ -1167,6 +1543,10 @@ namespace UVtools.Core.FileFormats return false; } + /// <summary> + /// Sets properties from print parameters + /// </summary> + /// <returns>Number of affected parameters</returns> public byte SetValuesFromPrintParametersModifiers() { if (PrintParameterModifiers is null) return 0; @@ -1182,36 +1562,34 @@ namespace UVtools.Core.FileFormats return changed; } - public void EditPrintParameters(OperationEditParameters operation) - { - if (operation.PerLayerOverride) - { - for (uint layerIndex = operation.LayerIndexStart; layerIndex <= operation.LayerIndexEnd; layerIndex++) - { - this[layerIndex].SetValuesFromPrintParametersModifiers(operation.Modifiers); - } - - foreach (var modifier in operation.Modifiers) - { - modifier.OldValue = modifier.NewValue; - } - RebuildGCode(); - } - else - { - SetValuesFromPrintParametersModifiers(); - } - } - + /// <summary> + /// Rebuilds GCode based on current settings + /// </summary> public virtual void RebuildGCode() { } + /// <summary> + /// Saves current configuration on input file + /// </summary> + /// <param name="progress"></param> public void Save(OperationProgress progress = null) { SaveAs(null, progress); } + /// <summary> + /// Saves current configuration on a copy + /// </summary> + /// <param name="filePath">File path to save copy as, use null to overwrite active file (Same as <see cref="Save"/>)</param> + /// <param name="progress"></param> public abstract void SaveAs(string filePath = null, OperationProgress progress = null); + /// <summary> + /// Converts this file type to another file type + /// </summary> + /// <param name="to">Target file format</param> + /// <param name="fileFullPath">Output path file</param> + /// <param name="progress"></param> + /// <returns>The converted file if successful, otherwise null</returns> public virtual FileFormat Convert(Type to, string fileFullPath, OperationProgress progress = null) { if (!IsValid) return null; @@ -1270,9 +1648,20 @@ namespace UVtools.Core.FileFormats return slicerFile; } + + /// <summary> + /// Converts this file type to another file type + /// </summary> + /// <param name="to">Target file format</param> + /// <param name="fileFullPath">Output path file</param> + /// <param name="progress"></param> + /// <returns>TThe converted file if successful, otherwise null</returns> public FileFormat Convert(FileFormat to, string fileFullPath, OperationProgress progress = null) => Convert(to.GetType(), fileFullPath, progress); + /// <summary> + /// Validate AntiAlias Level + /// </summary> public byte ValidateAntiAliasingLevel() { if (AntiAliasing < 2) return 1; diff --git a/UVtools.Core/FileFormats/IFileFormat.cs b/UVtools.Core/FileFormats/IFileFormat.cs deleted file mode 100644 index e96bb0a..0000000 --- a/UVtools.Core/FileFormats/IFileFormat.cs +++ /dev/null @@ -1,510 +0,0 @@ -/* - * GNU AFFERO GENERAL PUBLIC LICENSE - * Version 3, 19 November 2007 - * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> - * Everyone is permitted to copy and distribute verbatim copies - * of this license document, but changing it is not allowed. - */ - -using System; -using System.Drawing; -using System.Text; -using Emgu.CV; -using UVtools.Core.Operations; - -namespace UVtools.Core.FileFormats -{ - /// <summary> - /// Slicer file format representation interface - /// </summary> - public interface IFileFormat - { - #region Properties - /// <summary> - /// Gets the file format type - /// </summary> - FileFormat.FileFormatType FileType { get; } - - /// <summary> - /// Gets the valid file extensions for this <see cref="FileFormat"/> - /// </summary> - FileExtension[] FileExtensions { get; } - - /// <summary> - /// Gets the available <see cref="FileFormat.PrintParameterModifier"/> - /// </summary> - FileFormat.PrintParameterModifier[] PrintParameterModifiers { get; } - - /// <summary> - /// Gets the available <see cref="FileFormat.PrintParameterModifier"/> per layer - /// </summary> - FileFormat.PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } - - /// <summary> - /// Gets the file filter for open and save dialogs - /// </summary> - string FileFilter { get; } - - /// <summary> - /// Gets all valid file extensions in "*.extension1;*.extension2" format - /// </summary> - - string FileFilterExtensionsOnly { get; } - - /// <summary> - /// Gets or sets if change a global property should rebuild every layer data based on them - /// </summary> - bool SuppressRebuildProperties { get; set; } - - /// <summary> - /// Gets the input file path loaded into this <see cref="FileFormat"/> - /// </summary> - string FileFullPath { get; set; } - - /// <summary> - /// Gets the thumbnails count present in this file format - /// </summary> - byte ThumbnailsCount { get; } - - /// <summary> - /// Gets the number of created thumbnails - /// </summary> - byte CreatedThumbnailsCount { get; } - - /// <summary> - /// Gets the original thumbnail sizes - /// </summary> - System.Drawing.Size[] ThumbnailsOriginalSize { get; } - - /// <summary> - /// Gets the thumbnails for this <see cref="FileFormat"/> - /// </summary> - Mat[] Thumbnails { get; set; } - - /// <summary> - /// Gets the cached layers into compressed bytes - /// </summary> - LayerManager LayerManager { get; set; } - - Size Resolution { get; set; } - - /// <summary> - /// Gets the image width resolution - /// </summary> - uint ResolutionX { get; set; } - - /// <summary> - /// Gets the image height resolution - /// </summary> - uint ResolutionY { get; set; } - - /// <summary> - /// Gets the size of display in millimeters - /// </summary> - SizeF Display { get; set; } - - /// <summary> - /// Gets or sets the display width in millimeters - /// </summary> - float DisplayWidth { get; set; } - - /// <summary> - /// Gets or sets the display height in millimeters - /// </summary> - float DisplayHeight { get; set; } - - /// <summary> - /// Gets or sets if images need to be mirrored on lcd to print on the correct orientation - /// </summary> - bool MirrorDisplay { get; set; } - - /// <summary> - /// Gets or sets the maximum printer build Z volume - /// </summary> - float MaxPrintHeight { get; set; } - - /// <summary> - /// Gets or sets the pixels per mm on X direction - /// </summary> - float Xppmm { get; set; } - - /// <summary> - /// Gets or sets the pixels per mm on Y direction - /// </summary> - float Yppmm { get; set; } - - /// <summary> - /// Gets or sets the pixels per mm - /// </summary> - SizeF Ppmm { get; set; } - - /// <summary> - /// Gets the printer XY pixel resolution - /// </summary> - decimal XYResolution { get; } - - /// <summary> - /// Gets the printer XY pixel resolution in microns - /// </summary> - decimal XYResolutionUm { get; } - - bool HaveAntiAliasing { get; } - - /// <summary> - /// Gets or sets the AntiAliasing level - /// </summary> - byte AntiAliasing { get; set; } - - /// <summary> - /// Gets Layer Height in mm - /// </summary> - float LayerHeight { get; set; } - - /// <summary> - /// Gets or sets the print height in mm - /// </summary> - float PrintHeight { get; set; } - - /// <summary> - /// Gets the last layer index - /// </summary> - uint LastLayerIndex { get; } - - #region Universal Properties - - /// <summary> - /// Gets if this format support per layer override settings - /// </summary> - bool SupportPerLayerSettings { get; } - - /// <summary> - /// Gets the number of layers present in this file - /// </summary> - uint LayerCount { get; set; } - - /// <summary> - /// Gets or sets the number of initial layer count - /// </summary> - ushort BottomLayerCount { get; set; } - - /// <summary> - /// Gets the number of normal layer count - /// </summary> - uint NormalLayerCount { get; } - - /// <summary> - /// Gets or sets the initial exposure time for <see cref="BottomLayerCount"/> in seconds - /// </summary> - float BottomExposureTime { get; set; } - - /// <summary> - /// Gets or sets the normal layer exposure time in seconds - /// </summary> - float ExposureTime { get; set; } - - /// <summary> - /// Gets or sets the bottom layer off time in seconds - /// </summary> - float BottomLightOffDelay { get; set; } - - /// <summary> - /// Gets or sets the layer off time in seconds - /// </summary> - float LightOffDelay { get; set; } - - /// <summary> - /// Gets or sets the bottom lift height in mm - /// </summary> - float BottomLiftHeight { get; set; } - - /// <summary> - /// Gets or sets the lift height in mm - /// </summary> - float LiftHeight { get; set; } - - /// <summary> - /// Gets or sets the bottom lift speed in mm/min - /// </summary> - float BottomLiftSpeed { get; set; } - - /// <summary> - /// Gets or sets the speed in mm/min - /// </summary> - float LiftSpeed { get; set; } - - /// <summary> - /// Gets the speed in mm/min for the retracts - /// </summary> - float RetractSpeed { get; set; } - - /// <summary> - /// Gets or sets the bottom pwm value from 0 to 255 - /// </summary> - byte BottomLightPWM { get; set; } - - /// <summary> - /// Gets or sets the pwm value from 0 to 255 - /// </summary> - byte LightPWM { get; set; } - #endregion - - /// <summary> - /// Gets the estimate print time in seconds - /// </summary> - float PrintTime { get; set; } - - /// <summary> - /// Gets the estimate print time in seconds, if print doesn't support it it will be calculated - /// </summary> - float PrintTimeOrComputed { get; } - - /// <summary> - /// Gets the calculated estimate print time in seconds - /// </summary> - float PrintTimeComputed { get; } - - /// <summary> - /// Gets the estimate print time in hours - /// </summary> - float PrintTimeHours { get; } - - /// <summary> - /// Gets the estimate print time in hours and minutes formatted - /// </summary> - string PrintTimeString { get; } - - /// <summary> - /// Gets the estimate used material in ml - /// </summary> - float MaterialMilliliters { get; set; } - - /// <summary> - /// Gets the estimate material in grams - /// </summary> - float MaterialGrams { get; set; } - - /// <summary> - /// Gets the estimate material cost - /// </summary> - float MaterialCost { get; set; } - - /// <summary> - /// Gets the material name - /// </summary> - string MaterialName { get; set; } - - /// <summary> - /// Gets the machine name - /// </summary> - string MachineName { get; set; } - - /// <summary> - /// Gets the GCode, returns null if not supported - /// </summary> - StringBuilder GCode { get; set; } - string GCodeStr { get; } - - /// <summary> - /// Gets if this file have available gcode - /// </summary> - bool HaveGCode { get; } - - /// <summary> - /// Get all configuration objects with properties and values - /// </summary> - object[] Configs { get; } - - /// <summary> - /// Gets if this file is valid to read - /// </summary> - bool IsValid { get; } - - #endregion - - #region Methods - /// <summary> - /// Clears all definitions and properties, it also dispose valid candidates - /// </summary> - void Clear(); - - /// <summary> - /// Validate if a file is a valid <see cref="FileFormat"/> - /// </summary> - /// <param name="fileFullPath">Full file path</param> - void FileValidation(string fileFullPath); - - /// <summary> - /// Checks if a extension is valid under the <see cref="FileFormat"/> - /// </summary> - /// <param name="extension">Extension to check</param> - /// <param name="isFilePath">True if <see cref="extension"/> is a full file path, otherwise false for extension only</param> - /// <returns>True if valid, otherwise false</returns> - bool IsExtensionValid(string extension, bool isFilePath = false); - - /// <summary> - /// Gets all valid file extensions in a specified format - /// </summary> - - string GetFileExtensions(string prepend = ".", string separator = ", "); - - /// <summary> - /// Gets a thumbnail by it height or lower - /// </summary> - /// <param name="maxHeight">Max height allowed</param> - /// <returns></returns> - Mat GetThumbnail(uint maxHeight = 400); - - /// <summary> - /// Gets a thumbnail by the largest or smallest - /// </summary> - /// <param name="largest">True to get the largest, otherwise false</param> - /// <returns></returns> - Mat GetThumbnail(bool largest); - - /// <summary> - /// Sets thumbnails from a list of thumbnails and clone them - /// </summary> - /// <param name="images"></param> - void SetThumbnails(Mat[] images); - - /// <summary> - /// Sets all thumbnails the same image - /// </summary> - /// <param name="images">Image to set</param> - void SetThumbnails(Mat images); - - /// <summary> - /// Sets a thumbnail from a disk file - /// </summary> - /// <param name="index">Thumbnail index</param> - /// <param name="filePath"></param> - void SetThumbnail(int index, string filePath); - - /// <summary> - /// Encode to an output file - /// </summary> - /// <param name="fileFullPath">Output file</param> - /// <param name="progress"></param> - void Encode(string fileFullPath, OperationProgress progress = null); - void AfterEncode(); - - /* - /// <summary> - /// Begin encode to an output file - /// </summary> - /// <param name="fileFullPath">Output file</param> - //void BeginEncode(string fileFullPath); - - /// <summary> - /// Insert a layer image to be encoded - /// </summary> - /// <param name="image"></param> - /// <param name="layerIndex"></param> - //void InsertLayerImageEncode(Image<L8> image, uint layerIndex); - - /// <summary> - /// Finish the encoding procedure - /// </summary> - //void EndEncode();*/ - - /// <summary> - /// Decode a slicer file - /// </summary> - /// <param name="fileFullPath"></param> - /// <param name="progress"></param> - void Decode(string fileFullPath, OperationProgress progress = null); - - /// <summary> - /// Extract contents to a folder - /// </summary> - /// <param name="path">Path to folder where content will be extracted</param> - /// <param name="genericConfigExtract"></param> - /// <param name="genericLayersExtract"></param> - /// <param name="progress"></param> - void Extract(string path, bool genericConfigExtract = true, bool genericLayersExtract = true, - OperationProgress progress = null); - - /// <summary> - /// Get height in mm from layer height - /// </summary> - /// <param name="layerIndex"></param> - /// <param name="realHeight"></param> - /// <returns>The height in mm</returns> - float GetHeightFromLayer(uint layerIndex, bool realHeight = true); - - /// <summary> - /// Gets the value for initial layer or normal layers based on layer index - /// </summary> - /// <typeparam name="T">Type of value</typeparam> - /// <param name="layerIndex">Layer index</param> - /// <param name="initialLayerValue">Initial value</param> - /// <param name="normalLayerValue">Normal value</param> - /// <returns></returns> - T GetInitialLayerValueOrNormal<T>(uint layerIndex, T initialLayerValue, T normalLayerValue); - - void RefreshPrintParametersModifiersValues(); - void RefreshPrintParametersPerLayerModifiersValues(uint layerIndex); - - /// <summary> - /// Gets the value attributed to <see cref="FileFormat.PrintParameterModifier"/> - /// </summary> - /// <param name="modifier">Modifier to use</param> - /// <returns>A value</returns> - object GetValueFromPrintParameterModifier(FileFormat.PrintParameterModifier modifier); - - /// <summary> - /// Sets a property value attributed to <see cref="modifier"/> - /// </summary> - /// <param name="modifier">Modifier to use</param> - /// <param name="value">Value to set</param> - /// <returns>True if set, otherwise false = <see cref="modifier"/> not found</returns> - bool SetValueFromPrintParameterModifier(FileFormat.PrintParameterModifier modifier, decimal value); - - byte SetValuesFromPrintParametersModifiers(); - - void EditPrintParameters(OperationEditParameters operation); - - /// <summary> - /// Rebuilds GCode based on current settings - /// </summary> - void RebuildGCode(); - - /// <summary> - /// Saves current configuration on input file - /// </summary> - /// <param name="progress"></param> - void Save(OperationProgress progress = null); - - /// <summary> - /// Saves current configuration on a copy - /// </summary> - /// <param name="filePath">File path to save copy as, use null to overwrite active file (Same as <see cref="Save"/>)</param> - /// <param name="progress"></param> - void SaveAs(string filePath = null, OperationProgress progress = null); - - /// <summary> - /// Converts this file type to another file type - /// </summary> - /// <param name="to">Target file format</param> - /// <param name="fileFullPath">Output path file</param> - /// <param name="progress"></param> - /// <returns>The converted file if successful, otherwise null</returns> - FileFormat Convert(Type to, string fileFullPath, OperationProgress progress = null); - - /// <summary> - /// Converts this file type to another file type - /// </summary> - /// <param name="to">Target file format</param> - /// <param name="fileFullPath">Output path file</param> - /// <param name="progress"></param> - /// <returns>TThe converted file if successful, otherwise null</returns> - FileFormat Convert(FileFormat to, string fileFullPath, OperationProgress progress = null); - - /// <summary> - /// Validate AntiAlias Level - /// </summary> - byte ValidateAntiAliasingLevel(); - - #endregion - } -} diff --git a/UVtools.Core/FileFormats/ImageFile.cs b/UVtools.Core/FileFormats/ImageFile.cs index 490b286..c97ff1d 100644 --- a/UVtools.Core/FileFormats/ImageFile.cs +++ b/UVtools.Core/FileFormats/ImageFile.cs @@ -76,10 +76,13 @@ namespace UVtools.Core.FileFormats private Mat ImageMat { get; set; } - public override void Decode(string fileFullPath, OperationProgress progress = null) + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { - base.Decode(fileFullPath, progress); + throw new NotSupportedException(); + } + protected override void DecodeInternally(string fileFullPath, OperationProgress progress) + { ImageMat = CvInvoke.Imread(fileFullPath, ImreadModes.Grayscale); const byte startDivisor = 2; for (int i = 0; i < ThumbnailsCount; i++) @@ -106,7 +109,7 @@ namespace UVtools.Core.FileFormats public override FileFormat Convert(Type to, string fileFullPath, OperationProgress progress = null) { - throw new NotImplementedException(); + throw new NotSupportedException(); } diff --git a/UVtools.Core/FileFormats/LGSFile.cs b/UVtools.Core/FileFormats/LGSFile.cs index 84806fc..52f56a1 100644 --- a/UVtools.Core/FileFormats/LGSFile.cs +++ b/UVtools.Core/FileFormats/LGSFile.cs @@ -450,12 +450,9 @@ namespace UVtools.Core.FileFormats return bytes; } - public override void Encode(string fileFullPath, OperationProgress progress = null) - { - progress ??= new OperationProgress(); - progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); - base.Encode(fileFullPath, progress); + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) + { if (ResolutionY >= 2560) // Longer Orange 30 { HeaderSettings.Float_94 = 170; @@ -495,8 +492,6 @@ namespace UVtools.Core.FileFormats } } - AfterEncode(); - Debug.WriteLine("Encode Results:"); Debug.WriteLine(HeaderSettings); Debug.WriteLine("-End-"); @@ -522,10 +517,8 @@ namespace UVtools.Core.FileFormats return mat; } - public override void Decode(string fileFullPath, OperationProgress progress = null) + protected override void DecodeInternally(string fileFullPath, OperationProgress progress) { - base.Decode(fileFullPath, progress); - using (var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read)) { HeaderSettings = Helpers.Deserialize<Header>(inputFile); @@ -570,25 +563,17 @@ namespace UVtools.Core.FileFormats { if (progress.Token.IsCancellationRequested) return; - using (var image = layerData[layerIndex].Decode()) - { - this[layerIndex] = new Layer((uint) layerIndex, image, LayerManager); + using var image = layerData[layerIndex].Decode(); + this[layerIndex] = new Layer((uint) layerIndex, image, LayerManager); - lock (progress.Mutex) - { - progress++; - } + lock (progress.Mutex) + { + progress++; } }); LayerManager.RebuildLayersProperties(); - - - FileFullPath = fileFullPath; - } - - progress.Token.ThrowIfCancellationRequested(); } public override void SaveAs(string filePath = null, OperationProgress progress = null) diff --git a/UVtools.Core/FileFormats/MakerbaseFile.cs b/UVtools.Core/FileFormats/MakerbaseFile.cs index 218e54e..78f678e 100644 --- a/UVtools.Core/FileFormats/MakerbaseFile.cs +++ b/UVtools.Core/FileFormats/MakerbaseFile.cs @@ -150,10 +150,8 @@ namespace UVtools.Core.FileFormats #endregion #region Methods - public override void Encode(string fileFullPath, OperationProgress progress = null) + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { - base.Encode(fileFullPath, progress); - uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings); using (var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write)) { @@ -164,19 +162,15 @@ namespace UVtools.Core.FileFormats } - AfterEncode(); - Debug.WriteLine("Encode Results:"); Debug.WriteLine(HeaderSettings); Debug.WriteLine("-End-"); } - - public override void Decode(string fileFullPath, OperationProgress progress = null) - { - base.Decode(fileFullPath, progress); + protected override void DecodeInternally(string fileFullPath, OperationProgress progress) + { using (var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read)) { //HeaderSettings = Helpers.ByteToType<CbddlpFile.Header>(InputFile); @@ -186,13 +180,7 @@ namespace UVtools.Core.FileFormats { throw new FileLoadException("Not a valid Makerfile file!", fileFullPath); } - - - FileFullPath = fileFullPath; - } - - progress.Token.ThrowIfCancellationRequested(); } public override void SaveAs(string filePath = null, OperationProgress progress = null) diff --git a/UVtools.Core/FileFormats/PHZFile.cs b/UVtools.Core/FileFormats/PHZFile.cs index 4f9a6a3..76b45ea 100644 --- a/UVtools.Core/FileFormats/PHZFile.cs +++ b/UVtools.Core/FileFormats/PHZFile.cs @@ -1017,11 +1017,8 @@ namespace UVtools.Core.FileFormats LayersDefinitions = null; } - public override void Encode(string fileFullPath, OperationProgress progress = null) + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); - base.Encode(fileFullPath, progress); LayersHash.Clear(); /*if (HeaderSettings.EncryptionKey == 0) @@ -1139,8 +1136,6 @@ namespace UVtools.Core.FileFormats outputFile.Seek(0, SeekOrigin.Begin); Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - AfterEncode(); - Debug.WriteLine("Encode Results:"); Debug.WriteLine(HeaderSettings); Debug.WriteLine(Previews[0]); @@ -1149,11 +1144,8 @@ namespace UVtools.Core.FileFormats } } - public override void Decode(string fileFullPath, OperationProgress progress = null) + protected override void DecodeInternally(string fileFullPath, OperationProgress progress) { - base.Decode(fileFullPath, progress); - if(progress is null) progress = new OperationProgress(OperationProgress.StatusGatherLayers, LayerCount); - using (var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read)) { @@ -1167,9 +1159,6 @@ namespace UVtools.Core.FileFormats HeaderSettings.AntiAliasLevel = 1; - FileFullPath = fileFullPath; - - progress.Reset(OperationProgress.StatusDecodeThumbnails, ThumbnailsCount); Debug.Write("Header -> "); Debug.WriteLine(HeaderSettings); @@ -1256,8 +1245,6 @@ namespace UVtools.Core.FileFormats } }); } - - progress.Token.ThrowIfCancellationRequested(); } public override void SaveAs(string filePath = null, OperationProgress progress = null) diff --git a/UVtools.Core/FileFormats/PhotonSFile.cs b/UVtools.Core/FileFormats/PhotonSFile.cs index 4b14faf..c36ec5f 100644 --- a/UVtools.Core/FileFormats/PhotonSFile.cs +++ b/UVtools.Core/FileFormats/PhotonSFile.cs @@ -125,11 +125,12 @@ namespace UVtools.Core.FileFormats for (int i = 0; i < imageLength; i++) { - color = color <= 127 ? 0 : 255; // Sanitize no AA - if (spanMat[i] != color) + //color = color <= 127 ? 0 : 255; // Sanitize no AA + byte thisColor = spanMat[i] <= 127 ? 0 : 255; // Sanitize no AA + if (thisColor != color) { AddRep(); - color = spanMat[i]; + color = thisColor; // Sanitize no AA rep = 1; } else @@ -420,12 +421,10 @@ namespace UVtools.Core.FileFormats return bytes; } - public override void Encode(string fileFullPath, OperationProgress progress = null) - { - progress ??= new OperationProgress(); - progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); - base.Encode(fileFullPath, progress); + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) + { + throw new NotSupportedException("PhotonS is read-only format, please use pws instead!"); //uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings); using (var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write)) { @@ -463,8 +462,6 @@ namespace UVtools.Core.FileFormats } } - AfterEncode(); - Debug.WriteLine("Encode Results:"); Debug.WriteLine(HeaderSettings); Debug.WriteLine("-End-"); @@ -495,10 +492,8 @@ namespace UVtools.Core.FileFormats return mat; } - public override void Decode(string fileFullPath, OperationProgress progress = null) + protected override void DecodeInternally(string fileFullPath, OperationProgress progress) { - base.Decode(fileFullPath, progress); - using (var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read)) { HeaderSettings = Helpers.Deserialize<Header>(inputFile); @@ -546,24 +541,16 @@ namespace UVtools.Core.FileFormats { if (progress.Token.IsCancellationRequested) return; - using (var image = layerData[layerIndex].Decode()) + using var image = layerData[layerIndex].Decode(); + this[layerIndex] = new Layer((uint) layerIndex, image, LayerManager); + lock (progress.Mutex) { - this[layerIndex] = new Layer((uint) layerIndex, image, LayerManager); - lock (progress.Mutex) - { - progress++; - } + progress++; } }); LayerManager.RebuildLayersProperties(); - - - FileFullPath = fileFullPath; - } - - progress.Token.ThrowIfCancellationRequested(); } public override void SaveAs(string filePath = null, OperationProgress progress = null) diff --git a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs index c4b9edc..eafecf9 100644 --- a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs +++ b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs @@ -1048,11 +1048,8 @@ namespace UVtools.Core.FileFormats LayersDefinition = null; } - public override void Encode(string fileFullPath, OperationProgress progress = null) + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); - base.Encode(fileFullPath, progress); LayersHash.Clear(); LayersDefinition = new LayerDefinition(LayerCount); @@ -1124,14 +1121,10 @@ namespace UVtools.Core.FileFormats outputFile.Seek(0, SeekOrigin.Begin); Helpers.SerializeWriteFileStream(outputFile, FileMarkSettings); } - - AfterEncode(); } - public override void Decode(string fileFullPath, OperationProgress progress = null) + protected override void DecodeInternally(string fileFullPath, OperationProgress progress) { - base.Decode(fileFullPath, progress); - using (var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read)) { FileMarkSettings = Helpers.Deserialize<FileMark>(inputFile); @@ -1235,8 +1228,6 @@ namespace UVtools.Core.FileFormats } }); } - - progress.Token.ThrowIfCancellationRequested(); } public override void SaveAs(string filePath = null, OperationProgress progress = null) diff --git a/UVtools.Core/FileFormats/SL1File.cs b/UVtools.Core/FileFormats/SL1File.cs index 005b708..c5fd792 100644 --- a/UVtools.Core/FileFormats/SL1File.cs +++ b/UVtools.Core/FileFormats/SL1File.cs @@ -252,22 +252,30 @@ namespace UVtools.Core.FileFormats public class OutputConfig { - public string Action { get; set; } + public string Action { get; set; } = "print"; public string JobDir { get; set; } public float ExpTime { get; set; } public float ExpTimeFirst { get; set; } - public string FileCreationTimestamp { get; set; } + //public string FileCreationTimestamp { get; set; } + public string FileCreationTimestamp { + get + { + //2021-01-23 at 04:07:36 UTC + var now = DateTime.Now; + return $"{now.Year}-{now.Month:D2}-{now.Day:D2} at {now.Hour:D2}:{now.Minute:D2}:{now.Second:D2} {now.Kind}"; + } + } public float LayerHeight { get; set; } - public string MaterialName { get; set; } + public string MaterialName { get; set; } = About.Software; public ushort NumFade { get; set; } public ushort NumFast { get; set; } public ushort NumSlow { get; set; } - public string PrintProfile { get; set; } + public string PrintProfile { get; set; } = About.Software; public float PrintTime { get; set; } - public string PrinterModel { get; set; } - public string PrinterProfile { get; set; } - public string PrinterVariant { get; set; } - public string PrusaSlicerVersion { get; set; } + public string PrinterModel { get; set; } = "SL1"; + public string PrinterProfile { get; set; } = About.Software; + public string PrinterVariant { get; set; } = "default"; + public string PrusaSlicerVersion { get; set; } = "PrusaSlicer-2.3.0+win64-202101111315"; public float UsedMaterial { get; set; } public override string ToString() @@ -519,10 +527,12 @@ namespace UVtools.Core.FileFormats Statistics.Clear(); } - public override void Encode(string fileFullPath, OperationProgress progress = null) + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { - base.Encode(fileFullPath, progress); - + var filename = fileFullPath; + if (filename.EndsWith(TemporaryFileAppend)) filename = Path.GetFileNameWithoutExtension(filename); // tmp + filename = Path.GetFileNameWithoutExtension(filename); // sl1 + OutputConfigSettings.JobDir = filename; using (ZipArchive outputFile = ZipFile.Open(fileFullPath, ZipArchiveMode.Create)) { var entry = outputFile.CreateEntry("config.ini"); @@ -562,39 +572,28 @@ namespace UVtools.Core.FileFormats foreach (var thumbnail in Thumbnails) { if (ReferenceEquals(thumbnail, null)) continue; - using (var stream = outputFile.CreateEntry($"thumbnail/thumbnail{thumbnail.Width}x{thumbnail.Height}.png").Open()) - { - var vec = new VectorOfByte(); - CvInvoke.Imencode(".png", thumbnail, vec); - stream.WriteBytes(vec.ToArray()); - stream.Close(); - } + using var stream = outputFile.CreateEntry($"thumbnail/thumbnail{thumbnail.Width}x{thumbnail.Height}.png").Open(); + var vec = new VectorOfByte(); + CvInvoke.Imencode(".png", thumbnail, vec); + stream.WriteBytes(vec.ToArray()); + stream.Close(); } for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { progress.Token.ThrowIfCancellationRequested(); Layer layer = this[layerIndex]; - var layerImagePath = $"{Path.GetFileNameWithoutExtension(fileFullPath)}{layerIndex:D5}.png"; + var layerImagePath = $"{filename}{layerIndex:D5}.png"; //layer.Filename = layerImagePath; outputFile.PutFileContent(layerImagePath, layer.CompressedBytes, ZipArchiveMode.Create); progress++; } } - - AfterEncode(); } - - public override void Decode(string fileFullPath, OperationProgress progress = null) - { - base.Decode(fileFullPath, progress); - - progress ??= new OperationProgress(); - progress.Reset(OperationProgress.StatusGatherLayers, LayerCount); - - FileFullPath = fileFullPath; + protected override void DecodeInternally(string fileFullPath, OperationProgress progress) + { PrinterSettings = new Printer(); MaterialSettings = new Material(); PrintSettings = new Print(); @@ -602,7 +601,7 @@ namespace UVtools.Core.FileFormats Statistics.ExecutionTime.Restart(); - using (var inputFile = ZipFile.OpenRead(FileFullPath)) + using (var inputFile = ZipFile.OpenRead(fileFullPath)) { List<string> iniFiles = new(); foreach (ZipArchiveEntry entity in inputFile.Entries) @@ -625,7 +624,7 @@ namespace UVtools.Core.FileFormats foreach (var obj in Configs) { var attribute = obj.GetType().GetProperty(fieldName); - if (ReferenceEquals(attribute, null)) continue; + if (attribute is null || !attribute.CanWrite) continue; //Debug.WriteLine(attribute.Name); Helpers.SetPropertyValue(attribute, obj, keyValue[1]); diff --git a/UVtools.Core/FileFormats/UVJFile.cs b/UVtools.Core/FileFormats/UVJFile.cs index c74f438..f638aea 100644 --- a/UVtools.Core/FileFormats/UVJFile.cs +++ b/UVtools.Core/FileFormats/UVJFile.cs @@ -360,13 +360,8 @@ namespace UVtools.Core.FileFormats JsonSettings.Layers = new List<LayerData>(); } - public override void Encode(string fileFullPath, OperationProgress progress = null) + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); - - base.Encode(fileFullPath, progress); - // Redo layer data JsonSettings.Layers.Clear(); for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) @@ -430,17 +425,11 @@ namespace UVtools.Core.FileFormats progress++; } } - AfterEncode(); } - public override void Decode(string fileFullPath, OperationProgress progress = null) + protected override void DecodeInternally(string fileFullPath, OperationProgress progress) { - base.Decode(fileFullPath, progress); - if(progress is null) progress = new OperationProgress(); - progress.Reset(OperationProgress.StatusGatherLayers, LayerCount); - - FileFullPath = fileFullPath; - using (var inputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Read)) + using (var inputFile = ZipFile.Open(fileFullPath, ZipArchiveMode.Read)) { var entry = inputFile.GetEntry(FileConfigName); if (ReferenceEquals(entry, null)) @@ -466,21 +455,19 @@ namespace UVtools.Core.FileFormats } entry = inputFile.GetEntry(FilePreviewHugeName); - if (!ReferenceEquals(entry, null)) + if (entry is not null) { - using (Stream stream = entry.Open()) - { - Mat image = new Mat(); - CvInvoke.Imdecode(stream.ToArray(), ImreadModes.AnyColor, image); - Thumbnails[1] = image; - stream.Close(); - } + using Stream stream = entry.Open(); + Mat image = new Mat(); + CvInvoke.Imdecode(stream.ToArray(), ImreadModes.AnyColor, image); + Thumbnails[1] = image; + stream.Close(); } for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { entry = inputFile.GetEntry($"{FolderImageName}/{layerIndex:D8}.png"); - if (ReferenceEquals(entry, null)) continue; + if (entry is null) continue; this[layerIndex] = new Layer(layerIndex, entry.Open(), LayerManager) { diff --git a/UVtools.Core/FileFormats/ZCodexFile.cs b/UVtools.Core/FileFormats/ZCodexFile.cs index 208cd29..f29b907 100644 --- a/UVtools.Core/FileFormats/ZCodexFile.cs +++ b/UVtools.Core/FileFormats/ZCodexFile.cs @@ -382,12 +382,8 @@ namespace UVtools.Core.FileFormats LayersSettings.Clear(); } - public override void Encode(string fileFullPath, OperationProgress progress = null) - { - progress ??= new OperationProgress(); - progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); - base.Encode(fileFullPath, progress); - + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) + { float usedMaterial = MaterialMilliliters / LayerCount; ResinMetadataSettings.Layers.Clear(); for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) @@ -471,18 +467,14 @@ namespace UVtools.Core.FileFormats outputFile.PutFileContent("ResinGCodeData", GCode.ToString(), ZipArchiveMode.Create); } - AfterEncode(); } - public override void Decode(string fileFullPath, OperationProgress progress = null) + protected override void DecodeInternally(string fileFullPath, OperationProgress progress) { - base.Decode(fileFullPath, progress); - - FileFullPath = fileFullPath; - using (var inputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Read)) + using (var inputFile = ZipFile.Open(fileFullPath, ZipArchiveMode.Read)) { var entry = inputFile.GetEntry("ResinMetadata"); - if (ReferenceEquals(entry, null)) + if (entry is null) { Clear(); throw new FileLoadException("ResinMetadata not found", fileFullPath); @@ -491,7 +483,7 @@ namespace UVtools.Core.FileFormats ResinMetadataSettings = Helpers.JsonDeserializeObject<ResinMetadata>(entry.Open()); entry = inputFile.GetEntry("UserSettingsData"); - if (ReferenceEquals(entry, null)) + if (entry is null) { Clear(); throw new FileLoadException("UserSettingsData not found", fileFullPath); @@ -500,7 +492,7 @@ namespace UVtools.Core.FileFormats UserSettings = Helpers.JsonDeserializeObject<UserSettingsdata>(entry.Open()); entry = inputFile.GetEntry("ZCodeMetadata"); - if (ReferenceEquals(entry, null)) + if (entry is null) { Clear(); throw new FileLoadException("ZCodeMetadata not found", fileFullPath); @@ -509,7 +501,7 @@ namespace UVtools.Core.FileFormats ZCodeMetadataSettings = Helpers.JsonDeserializeObject<ZCodeMetadata>(entry.Open()); entry = inputFile.GetEntry("ResinGCodeData"); - if (ReferenceEquals(entry, null)) + if (entry is null) { Clear(); throw new FileLoadException("ResinGCodeData not found", fileFullPath); @@ -615,12 +607,9 @@ M106 S0 entry = inputFile.GetEntry("Preview.png"); if (!ReferenceEquals(entry, null)) { - using (Stream stream = entry.Open()) - { - - CvInvoke.Imdecode(stream.ToArray(), ImreadModes.AnyColor, Thumbnails[0]); - stream.Close(); - } + using Stream stream = entry.Open(); + CvInvoke.Imdecode(stream.ToArray(), ImreadModes.AnyColor, Thumbnails[0]); + stream.Close(); } } diff --git a/UVtools.Core/Layer/Layer.cs b/UVtools.Core/Layer/Layer.cs index ebec600..74ca871 100644 --- a/UVtools.Core/Layer/Layer.cs +++ b/UVtools.Core/Layer/Layer.cs @@ -7,17 +7,15 @@ */ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Drawing; -using System.IO.Compression; using System.Linq; using Emgu.CV; using Emgu.CV.CvEnum; -using Emgu.CV.Structure; using Emgu.CV.Util; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; using UVtools.Core.Objects; -using UVtools.Core.Operations; using Stream = System.IO.Stream; namespace UVtools.Core @@ -27,9 +25,24 @@ namespace UVtools.Core /// </summary> public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint> { + #region Members + private byte[] _compressedBytes; + private uint _nonZeroPixelCount; + private Rectangle _boundingRectangle = Rectangle.Empty; + private bool _isModified; + private uint _index; + private float _positionZ; + private float _exposureTime; + private float _lightOffDelay = FileFormat.DefaultLightOffDelay; + private float _liftHeight = FileFormat.DefaultLiftHeight; + private float _liftSpeed = FileFormat.DefaultLiftSpeed; + private float _retractSpeed = FileFormat.DefaultRetractSpeed; + private byte _lightPwm = FileFormat.DefaultLightPWM; + #endregion + #region Properties - public object Mutex = new object(); + public object Mutex = new(); /// <summary> /// Gets the parent layer manager @@ -152,21 +165,35 @@ namespace UVtools.Core public float PositionZ { get => _positionZ; - set => RaiseAndSetIfChanged(ref _positionZ, value); + set + { + if(!RaiseAndSetIfChanged(ref _positionZ, value)) return; + RaisePropertyChanged(nameof(LayerHeight)); + } } - private byte[] _compressedBytes; - private uint _nonZeroPixelCount; - private Rectangle _boundingRectangle = Rectangle.Empty; - private bool _isModified; - private uint _index; - private float _positionZ; - private float _exposureTime; - private float _lightOffDelay = FileFormat.DefaultLightOffDelay; - private float _liftHeight = FileFormat.DefaultLiftHeight; - private float _liftSpeed = FileFormat.DefaultLiftSpeed; - private float _retractSpeed = FileFormat.DefaultRetractSpeed; - private byte _lightPwm = FileFormat.DefaultLightPWM; + /// <summary> + /// Gets the layer height in millimeters of this layer + /// </summary> + public float LayerHeight + { + get + { + if (_index == 0) return _positionZ; + Layer previousLayer = this; + + while ((previousLayer = previousLayer.PreviousLayer()) is not null) // This cycle returns the correct layer height if two or more layers have the same position z + { + var layerHeight = (float)Math.Round(_positionZ - previousLayer.PositionZ, 2); + //Debug.WriteLine($"Layer {_index}-{previousLayer.Index}: {_positionZ} - {previousLayer.PositionZ}: {layerHeight}"); + if (layerHeight == 0f) continue; + if (layerHeight < 0f) break; + return layerHeight; + } + + return ParentLayerManager.SlicerFile.LayerHeight; + } + } /// <summary> /// Gets or sets layer image compressed data @@ -348,7 +375,7 @@ namespace UVtools.Core public override string ToString() { - return $"{nameof(Index)}: {Index}, {nameof(Filename)}: {Filename}, {nameof(NonZeroPixelCount)}: {NonZeroPixelCount}, {nameof(BoundingRectangle)}: {BoundingRectangle}, {nameof(IsBottomLayer)}: {IsBottomLayer}, {nameof(IsNormalLayer)}: {IsNormalLayer}, {nameof(PositionZ)}: {PositionZ}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(LightPWM)}: {LightPWM}, {nameof(IsModified)}: {IsModified}"; + return $"{nameof(Index)}: {Index}, {nameof(Filename)}: {Filename}, {nameof(NonZeroPixelCount)}: {NonZeroPixelCount}, {nameof(BoundingRectangle)}: {BoundingRectangle}, {nameof(IsBottomLayer)}: {IsBottomLayer}, {nameof(IsNormalLayer)}: {IsNormalLayer}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(PositionZ)}: {PositionZ}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(LightPWM)}: {LightPWM}, {nameof(IsModified)}: {IsModified}"; } #endregion @@ -388,18 +415,18 @@ namespace UVtools.Core public Layer PreviousLayer() { - if (ReferenceEquals(ParentLayerManager, null) || Index == 0) + if (ParentLayerManager is null || _index == 0) return null; - return ParentLayerManager[Index - 1]; + return ParentLayerManager[_index - 1]; } public Layer NextLayer() { - if (ReferenceEquals(ParentLayerManager, null) || Index >= ParentLayerManager.Count - 1) + if (ParentLayerManager is null || _index >= ParentLayerManager.Count - 1) return null; - return ParentLayerManager[Index + 1]; + return ParentLayerManager[_index + 1]; } public bool SetValueFromPrintParameterModifier(FileFormat.PrintParameterModifier modifier, decimal value) diff --git a/UVtools.Core/Layer/LayerManager.cs b/UVtools.Core/Layer/LayerManager.cs index a5ec45d..65127c4 100644 --- a/UVtools.Core/Layer/LayerManager.cs +++ b/UVtools.Core/Layer/LayerManager.cs @@ -49,8 +49,14 @@ namespace UVtools.Core } } + /// <summary> + /// Gets the bounding rectangle of the object + /// </summary> private Rectangle _boundingRectangle = Rectangle.Empty; + /// <summary> + /// Gets the bounding rectangle of the object + /// </summary> public Rectangle BoundingRectangle { get => GetBoundingRectangle(); @@ -79,7 +85,7 @@ namespace UVtools.Core } } - public float LayerHeight => Layers[0].PositionZ; + //public float LayerHeight => Layers[0].PositionZ; #endregion @@ -1294,5 +1300,14 @@ namespace UVtools.Core #endregion + + #region Formater + + public override string ToString() + { + return $"{nameof(BoundingRectangle)}: {BoundingRectangle}, {nameof(Count)}: {Count}, {nameof(IsModified)}: {IsModified}"; + } + + #endregion } } diff --git a/UVtools.Core/Objects/StringTag.cs b/UVtools.Core/Objects/StringTag.cs index d19d686..edaea6f 100644 --- a/UVtools.Core/Objects/StringTag.cs +++ b/UVtools.Core/Objects/StringTag.cs @@ -30,7 +30,7 @@ namespace UVtools.Core.Objects public string TagString { - get => Tag.ToString(); + get => Tag?.ToString(); set => Tag = value; } diff --git a/UVtools.Core/Operations/Operation.cs b/UVtools.Core/Operations/Operation.cs index fabeb31..f0d896d 100644 --- a/UVtools.Core/Operations/Operation.cs +++ b/UVtools.Core/Operations/Operation.cs @@ -8,7 +8,6 @@ using System; using System.Drawing; -using System.Reflection.Metadata.Ecma335; using System.Xml.Serialization; using Emgu.CV; using UVtools.Core.FileFormats; @@ -19,13 +18,31 @@ namespace UVtools.Core.Operations [Serializable] public abstract class Operation : BindableBase, IDisposable { + #region Members private Rectangle _roi = Rectangle.Empty; private uint _layerIndexEnd; private uint _layerIndexStart; private string _profileName; private bool _profileIsDefault; private Enumerations.LayerRangeSelection _layerRangeSelection = Enumerations.LayerRangeSelection.All; + private FileFormat _slicerFile; public const byte ClassNameLength = 9; + #endregion + + #region Properties + [XmlIgnore] + public FileFormat SlicerFile + { + get => _slicerFile; + set + { + if(!RaiseAndSetIfChanged(ref _slicerFile, value)) return; + InitWithSlicerFile(); + } + } + + [XmlIgnore] + public object Tag { get; set; } /// <summary> /// Gets the ID name of this operation, this comes from class name with "Operation" removed @@ -43,6 +60,19 @@ namespace UVtools.Core.Operations set => RaiseAndSetIfChanged(ref _layerRangeSelection, value); } + public virtual string LayerRangeString + { + get + { + if (LayerRangeSelection == Enumerations.LayerRangeSelection.None) + { + return $" [Layers: {LayerIndexStart}-{LayerIndexEnd}]"; + } + + return $" [Layers: {LayerRangeSelection}]"; + } + } + /// <summary> /// Gets if this operation should set layer range to the actual layer index on layer preview /// </summary> @@ -96,18 +126,6 @@ namespace UVtools.Core.Operations public bool HaveAction => !string.IsNullOrEmpty(ProgressAction); /// <summary> - /// Validates the operation - /// </summary> - /// <returns>null or empty if validates, or else, return a string with error message</returns> - public virtual StringTag Validate(params object[] parameters) => null; - - public bool CanValidate(params object[] parameters) - { - var result = Validate(parameters); - return result is null || string.IsNullOrEmpty(result.Content); - } - - /// <summary> /// Gets the start layer index where operation will starts in /// </summary> public virtual uint LayerIndexStart @@ -153,6 +171,105 @@ namespace UVtools.Core.Operations } public bool HaveROI => !ROI.IsEmpty; + #endregion + + #region Constructor + protected Operation() { } + + protected Operation(FileFormat slicerFile) + { + _slicerFile = slicerFile; + SelectAllLayers(); + InitWithSlicerFile(); + } + #endregion + + #region Methods + /// <summary> + /// Validates the operation + /// </summary> + /// <returns>null or empty if validates, or else, return a string with error message</returns> + public virtual StringTag Validate(params object[] parameters) => null; + + public bool CanValidate(params object[] parameters) + { + var result = Validate(parameters); + return result is null || string.IsNullOrEmpty(result.Content); + } + + public void SelectAllLayers() + { + LayerIndexStart = 0; + LayerIndexEnd = SlicerFile.LastLayerIndex; + LayerRangeSelection = Enumerations.LayerRangeSelection.All; + } + + public void SelectCurrentLayer(uint layerIndex) + { + LayerIndexStart = LayerIndexEnd = layerIndex; + LayerRangeSelection = Enumerations.LayerRangeSelection.Current; + } + + public void SelectBottomLayers() + { + LayerIndexStart = 0; + LayerIndexEnd = SlicerFile.BottomLayerCount - 1u; + LayerRangeSelection = Enumerations.LayerRangeSelection.Bottom; + } + + public void SelectNormalLayers() + { + LayerIndexStart = SlicerFile.BottomLayerCount; + LayerIndexEnd = SlicerFile.LastLayerIndex; + LayerRangeSelection = Enumerations.LayerRangeSelection.Normal; + } + + public void SelectFirstLayer() + { + LayerIndexStart = LayerIndexEnd = 0; + LayerRangeSelection = Enumerations.LayerRangeSelection.First; + } + + public void SelectLastLayer() + { + LayerIndexStart = LayerIndexEnd = SlicerFile.LastLayerIndex; + LayerRangeSelection = Enumerations.LayerRangeSelection.Last; + } + + public void SelectLayers(Enumerations.LayerRangeSelection range) + { + switch (range) + { + case Enumerations.LayerRangeSelection.None: + break; + case Enumerations.LayerRangeSelection.All: + SelectAllLayers(); + break; + case Enumerations.LayerRangeSelection.Current: + //SelectCurrentLayer(); + break; + case Enumerations.LayerRangeSelection.Bottom: + SelectBottomLayers(); + break; + case Enumerations.LayerRangeSelection.Normal: + SelectNormalLayers(); + break; + case Enumerations.LayerRangeSelection.First: + SelectFirstLayer(); + break; + case Enumerations.LayerRangeSelection.Last: + SelectLastLayer(); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + + /// <summary> + /// Called to init the object when <see cref="SlicerFile"/> changes + /// </summary> + public virtual void InitWithSlicerFile() { } public Mat GetRoiOrDefault(Mat defaultMat) { @@ -161,7 +278,9 @@ namespace UVtools.Core.Operations public virtual Operation Clone() { - return MemberwiseClone() as Operation; + var operation = MemberwiseClone() as Operation; + operation.SlicerFile = _slicerFile; + return operation; } public override string ToString() @@ -171,23 +290,23 @@ namespace UVtools.Core.Operations var result = $"{Title}: {LayerRangeString}"; return result; } + - public virtual string LayerRangeString + protected virtual bool ExecuteInternally(OperationProgress progress) { - get - { - if (LayerRangeSelection == Enumerations.LayerRangeSelection.None) - { - return $" [Layers: {LayerIndexStart}-{LayerIndexEnd}]"; - } - - return $" [Layers: {LayerRangeSelection}]"; - } + throw new NotImplementedException(); } - public virtual bool Execute(FileFormat slicerFile, OperationProgress progress = null) + public bool Execute(OperationProgress progress = null) { - throw new NotImplementedException(); + if (_slicerFile is null) throw new InvalidOperationException($"{Title} can't execute due the lacking of file parent."); + progress ??= new OperationProgress(); + progress.Reset(ProgressAction, LayerRangeCount); + + var result = ExecuteInternally(progress); + + progress.Token.ThrowIfCancellationRequested(); + return result; } public virtual bool Execute(Mat mat, params object[] arguments) @@ -196,5 +315,6 @@ namespace UVtools.Core.Operations } public virtual void Dispose() { } + #endregion } } diff --git a/UVtools.Core/Operations/OperationArithmetic.cs b/UVtools.Core/Operations/OperationArithmetic.cs index fc6fe69..c4ef8dd 100644 --- a/UVtools.Core/Operations/OperationArithmetic.cs +++ b/UVtools.Core/Operations/OperationArithmetic.cs @@ -113,6 +113,14 @@ namespace UVtools.Core.Operations public bool IsValid => SetLayers.Count > 0 & Operations.Count > 0; #endregion + #region Constructor + + public OperationArithmetic() { } + + public OperationArithmetic(FileFormat slicerFile) : base(slicerFile) { } + + #endregion + #region Methods public bool Parse() @@ -205,17 +213,16 @@ namespace UVtools.Core.Operations return true; } - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { if (!IsValid) return false; - progress ??= new OperationProgress(); - progress.Reset(ProgressAction, (uint)Operations.Count); - using Mat result = slicerFile[Operations[0].LayerIndex].LayerMat; + using Mat result = SlicerFile[Operations[0].LayerIndex].LayerMat; Mat resultRoi = GetRoiOrDefault(result); for (int i = 1; i < Operations.Count; i++) { - using var image = slicerFile[Operations[i].LayerIndex].LayerMat; + progress.Token.ThrowIfCancellationRequested(); + using var image = SlicerFile[Operations[i].LayerIndex].LayerMat; Mat imageRoi = GetRoiOrDefault(image); switch (Operations[i - 1].Operator) { @@ -245,18 +252,19 @@ namespace UVtools.Core.Operations Parallel.ForEach(SetLayers, layerIndex => { + if (progress.Token.IsCancellationRequested) return; if (Operations.Count == 1 && HaveROI) { - var mat = slicerFile[layerIndex].LayerMat; + var mat = SlicerFile[layerIndex].LayerMat; var matRoi = GetRoiOrDefault(mat); resultRoi.CopyTo(matRoi); - slicerFile[layerIndex].LayerMat = mat; + SlicerFile[layerIndex].LayerMat = mat; return; } - slicerFile[layerIndex].LayerMat = result; + SlicerFile[layerIndex].LayerMat = result; }); - return true; + return !progress.Token.IsCancellationRequested; } #endregion diff --git a/UVtools.Core/Operations/OperationBlur.cs b/UVtools.Core/Operations/OperationBlur.cs index a5237e6..bc9b456 100644 --- a/UVtools.Core/Operations/OperationBlur.cs +++ b/UVtools.Core/Operations/OperationBlur.cs @@ -124,28 +124,33 @@ namespace UVtools.Core.Operations #endregion + #region Constructor + + public OperationBlur() { } + + public OperationBlur(FileFormat slicerFile) : base(slicerFile) { } + + #endregion + #region Methods - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) - { - progress ??= new OperationProgress(); - progress.Reset(ProgressAction, LayerRangeCount); + protected override bool ExecuteInternally(OperationProgress progress) + { Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex => { if (progress.Token.IsCancellationRequested) return; - using (var mat = slicerFile[layerIndex].LayerMat) + using (var mat = SlicerFile[layerIndex].LayerMat) { Execute(mat); - slicerFile[layerIndex].LayerMat = mat; + SlicerFile[layerIndex].LayerMat = mat; } lock (progress.Mutex) { progress++; } }); - progress.Token.ThrowIfCancellationRequested(); - return true; + return !progress.Token.IsCancellationRequested; } public override bool Execute(Mat mat, params object[] arguments) diff --git a/UVtools.Core/Operations/OperationCalculator.cs b/UVtools.Core/Operations/OperationCalculator.cs index 0070d17..68aa251 100644 --- a/UVtools.Core/Operations/OperationCalculator.cs +++ b/UVtools.Core/Operations/OperationCalculator.cs @@ -8,6 +8,7 @@ using System; using System.Drawing; +using UVtools.Core.FileFormats; using UVtools.Core.Objects; namespace UVtools.Core.Operations @@ -15,6 +16,7 @@ namespace UVtools.Core.Operations [Serializable] public class OperationCalculator : Operation { + #region Overrides public override string Title => "Calculator"; public override string Description => null; @@ -28,15 +30,31 @@ namespace UVtools.Core.Operations public override bool CanROI => false; public override bool CanHaveProfiles => false; + #endregion + #region Properties public MillimetersToPixels CalcMillimetersToPixels { get; set; } public LightOffDelayC CalcLightOffDelay { get; set; } public OptimalModelTilt CalcOptimalModelTilt { get; set; } + #endregion - public OperationCalculator() + #region Constructor + + public OperationCalculator() { } + + public OperationCalculator(FileFormat slicerFile) : base(slicerFile) { + CalcMillimetersToPixels = new MillimetersToPixels(slicerFile.Resolution, slicerFile.Display); + CalcLightOffDelay = new LightOffDelayC( + (decimal) SlicerFile.LiftHeight, (decimal) slicerFile.BottomLiftHeight, + (decimal) SlicerFile.LiftSpeed, (decimal) slicerFile.BottomLiftSpeed, + (decimal) SlicerFile.RetractSpeed, (decimal) slicerFile.RetractSpeed); + CalcOptimalModelTilt = new OptimalModelTilt(slicerFile.Resolution, slicerFile.Display, + (decimal) slicerFile.LayerHeight); } + #endregion + public abstract class Calculation : BindableBase { public abstract string Description { get; } diff --git a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs index b4e93e9..0b3f789 100644 --- a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs +++ b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs @@ -26,7 +26,6 @@ namespace UVtools.Core.Operations public sealed class OperationCalibrateElephantFoot : Operation { #region Members - private Size _resolution = Size.Empty; private decimal _layerHeight = 0.05M; private bool _syncLayers; private ushort _bottomLayers = 10; @@ -112,14 +111,6 @@ namespace UVtools.Core.Operations #region Properties - [XmlIgnore] - public Size Resolution - { - get => _resolution; - set => RaiseAndSetIfChanged(ref _resolution, value); - } - - public decimal LayerHeight { get => _layerHeight; @@ -347,6 +338,21 @@ namespace UVtools.Core.Operations #endregion + #region Constructor + + public OperationCalibrateElephantFoot() { } + + public OperationCalibrateElephantFoot(FileFormat slicerFile) : base(slicerFile) + { + _layerHeight = (decimal)slicerFile.LayerHeight; + //_bottomLayers = slicerFile.BottomLayerCount; + _bottomExposure = (decimal)slicerFile.BottomExposureTime; + _normalExposure = (decimal)slicerFile.ExposureTime; + _mirrorOutput = slicerFile.MirrorDisplay; + } + + #endregion + #region Equality private bool Equals(OperationCalibrateElephantFoot other) @@ -400,7 +406,7 @@ namespace UVtools.Core.Operations var anchor = new Point(-1, -1); var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor); - layers[0] = EmguExtensions.InitMat(Resolution); + layers[0] = EmguExtensions.InitMat(SlicerFile.Resolution); LineType lineType = _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected; int length = (int) (250 * _partScale); int triangleLength = (int) (50 * _partScale); @@ -633,32 +639,25 @@ namespace UVtools.Core.Operations return thumbnail; } - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(ProgressAction, 3); - slicerFile.SuppressRebuildProperties = true; - + progress.ItemCount = 3; + var newLayers = new Layer[LayerCount]; - slicerFile.LayerHeight = (float)LayerHeight; - slicerFile.BottomExposureTime = (float)BottomExposure; - slicerFile.ExposureTime = (float)NormalExposure; - slicerFile.BottomLayerCount = BottomLayers; - var layers = GetLayers(); progress++; - var bottomLayer = new Layer(0, layers[0], slicerFile.LayerManager) + var bottomLayer = new Layer(0, layers[0], SlicerFile.LayerManager) { IsModified = true }; - var layer = new Layer(0, layers[1], slicerFile.LayerManager) + var layer = new Layer(0, layers[1], SlicerFile.LayerManager) { IsModified = true }; - var moveOp = new OperationMove(bottomLayer.BoundingRectangle, layers[0].Size); + var moveOp = new OperationMove(SlicerFile, bottomLayer.BoundingRectangle); moveOp.Execute(layers[0]); moveOp.Execute(layers[1]); @@ -671,7 +670,7 @@ namespace UVtools.Core.Operations layerIndex < LayerCount; layerIndex++) { - newLayers[layerIndex] = slicerFile.GetInitialLayerValueOrNormal(layerIndex, bottomLayer.Clone(), layer.Clone()); + newLayers[layerIndex] = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, bottomLayer.Clone(), layer.Clone()); } foreach (var mat in layers) @@ -680,16 +679,22 @@ namespace UVtools.Core.Operations } - if (slicerFile.ThumbnailsCount > 0) - slicerFile.SetThumbnails(GetThumbnail()); + if (SlicerFile.ThumbnailsCount > 0) + SlicerFile.SetThumbnails(GetThumbnail()); progress++; - slicerFile.LayerManager.Layers = newLayers; - slicerFile.SuppressRebuildProperties = false; - slicerFile.LayerManager.RebuildLayersProperties(); + SlicerFile.SuppressRebuildProperties = true; + SlicerFile.LayerHeight = (float)LayerHeight; + SlicerFile.BottomExposureTime = (float)BottomExposure; + SlicerFile.ExposureTime = (float)NormalExposure; + SlicerFile.BottomLayerCount = BottomLayers; + + SlicerFile.LayerManager.Layers = newLayers; + SlicerFile.LayerManager.RebuildLayersProperties(); + SlicerFile.SuppressRebuildProperties = false; - return true; + return !progress.Token.IsCancellationRequested; } #endregion diff --git a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs new file mode 100644 index 0000000..183d20e --- /dev/null +++ b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs @@ -0,0 +1,1015 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Emgu.CV; +using Emgu.CV.CvEnum; +using Emgu.CV.Structure; +using UVtools.Core.Extensions; +using UVtools.Core.FileFormats; +using UVtools.Core.Objects; + +namespace UVtools.Core.Operations +{ + [Serializable] + public sealed class OperationCalibrateExposureFinder : Operation + { + #region Sub classes + + [Serializable] + public sealed class ExposureItem : BindableBase, IComparable<ExposureItem> + { + private decimal _layerHeight; + private decimal _bottomExposure; + private decimal _exposure; + + + /// <summary> + /// Gets or sets the layer height in millimeters + /// </summary> + public decimal LayerHeight + { + get => _layerHeight; + set => RaiseAndSetIfChanged(ref _layerHeight, Math.Round(value, 2)); + } + + + /// <summary> + /// Gets or sets the bottom exposure in seconds + /// </summary> + public decimal BottomExposure + { + get => _bottomExposure; + set => RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2)); + } + + /// <summary> + /// Gets or sets the bottom exposure in seconds + /// </summary> + public decimal Exposure + { + get => _exposure; + set => RaiseAndSetIfChanged(ref _exposure, Math.Round(value, 2)); + } + + public bool IsValid => _layerHeight > 0 && _bottomExposure > 0 && _exposure > 0; + + public ExposureItem() { } + + public ExposureItem(decimal layerHeight, decimal bottomExposure = 0, decimal exposure = 0) + { + _layerHeight = Math.Round(layerHeight, 2); + _bottomExposure = Math.Round(bottomExposure, 2); + _exposure = Math.Round(exposure, 2); + } + + public override string ToString() + { + return $"{nameof(LayerHeight)}: {LayerHeight}mm, {nameof(BottomExposure)}: {BottomExposure}s, {nameof(Exposure)}: {Exposure}s"; + } + + private bool Equals(ExposureItem other) + { + return _layerHeight == other._layerHeight && _bottomExposure == other._bottomExposure && _exposure == other._exposure; + } + + public override bool Equals(object obj) + { + return ReferenceEquals(this, obj) || obj is ExposureItem other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(_layerHeight, _bottomExposure, _exposure); + } + + public int CompareTo(ExposureItem other) + { + if (ReferenceEquals(this, other)) return 0; + if (ReferenceEquals(null, other)) return 1; + var layerHeightComparison = _layerHeight.CompareTo(other._layerHeight); + if (layerHeightComparison != 0) return layerHeightComparison; + var bottomExposureComparison = _bottomExposure.CompareTo(other._bottomExposure); + if (bottomExposureComparison != 0) return bottomExposureComparison; + return _exposure.CompareTo(other._exposure); + } + } + + #endregion + + #region Constants + + const byte TextSpacing = 60; + const byte TextLineBreak = 30; + const FontFace FontFace = Emgu.CV.CvEnum.FontFace.HersheyDuplex; + const byte TextStartX = 10; + //const byte TextStartY = 50; + const double TextScale = 0.8; + const byte TextThickness = 2; + + #endregion + + #region Members + private decimal _displayWidth; + private decimal _displayHeight; + private decimal _layerHeight = 0.05M; + private ushort _bottomLayers = 3; + private decimal _bottomExposure = 60; + private decimal _normalExposure = 12; + private decimal _topBottomMargin = 5; + private decimal _leftRightMargin = 5; + private byte _chamferLayers = 0; + private byte _erodeBottomIterations = 0; + private decimal _partMargin = 0; + private bool _enableAntiAliasing = true; + private bool _mirrorOutput; + private decimal _baseHeight = 1; + private decimal _cylinderHeight = 1; + private decimal _cylinderMargin = 1.5m; + private Measures _unitOfMeasure = Measures.Millimeters; + private string _cylinderDiametersMm = "0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0"; + private string _cylinderDiametersPx = "1, 2, 3, 4, 5, 7, 10 ,15, 20"; + private bool _multipleLayerHeight; + private decimal _multipleLayerHeightMaximum = 0.1m; + private decimal _multipleLayerHeightStep = 0.01m; + private bool _multipleExposures; + private Shapes _shape = Shapes.Circle; + private ExposureGenTypes _exposureGenType = ExposureGenTypes.Linear; + private bool _exposureGenIgnoreBaseExposure; + private decimal _exposureGenBottomStep = 0.5m; + private decimal _exposureGenNormalStep = 0.2m; + private byte _exposureGenTests = 4; + private decimal _exposureGenManualLayerHeight; + private decimal _exposureGenManualBottom; + private decimal _exposureGenManualNormal; + private ObservableCollection<ExposureItem> _exposureTable = new(); + + #endregion + + #region Overrides + + public override bool CanROI => false; + + //public override bool CanCancel => false; + + public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None; + + public override string Title => "Exposure time finder"; + public override string Description => + "Generates test models with various strategies and increments to verify the best exposure time for a given layer height.\n" + + "You must repeat this test when change any of the following: printer, LEDs, resin and exposure times.\n" + + "Note: The current opened file will be overwritten with this test, use a dummy or a not needed file."; + + public override string ConfirmationText => + $"generate the exposure time finder test?"; + + public override string ProgressTitle => + $"Generating the exposure time finder test"; + + public override string ProgressAction => "Generated layers"; + + public override StringTag Validate(params object[] parameters) + { + var sb = new StringBuilder(); + + if (_displayWidth <= 0) + { + sb.AppendLine("Display width must be a positive value."); + } + + if (_displayHeight <= 0) + { + sb.AppendLine("Display height must be a positive value."); + } + + if (Cylinders.Length <= 0) + { + sb.AppendLine("No objects to output."); + } + + if (_multipleExposures) + { + var endLayerHeight = _multipleLayerHeight ? _multipleLayerHeightMaximum : _layerHeight; + for (decimal layerHeight = _layerHeight; + layerHeight <= endLayerHeight; + layerHeight += _multipleLayerHeightStep) + { + bool found = false; + foreach (var exposureItem in _exposureTable) + { + if (exposureItem.LayerHeight == layerHeight && exposureItem.IsValid) + { + found = true; + break; + } + } + if(!found) + sb.AppendLine($"[ME]: {layerHeight:F2}mm layer height have no exposure(s)."); + } + } + + return new StringTag(sb.ToString()); + } + + public override string ToString() + { + var result = $"[Layer Height: {_layerHeight}] " + + $"[Bottom layers: {_bottomLayers}] " + + $"[Exposure: {_bottomExposure}/{_normalExposure}] " + + $"[TB:{_topBottomMargin} LR:{_leftRightMargin} PM:{_partMargin} CM:{_cylinderMargin}] " + + $"[Chamfer: {_chamferLayers}] [Erode: {_erodeBottomIterations}] " + + $"[Obj height: {_cylinderHeight}] " + + $"[Cylinders: {Cylinders.Length}] " + + $"[AA: {_enableAntiAliasing}] [Mirror: {_mirrorOutput}]"; + if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}"; + return result; + } + + #endregion + + #region Properties + + public decimal DisplayWidth + { + get => _displayWidth; + set + { + if(!RaiseAndSetIfChanged(ref _displayWidth, Math.Round(value, 2))) return; + RaisePropertyChanged(nameof(Xppmm)); + } + } + + public decimal DisplayHeight + { + get => _displayHeight; + set + { + if(!RaiseAndSetIfChanged(ref _displayHeight, Math.Round(value, 2))) return; + RaisePropertyChanged(nameof(Yppmm)); + } + } + + public decimal Xppmm => DisplayWidth > 0 ? Math.Round(SlicerFile.Resolution.Width / DisplayWidth, 2) : 0; + public decimal Yppmm => DisplayHeight > 0 ? Math.Round(SlicerFile.Resolution.Height / DisplayHeight, 2) : 0; + public decimal Ppmm => Math.Max(Xppmm, Yppmm); + + public decimal LayerHeight + { + get => _layerHeight; + set + { + if(!RaiseAndSetIfChanged(ref _layerHeight, Math.Round(value, 2))) return; + RaisePropertyChanged(nameof(BottomLayersMM)); + RaisePropertyChanged(nameof(AvailableLayerHeights)); + } + } + + public ushort Microns => (ushort)(LayerHeight * 1000); + + public ushort BottomLayers + { + get => _bottomLayers; + set + { + if(!RaiseAndSetIfChanged(ref _bottomLayers, value)) return; + RaisePropertyChanged(nameof(BottomLayersMM)); + } + } + + public decimal BottomLayersMM => Math.Round(LayerHeight * BottomLayers, 2); + + public decimal BottomExposure + { + get => _bottomExposure; + set => RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2)); + } + + public decimal NormalExposure + { + get => _normalExposure; + set => RaiseAndSetIfChanged(ref _normalExposure, Math.Round(value, 2)); + } + + public decimal TopBottomMargin + { + get => _topBottomMargin; + set => RaiseAndSetIfChanged(ref _topBottomMargin, Math.Round(value, 2)); + } + + public decimal LeftRightMargin + { + get => _leftRightMargin; + set => RaiseAndSetIfChanged(ref _leftRightMargin, Math.Round(value, 2)); + } + + public byte ChamferLayers + { + get => _chamferLayers; + set => RaiseAndSetIfChanged(ref _chamferLayers, value); + } + + public byte ErodeBottomIterations + { + get => _erodeBottomIterations; + set => RaiseAndSetIfChanged(ref _erodeBottomIterations, value); + } + + public decimal PartMargin + { + get => _partMargin; + set => RaiseAndSetIfChanged(ref _partMargin, Math.Round(value, 2)); + } + + public bool EnableAntiAliasing + { + get => _enableAntiAliasing; + set => RaiseAndSetIfChanged(ref _enableAntiAliasing, value); + } + + public bool MirrorOutput + { + get => _mirrorOutput; + set => RaiseAndSetIfChanged(ref _mirrorOutput, value); + } + + public decimal BaseHeight + { + get => _baseHeight; + set => RaiseAndSetIfChanged(ref _baseHeight, Math.Round(value, 2)); + } + + public decimal CylinderHeight + { + get => _cylinderHeight; + set => RaiseAndSetIfChanged(ref _cylinderHeight, Math.Round(value, 2)); + } + + public decimal CylinderMargin + { + get => _cylinderMargin; + set => RaiseAndSetIfChanged(ref _cylinderMargin, Math.Round(value, 2)); + } + + public decimal TotalHeight => BaseHeight + CylinderHeight; + + public Measures UnitOfMeasure + { + get => _unitOfMeasure; + set + { + if (!RaiseAndSetIfChanged(ref _unitOfMeasure, value)) return; + RaisePropertyChanged(nameof(IsUnitOfMeasureMm)); + } + } + + public bool IsUnitOfMeasureMm => _unitOfMeasure == Measures.Millimeters; + + public string CylinderDiametersMm + { + get => _cylinderDiametersMm; + set => RaiseAndSetIfChanged(ref _cylinderDiametersMm, value); + } + + public string CylinderDiametersPx + { + get => _cylinderDiametersPx; + set => RaiseAndSetIfChanged(ref _cylinderDiametersPx, value); + } + + /// <summary> + /// Gets all cylinders in pixels and ordered + /// </summary> + public int[] Cylinders + { + get + { + List<int> cylinders = new(); + + if (_unitOfMeasure == Measures.Millimeters) + { + var split = _cylinderDiametersMm.Split(',', StringSplitOptions.TrimEntries); + foreach (var mmStr in split) + { + if (string.IsNullOrWhiteSpace(mmStr)) continue; + if (!decimal.TryParse(mmStr, out var mm)) continue; + if(mm <= 0) continue; + var mmPx = (int) Math.Floor(mm * Ppmm); + if(cylinders.Contains(mmPx)) continue; + cylinders.Add((int)Math.Floor(mm * Ppmm)); + } + } + else + { + var split = _cylinderDiametersPx.Split(',', StringSplitOptions.TrimEntries); + foreach (var pxStr in split) + { + if (string.IsNullOrWhiteSpace(pxStr)) continue; + if (!int.TryParse(pxStr, out var px)) continue; + if (px <= 0) continue; + if (cylinders.Contains(px)) continue; + cylinders.Add(px); + } + } + + return cylinders.OrderBy(pixels => pixels).ToArray(); + } + } + + public Shapes Shape + { + get => _shape; + set => RaiseAndSetIfChanged(ref _shape, value); + } + + public int GetCylindersLength(int[] cylinders) + { + return (int) (cylinders.Sum() + (cylinders.Length-1) * _cylinderMargin * Yppmm); + } + + public bool MultipleLayerHeight + { + get => _multipleLayerHeight; + set + { + if(!RaiseAndSetIfChanged(ref _multipleLayerHeight, value)) return; + RaisePropertyChanged(nameof(AvailableLayerHeights)); + } + } + + public decimal MultipleLayerHeightMaximum + { + get => _multipleLayerHeightMaximum; + set + { + if(!RaiseAndSetIfChanged(ref _multipleLayerHeightMaximum, value)) return; + RaisePropertyChanged(nameof(AvailableLayerHeights)); + } + } + + public decimal MultipleLayerHeightStep + { + get => _multipleLayerHeightStep; + set + { + if(!RaiseAndSetIfChanged(ref _multipleLayerHeightStep, value)) return; + RaisePropertyChanged(nameof(AvailableLayerHeights)); + } + } + + public bool MultipleExposures + { + get => _multipleExposures; + set => RaiseAndSetIfChanged(ref _multipleExposures, value); + } + + public ExposureGenTypes ExposureGenType + { + get => _exposureGenType; + set => RaiseAndSetIfChanged(ref _exposureGenType, value); + } + + public bool ExposureGenIgnoreBaseExposure + { + get => _exposureGenIgnoreBaseExposure; + set => RaiseAndSetIfChanged(ref _exposureGenIgnoreBaseExposure, value); + } + + public decimal ExposureGenBottomStep + { + get => _exposureGenBottomStep; + set => RaiseAndSetIfChanged(ref _exposureGenBottomStep, Math.Round(value, 2)); + } + + public decimal ExposureGenNormalStep + { + get => _exposureGenNormalStep; + set => RaiseAndSetIfChanged(ref _exposureGenNormalStep, Math.Round(value, 2)); + } + + public byte ExposureGenTests + { + get => _exposureGenTests; + set => RaiseAndSetIfChanged(ref _exposureGenTests, value); + } + + public decimal ExposureGenManualLayerHeight + { + get => _exposureGenManualLayerHeight; + set => RaiseAndSetIfChanged(ref _exposureGenManualLayerHeight, value); + } + + public decimal[] AvailableLayerHeights + { + get + { + List<decimal> layerHeights = new List<decimal>(); + var endLayerHeight = _multipleLayerHeight ? _multipleLayerHeightMaximum : _layerHeight; + List<ExposureItem> list = new(); + for (decimal layerHeight = _layerHeight; layerHeight <= endLayerHeight; layerHeight += _multipleLayerHeightStep) + { + layerHeights.Add(Math.Round(layerHeight, 2)); + } + + return layerHeights.ToArray(); + } + } + + public decimal ExposureGenManualBottom + { + get => _exposureGenManualBottom; + set => RaiseAndSetIfChanged(ref _exposureGenManualBottom, value); + } + + public decimal ExposureGenManualNormal + { + get => _exposureGenManualNormal; + set => RaiseAndSetIfChanged(ref _exposureGenManualNormal, value); + } + + public ExposureItem ExposureManualEntry => new (_exposureGenManualLayerHeight, _exposureGenManualBottom, _exposureGenManualNormal); + + + public ObservableCollection<ExposureItem> ExposureTable + { + get => _exposureTable; + set => RaiseAndSetIfChanged(ref _exposureTable, value); + } + + #endregion + + #region Constructor + + public OperationCalibrateExposureFinder() { } + + public OperationCalibrateExposureFinder(FileFormat slicerFile) : base(slicerFile) + { + _layerHeight = (decimal)slicerFile.LayerHeight; + _bottomLayers = slicerFile.BottomLayerCount; + _bottomExposure = (decimal)slicerFile.BottomExposureTime; + _normalExposure = (decimal)slicerFile.ExposureTime; + _mirrorOutput = slicerFile.MirrorDisplay; + } + + public override void InitWithSlicerFile() + { + base.InitWithSlicerFile(); + if (SlicerFile.DisplayWidth > 0) + DisplayWidth = (decimal)SlicerFile.DisplayWidth; + if (SlicerFile.DisplayHeight > 0) + DisplayHeight = (decimal)SlicerFile.DisplayHeight; + + if (_exposureGenManualBottom == 0) + _exposureGenManualBottom = (decimal) SlicerFile.BottomExposureTime; + if (_exposureGenManualNormal == 0) + _exposureGenManualNormal = (decimal)SlicerFile.ExposureTime; + + if (!SlicerFile.HavePrintParameterPerLayerModifier(FileFormat.PrintParameterModifier.ExposureSeconds)) + { + _multipleLayerHeight = false; + _multipleExposures = false; + } + } + + #endregion + + #region Enums + + public enum Measures : byte + { + Millimeters, + Pixels, + } + + public static Array MeasuresItems => Enum.GetValues(typeof(Measures)); + + public enum Shapes : byte + { + Circle, + Square + } + + public static Array ShapesItems => Enum.GetValues(typeof(Shapes)); + + public enum ExposureGenTypes : byte + { + Linear, + Multiplier + } + + public static Array ExposureGenTypeItems => Enum.GetValues(typeof(ExposureGenTypes)); + #endregion + + #region Equality + + private bool Equals(OperationCalibrateExposureFinder other) + { + return _layerHeight == other._layerHeight && _bottomLayers == other._bottomLayers && _bottomExposure == other._bottomExposure && _normalExposure == other._normalExposure && _topBottomMargin == other._topBottomMargin && _leftRightMargin == other._leftRightMargin && _chamferLayers == other._chamferLayers && _erodeBottomIterations == other._erodeBottomIterations && _partMargin == other._partMargin && _enableAntiAliasing == other._enableAntiAliasing && _mirrorOutput == other._mirrorOutput && _baseHeight == other._baseHeight && _cylinderHeight == other._cylinderHeight && _cylinderMargin == other._cylinderMargin && _unitOfMeasure == other._unitOfMeasure && _cylinderDiametersMm == other._cylinderDiametersMm && _cylinderDiametersPx == other._cylinderDiametersPx && _multipleLayerHeight == other._multipleLayerHeight && _multipleLayerHeightMaximum == other._multipleLayerHeightMaximum && _multipleLayerHeightStep == other._multipleLayerHeightStep && _multipleExposures == other._multipleExposures && _shape == other._shape && _exposureGenType == other._exposureGenType && _exposureGenIgnoreBaseExposure == other._exposureGenIgnoreBaseExposure && _exposureGenBottomStep == other._exposureGenBottomStep && _exposureGenNormalStep == other._exposureGenNormalStep && _exposureGenTests == other._exposureGenTests && Equals(_exposureTable, other._exposureTable); + } + + public override bool Equals(object obj) + { + return ReferenceEquals(this, obj) || obj is OperationCalibrateExposureFinder other && Equals(other); + } + + public override int GetHashCode() + { + var hashCode = new HashCode(); + hashCode.Add(_layerHeight); + hashCode.Add(_bottomLayers); + hashCode.Add(_bottomExposure); + hashCode.Add(_normalExposure); + hashCode.Add(_topBottomMargin); + hashCode.Add(_leftRightMargin); + hashCode.Add(_chamferLayers); + hashCode.Add(_erodeBottomIterations); + hashCode.Add(_partMargin); + hashCode.Add(_enableAntiAliasing); + hashCode.Add(_mirrorOutput); + hashCode.Add(_baseHeight); + hashCode.Add(_cylinderHeight); + hashCode.Add(_cylinderMargin); + hashCode.Add((int) _unitOfMeasure); + hashCode.Add(_cylinderDiametersMm); + hashCode.Add(_cylinderDiametersPx); + hashCode.Add(_multipleLayerHeight); + hashCode.Add(_multipleLayerHeightMaximum); + hashCode.Add(_multipleLayerHeightStep); + hashCode.Add(_multipleExposures); + hashCode.Add((int) _shape); + hashCode.Add((int) _exposureGenType); + hashCode.Add(_exposureGenIgnoreBaseExposure); + hashCode.Add(_exposureGenBottomStep); + hashCode.Add(_exposureGenNormalStep); + hashCode.Add(_exposureGenTests); + hashCode.Add(_exposureTable); + return hashCode.ToHashCode(); + } + + #endregion + + #region Methods + + public void SortExposureTable() + { + var list = _exposureTable.ToList(); + list.Sort(); + ExposureTable = new(list); + } + + public void SanitizeExposureTable() + { + List<ExposureItem> list = _exposureTable.ToList().Distinct().ToList(); + list.Sort(); + ExposureTable = new(list); + } + + public void GenerateExposure() + { + var endLayerHeight = _multipleLayerHeight ? _multipleLayerHeightMaximum : _layerHeight; + List<ExposureItem> list = new(); + for (decimal layerHeight = _layerHeight; + layerHeight <= endLayerHeight; + layerHeight += _multipleLayerHeightStep) + { + if(!_exposureGenIgnoreBaseExposure) + list.Add(new ExposureItem(layerHeight, (decimal) SlicerFile.BottomExposureTime, (decimal) SlicerFile.ExposureTime)); + for (ushort testN = 1; testN <= _exposureGenTests; testN++) + { + decimal bottomExposureTime = 0; + decimal exposureTime = 0; + + switch (_exposureGenType) + { + case ExposureGenTypes.Linear: + bottomExposureTime = (decimal) SlicerFile.BottomExposureTime + _exposureGenBottomStep * testN; + exposureTime = (decimal) SlicerFile.ExposureTime + _exposureGenNormalStep * testN; + break; + case ExposureGenTypes.Multiplier: + bottomExposureTime = (decimal)SlicerFile.BottomExposureTime + (decimal)SlicerFile.BottomExposureTime * layerHeight * _exposureGenBottomStep * testN; + exposureTime = (decimal)SlicerFile.ExposureTime + (decimal)SlicerFile.ExposureTime * layerHeight * _exposureGenNormalStep * testN; + break; + } + + ExposureItem item = new(layerHeight, bottomExposureTime, exposureTime); + if(list.Contains(item)) continue; // Already on list, skip + list.Add(item); + } + } + + ExposureTable = new(list); + } + + public Mat[] GetLayers() + { + var cylinders = Cylinders; + int cylinderMarginX = (int) (Xppmm * _cylinderMargin); + int cylinderMarginY = (int) (Yppmm * _cylinderMargin); + Rectangle rect = new Rectangle(new Point(1, 1), + new Size(cylinderMarginX * 4 + cylinders[^1] * 2, + cylinderMarginY * 3 + GetCylindersLength(cylinders) + TextSpacing)); + var layers = new Mat[2]; + layers[0] = EmguExtensions.InitMat(rect.Size.Inflate(2)); + + CvInvoke.Rectangle(layers[0], rect, EmguExtensions.WhiteByte, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + layers[1] = layers[0].Clone(); + CvInvoke.Rectangle(layers[1], new Rectangle(0, 0, rect.Size.Width / 2, layers[0].Height), EmguExtensions.BlackByte, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + + //int holeXPos = (int) Math.Round(Xppmm * (CylinderMargin + maxCylinder / 2)); + // Print holes + int holeXPos = 0; + int holeYPos = 0; + for (var layerIndex = 0; layerIndex < layers.Length; layerIndex++) + { + var layer = layers[layerIndex]; + holeYPos = cylinderMarginY; + for (int i = 0; i < cylinders.Length; i++) + { + var diameter = cylinders[i]; + var radius = diameter / 2; + + switch (_shape) + { + case Shapes.Circle: + holeXPos = rect.X + cylinderMarginX + cylinders[^1] - radius; + holeYPos += radius; + break; + case Shapes.Square: + holeXPos = rect.X + cylinderMarginX + cylinders[^1] - diameter; + break; + } + + + // Left side + if (layerIndex == 1) + { + if (diameter == 1) + { + layer.SetByte(holeXPos, holeYPos, 255); + } + else + { + switch (_shape) + { + case Shapes.Circle: + CvInvoke.Circle(layers[layerIndex], + new Point(holeXPos, holeYPos), + radius, EmguExtensions.WhiteByte, -1, + _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + break; + case Shapes.Square: + CvInvoke.Rectangle(layers[layerIndex], + new Rectangle(new Point(holeXPos, holeYPos), new Size(diameter, diameter)), + EmguExtensions.WhiteByte, -1, + _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + break; + } + + } + } + + //holeXPos = layers[0].Width - holeXPos; + switch (_shape) + { + case Shapes.Circle: + holeXPos = layers[0].Width - rect.X - cylinderMarginX - cylinders[^1] + radius; + break; + case Shapes.Square: + holeXPos = layers[0].Width - rect.X - cylinderMarginX - cylinders[^1]; + break; + } + + // Right side + if (diameter == 1) + { + layer.SetByte(holeXPos, holeYPos, 0); + } + else + { + switch (_shape) + { + case Shapes.Circle: + CvInvoke.Circle(layers[layerIndex], + new Point(holeXPos, holeYPos), + radius, EmguExtensions.BlackByte, -1, + _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + break; + case Shapes.Square: + CvInvoke.Rectangle(layers[layerIndex], + new Rectangle(new Point(holeXPos, holeYPos), new Size(diameter, diameter)), + EmguExtensions.BlackByte, -1, + _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + break; + } + } + + + holeYPos += cylinderMarginY; + + switch (_shape) + { + case Shapes.Circle: + holeYPos += radius; + break; + case Shapes.Square: + holeYPos += diameter; + break; + } + + + } + } + + + /*if (_mirrorOutput) + { + Parallel.ForEach(layers, mat => CvInvoke.Flip(mat, mat, FlipType.Horizontal)); + }*/ + + //layers[^1].Save("E:\\layer.png"); + + return layers; + } + + public Mat GetThumbnail() + { + Mat thumbnail = EmguExtensions.InitMat(new Size(400, 200), 3); + var fontFace = FontFace.HersheyDuplex; + var fontScale = 1; + var fontThickness = 2; + const byte xSpacing = 45; + const byte ySpacing = 45; + CvInvoke.PutText(thumbnail, "UVtools", new Point(140, 35), fontFace, fontScale, new MCvScalar(255, 27, 245), fontThickness + 1); + CvInvoke.Line(thumbnail, new Point(xSpacing, 0), new Point(xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); + CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); + CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); + CvInvoke.PutText(thumbnail, "Exposure Time Cal.", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness); + CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness); + CvInvoke.PutText(thumbnail, $"Objects: {Cylinders.Length}", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness); + + return thumbnail; + } + + protected override bool ExecuteInternally(OperationProgress progress) + { + progress.ItemCount = 0; + SanitizeExposureTable(); + var layers = GetLayers(); + if (layers[0].Width > SlicerFile.ResolutionX || layers[0].Height > SlicerFile.ResolutionY) + { + return false; + } + + List<Layer> newLayers = new(); + + Dictionary<ExposureItem, Point> table = new(); + var endLayerHeight = _multipleLayerHeight ? _multipleLayerHeightMaximum : _layerHeight; + var totalHeight = TotalHeight; + int sideMarginPx = (int)Math.Floor(_leftRightMargin * Xppmm); + int topBottomMarginPx = (int)Math.Floor(_topBottomMargin * Yppmm); + uint layerIndex = 0; + int currentX = sideMarginPx; + int currentY = topBottomMarginPx; + int cylinderMarginY = (int)(Yppmm * _cylinderMargin); + + var anchor = new Point(-1, -1); + using var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor); + + void AddLayer(decimal currentHeight, decimal layerHeight, decimal bottomExposure, decimal normalExposure) + { + var layerDifference = currentHeight / layerHeight; + + if (!layerDifference.IsInteger()) return; // Not at right height to process with layer height + //Debug.WriteLine($"{currentHeight} / {layerHeight} = {layerDifference}, Floor={Math.Floor(layerDifference)}"); + + Point position; + ExposureItem key = new(layerHeight, bottomExposure, normalExposure); + + if (table.TryGetValue(key, out var pos)) + { + position = pos; + } + else + { + if (currentX + layers[0].Width + sideMarginPx > SlicerFile.ResolutionX) + { + currentX = sideMarginPx; + currentY += layers[0].Height + (int)Math.Floor(_partMargin * Yppmm); + } + + if (currentY + layers[0].Height + topBottomMarginPx > SlicerFile.ResolutionY) + { + return; // Reach the end + } + + position = new Point(currentX, currentY); + table.Add(key, new Point(currentX, currentY)); + + currentX += layers[0].Width + (int)Math.Floor(_partMargin * Xppmm); + } + + ushort microns = (ushort)Math.Floor(layerHeight * 1000); + + Mat mat = EmguExtensions.InitMat(SlicerFile.Resolution); + Mat matRoi = new(mat, new Rectangle(position, layers[0].Size)); + + int layerCountOnHeight = (int)Math.Floor(currentHeight / layerHeight); + bool isBottomLayer = layerCountOnHeight <= _bottomLayers; + bool isBaseLayer = currentHeight <= _baseHeight; + layers[isBaseLayer ? 0 : 1].CopyTo(matRoi); + + if (isBottomLayer && _erodeBottomIterations > 0) + { + CvInvoke.Erode(matRoi, matRoi, kernel, anchor, _erodeBottomIterations, BorderType.Reflect101, default); + } + + if (layerCountOnHeight < _chamferLayers) + { + CvInvoke.Erode(matRoi, matRoi, kernel, anchor, _chamferLayers - layerCountOnHeight, BorderType.Reflect101, default); + } + + var textHeightStart = matRoi.Height - cylinderMarginY - TextSpacing; + CvInvoke.PutText(matRoi, $"{microns}u", new Point(TextStartX, textHeightStart), FontFace, TextScale, EmguExtensions.WhiteByte, TextThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(matRoi, $"{bottomExposure}s", new Point(TextStartX, textHeightStart + TextLineBreak), FontFace, TextScale, EmguExtensions.WhiteByte, TextThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(matRoi, $"{normalExposure}s", new Point(TextStartX, textHeightStart + TextLineBreak * 2), FontFace, TextScale, EmguExtensions.WhiteByte, TextThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(matRoi, $"{microns}u", new Point(matRoi.Width / 2 + TextStartX, textHeightStart), FontFace, TextScale, EmguExtensions.BlackByte, TextThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(matRoi, $"{bottomExposure}s", new Point(matRoi.Width / 2 + TextStartX, textHeightStart + TextLineBreak), FontFace, TextScale, EmguExtensions.BlackByte, TextThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(matRoi, $"{normalExposure}s", new Point(matRoi.Width / 2 + TextStartX, textHeightStart + TextLineBreak * 2), FontFace, TextScale, EmguExtensions.BlackByte, TextThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + + Layer layer = new(layerIndex++, mat, SlicerFile) + { + PositionZ = (float)currentHeight, + ExposureTime = isBottomLayer ? (float)bottomExposure : (float)normalExposure, + LiftHeight = isBottomLayer ? SlicerFile.BottomLiftHeight : SlicerFile.LiftHeight, + LiftSpeed = isBottomLayer ? SlicerFile.BottomLiftSpeed : SlicerFile.LiftSpeed, + RetractSpeed = SlicerFile.RetractSpeed, + LightOffDelay = isBottomLayer ? SlicerFile.BottomLightOffDelay : SlicerFile.LightOffDelay, + LightPWM = isBottomLayer ? SlicerFile.BottomLightPWM : SlicerFile.LightPWM, + IsModified = true + }; + newLayers.Add(layer); + mat.Dispose(); + + progress++; + } + + for (decimal currentHeight = _layerHeight; currentHeight <= totalHeight; currentHeight += 0.01m) + { + currentHeight = Math.Round(currentHeight, 2); + for (decimal layerHeight = _layerHeight; layerHeight <= endLayerHeight; layerHeight += _multipleLayerHeightStep) + { + progress.Token.ThrowIfCancellationRequested(); + layerHeight = Math.Round(layerHeight, 2); + + if (_multipleExposures) + { + foreach (var exposureItem in _exposureTable) + { + if (exposureItem.IsValid && exposureItem.LayerHeight == layerHeight) + { + AddLayer(currentHeight, layerHeight, exposureItem.BottomExposure, exposureItem.Exposure); + } + } + } + else + { + AddLayer(currentHeight, layerHeight, _bottomExposure, _normalExposure); + } + } + } + + if (SlicerFile.ThumbnailsCount > 0) + SlicerFile.SetThumbnails(GetThumbnail()); + + SlicerFile.SuppressRebuildProperties = true; + SlicerFile.LayerHeight = (float)LayerHeight; + SlicerFile.BottomExposureTime = (float)BottomExposure; + SlicerFile.ExposureTime = (float)NormalExposure; + SlicerFile.BottomLayerCount = BottomLayers; + SlicerFile.LayerManager.Layers = newLayers.ToArray(); + SlicerFile.SuppressRebuildProperties = false; + + if (_mirrorOutput) + { + new OperationFlip(SlicerFile){FlipDirection = Enumerations.FlipDirection.Horizontally}.Execute(progress); + } + + new OperationMove(SlicerFile).Execute(progress); + + return !progress.Token.IsCancellationRequested; + } + + #endregion + } +} diff --git a/UVtools.Core/Operations/OperationCalibrateExternalTests.cs b/UVtools.Core/Operations/OperationCalibrateExternalTests.cs index 64a5bb6..99fa3fd 100644 --- a/UVtools.Core/Operations/OperationCalibrateExternalTests.cs +++ b/UVtools.Core/Operations/OperationCalibrateExternalTests.cs @@ -7,6 +7,8 @@ */ +using UVtools.Core.FileFormats; + namespace UVtools.Core.Operations { public class OperationCalibrateExternalTests : Operation @@ -29,7 +31,15 @@ namespace UVtools.Core.Operations public override string ProgressTitle => null; public override string ProgressAction => null; - + + #endregion + + #region Constructor + + public OperationCalibrateExternalTests() { } + + public OperationCalibrateExternalTests(FileFormat slicerFile) : base(slicerFile) { } + #endregion #region Properties @@ -37,7 +47,7 @@ namespace UVtools.Core.Operations #endregion #region Equality - + #endregion #region Methods diff --git a/UVtools.Core/Operations/OperationCalibrateGrayscale.cs b/UVtools.Core/Operations/OperationCalibrateGrayscale.cs index 7d78d6f..844d7fd 100644 --- a/UVtools.Core/Operations/OperationCalibrateGrayscale.cs +++ b/UVtools.Core/Operations/OperationCalibrateGrayscale.cs @@ -10,7 +10,6 @@ using System; using System.Drawing; using System.Text; using System.Threading.Tasks; -using System.Xml.Serialization; using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Structure; @@ -25,7 +24,6 @@ namespace UVtools.Core.Operations public sealed class OperationCalibrateGrayscale : Operation { #region Members - private Size _resolution = Size.Empty; private decimal _layerHeight = 0.05M; private ushort _bottomLayers = 10; private ushort _interfaceLayers = 5; @@ -100,15 +98,21 @@ namespace UVtools.Core.Operations #endregion - #region Properties + #region Constructor + + public OperationCalibrateGrayscale() { } - [XmlIgnore] - public Size Resolution + public OperationCalibrateGrayscale(FileFormat slicerFile) : base(slicerFile) { - get => _resolution; - set => RaiseAndSetIfChanged(ref _resolution, value); + _layerHeight = (decimal)slicerFile.LayerHeight; + _bottomLayers = slicerFile.BottomLayerCount; + _bottomExposure = (decimal)slicerFile.BottomExposureTime; + _normalExposure = (decimal)slicerFile.ExposureTime; + _mirrorOutput = slicerFile.MirrorDisplay; } + #endregion + #region Properties public decimal LayerHeight { @@ -340,10 +344,10 @@ namespace UVtools.Core.Operations { Mat[] layers = new Mat[3]; - layers[0] = EmguExtensions.InitMat(Resolution); + layers[0] = EmguExtensions.InitMat(SlicerFile.Resolution); - int radius = Math.Max(100, Math.Min(Resolution.Width, Resolution.Height) - _outerMargin * 2) / 2 ; - Point center = new Point(Resolution.Width / 2, Resolution.Height / 2); + int radius = Math.Max(100, Math.Min(SlicerFile.Resolution.Width, SlicerFile.Resolution.Height) - _outerMargin * 2) / 2 ; + Point center = new Point(SlicerFile.Resolution.Width / 2, SlicerFile.Resolution.Height / 2); int innerRadius = Math.Max(100, radius - _innerMargin); double topLineLength = 0; @@ -452,32 +456,22 @@ namespace UVtools.Core.Operations return thumbnail; } - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(ProgressAction, LayerCount); - slicerFile.SuppressRebuildProperties = true; - + progress.ItemCount = LayerCount; var newLayers = new Layer[LayerCount]; - slicerFile.LayerHeight = (float)LayerHeight; - slicerFile.BottomExposureTime = (float)BottomExposure; - slicerFile.ExposureTime = (float)NormalExposure; - slicerFile.BottomLayerCount = BottomLayers; - var layers = GetLayers(); - progress++; - - var bottomLayer = new Layer(0, layers[0], slicerFile.LayerManager) + var bottomLayer = new Layer(0, layers[0], SlicerFile.LayerManager) { IsModified = true }; - var interfaceLayer = InterfaceLayers > 0 && layers[1] is not null ? new Layer(0, layers[1], slicerFile.LayerManager) + var interfaceLayer = InterfaceLayers > 0 && layers[1] is not null ? new Layer(0, layers[1], SlicerFile.LayerManager) { IsModified = true } : null; - var layer = new Layer(0, layers[2], slicerFile.LayerManager) + var layer = new Layer(0, layers[2], SlicerFile.LayerManager) { IsModified = true }; @@ -511,17 +505,20 @@ namespace UVtools.Core.Operations } - if (slicerFile.ThumbnailsCount > 0) - slicerFile.SetThumbnails(GetThumbnail()); + if (SlicerFile.ThumbnailsCount > 0) + SlicerFile.SetThumbnails(GetThumbnail()); - progress++; + SlicerFile.SuppressRebuildProperties = true; + SlicerFile.LayerHeight = (float)LayerHeight; + SlicerFile.BottomExposureTime = (float)BottomExposure; + SlicerFile.ExposureTime = (float)NormalExposure; + SlicerFile.BottomLayerCount = BottomLayers; + SlicerFile.LayerManager.Layers = newLayers; + SlicerFile.LayerManager.RebuildLayersProperties(); + SlicerFile.SuppressRebuildProperties = false; - slicerFile.LayerManager.Layers = newLayers; - slicerFile.SuppressRebuildProperties = false; - slicerFile.LayerManager.RebuildLayersProperties(); - - return true; + return !progress.Token.IsCancellationRequested; } #endregion diff --git a/UVtools.Core/Operations/OperationCalibrateStressTower.cs b/UVtools.Core/Operations/OperationCalibrateStressTower.cs index 4d95608..1318f09 100644 --- a/UVtools.Core/Operations/OperationCalibrateStressTower.cs +++ b/UVtools.Core/Operations/OperationCalibrateStressTower.cs @@ -10,7 +10,6 @@ using System; using System.Drawing; using System.Text; using System.Threading.Tasks; -using System.Xml.Serialization; using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Structure; @@ -98,10 +97,31 @@ namespace UVtools.Core.Operations #endregion - #region Properties + #region Constructor + + public OperationCalibrateStressTower() { } + + public OperationCalibrateStressTower(FileFormat slicerFile) : base(slicerFile) + { + _layerHeight = (decimal)slicerFile.LayerHeight; + _bottomLayers = slicerFile.BottomLayerCount; + _bottomExposure = (decimal)slicerFile.BottomExposureTime; + _normalExposure = (decimal)slicerFile.ExposureTime; + _mirrorOutput = slicerFile.MirrorDisplay; + } + + public override void InitWithSlicerFile() + { + base.InitWithSlicerFile(); + if (SlicerFile.DisplayWidth > 0) + DisplayWidth = (decimal)SlicerFile.DisplayWidth; + if (SlicerFile.DisplayHeight > 0) + DisplayHeight = (decimal)SlicerFile.DisplayHeight; + } - [XmlIgnore] - public Size Resolution { get; set; } = Size.Empty; + #endregion + + #region Properties public decimal DisplayWidth { @@ -294,8 +314,8 @@ namespace UVtools.Core.Operations { var layers = new Mat[LayerCount]; - Slicer slicer = new(Resolution, new SizeF((float) DisplayWidth, (float) DisplayHeight)); - Point center = new Point(Resolution.Width / 2, Resolution.Height / 2); + Slicer slicer = new(SlicerFile.Resolution, new SizeF((float) DisplayWidth, (float) DisplayHeight)); + Point center = new Point(SlicerFile.Resolution.Width / 2, SlicerFile.Resolution.Height / 2); uint baseRadius = slicer.PixelsFromMillimeters(_baseDiameter) / 2; uint baseLayers = (ushort) Math.Floor(_baseHeight / _layerHeight); uint bodyLayers = (ushort) Math.Floor(_bodyHeight / _layerHeight); @@ -315,7 +335,7 @@ namespace UVtools.Core.Operations var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor);*/ Parallel.For(0, LayerCount, layerIndex => { - layers[layerIndex] = EmguExtensions.InitMat(Resolution); + layers[layerIndex] = EmguExtensions.InitMat(SlicerFile.Resolution); }); Parallel.For(0, baseLayers, layerIndex => @@ -389,24 +409,16 @@ namespace UVtools.Core.Operations return thumbnail; } - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(ProgressAction, LayerCount); - slicerFile.SuppressRebuildProperties = true; - + progress.ItemCount = LayerCount; var newLayers = new Layer[LayerCount]; - slicerFile.LayerHeight = (float)LayerHeight; - slicerFile.BottomExposureTime = (float)BottomExposure; - slicerFile.ExposureTime = (float)NormalExposure; - slicerFile.BottomLayerCount = BottomLayers; - var layers = GetLayers(); Parallel.For(0, LayerCount, layerIndex => { - newLayers[layerIndex] = new Layer((uint)layerIndex, layers[layerIndex], slicerFile.LayerManager); + newLayers[layerIndex] = new Layer((uint)layerIndex, layers[layerIndex], SlicerFile.LayerManager); layers[layerIndex].Dispose(); lock (progress) { @@ -414,22 +426,21 @@ namespace UVtools.Core.Operations } }); - slicerFile.LayerManager.Layers = newLayers; - slicerFile.LayerManager.RebuildLayersProperties(); - - /*var moveOp = new OperationMove(slicerFile.LayerManager.BoundingRectangle, slicerFile.Resolution) - { - IsCutMove = true, - LayerIndexEnd = slicerFile.LayerCount - 1 - }; - moveOp.Execute(slicerFile, progress);*/ + + if (SlicerFile.ThumbnailsCount > 0) + SlicerFile.SetThumbnails(GetThumbnail()); - if (slicerFile.ThumbnailsCount > 0) - slicerFile.SetThumbnails(GetThumbnail()); + SlicerFile.SuppressRebuildProperties = true; + SlicerFile.LayerHeight = (float)LayerHeight; + SlicerFile.BottomExposureTime = (float)BottomExposure; + SlicerFile.ExposureTime = (float)NormalExposure; + SlicerFile.BottomLayerCount = BottomLayers; + SlicerFile.LayerManager.Layers = newLayers; + SlicerFile.LayerManager.RebuildLayersProperties(); + SlicerFile.SuppressRebuildProperties = false; - slicerFile.SuppressRebuildProperties = false; - return true; + return !progress.Token.IsCancellationRequested; } #endregion diff --git a/UVtools.Core/Operations/OperationCalibrateTolerance.cs b/UVtools.Core/Operations/OperationCalibrateTolerance.cs index aa9bf81..190f111 100644 --- a/UVtools.Core/Operations/OperationCalibrateTolerance.cs +++ b/UVtools.Core/Operations/OperationCalibrateTolerance.cs @@ -128,9 +128,6 @@ namespace UVtools.Core.Operations #region Properties - [XmlIgnore] - public Size Resolution { get; set; } = Size.Empty; - public decimal DisplayWidth { get => _displayWidth; @@ -151,8 +148,8 @@ namespace UVtools.Core.Operations } } - public decimal Xppmm => DisplayWidth > 0 ? Math.Round(Resolution.Width / DisplayWidth, 2) : 0; - public decimal Yppmm => DisplayHeight > 0 ? Math.Round(Resolution.Height / DisplayHeight, 2) : 0; + public decimal Xppmm => DisplayWidth > 0 ? Math.Round(SlicerFile.Resolution.Width / DisplayWidth, 2) : 0; + public decimal Yppmm => DisplayHeight > 0 ? Math.Round(SlicerFile.Resolution.Height / DisplayHeight, 2) : 0; public decimal LayerHeight { @@ -425,8 +422,32 @@ namespace UVtools.Core.Operations */ #endregion + #region Constructor + + public OperationCalibrateTolerance() { } + + public OperationCalibrateTolerance(FileFormat slicerFile) : base(slicerFile) + { + _layerHeight = (decimal)slicerFile.LayerHeight; + _bottomLayers = slicerFile.BottomLayerCount; + _bottomExposure = (decimal)slicerFile.BottomExposureTime; + _normalExposure = (decimal)slicerFile.ExposureTime; + _mirrorOutput = slicerFile.MirrorDisplay; + } + + public override void InitWithSlicerFile() + { + base.InitWithSlicerFile(); + if (SlicerFile.DisplayWidth > 0) + DisplayWidth = (decimal)SlicerFile.DisplayWidth; + if (SlicerFile.DisplayHeight > 0) + DisplayHeight = (decimal)SlicerFile.DisplayHeight; + } + + #endregion + #region Enums - + public enum Shapes : byte { Circle, @@ -483,7 +504,7 @@ namespace UVtools.Core.Operations public Mat[] GetLayers() { var layers = new Mat[LayerCount]; - var layer = EmguExtensions.InitMat(Resolution); + var layer = EmguExtensions.InitMat(SlicerFile.Resolution); ushort startX = Math.Max((ushort)2, _leftRightMargin); ushort startY = Math.Max((ushort)2, _topBottomMargin); @@ -534,13 +555,13 @@ namespace UVtools.Core.Operations if (_fuseParts) { if (xPixels >= FemaleHoleDiameterXPixels || yPixels >= FemaleHoleDiameterYPixels) return false; - if (currentX + FemaleDiameterXPixels + _leftRightMargin >= Resolution.Width) + if (currentX + FemaleDiameterXPixels + _leftRightMargin >= SlicerFile.Resolution.Width) { currentX = startX; currentY += (int)FemaleDiameterYPixels + PartMargin; } - if (currentY + FemaleDiameterYPixels + _topBottomMargin >= Resolution.Height) + if (currentY + FemaleDiameterYPixels + _topBottomMargin >= SlicerFile.Resolution.Height) { return false; // Insufficient size } @@ -571,13 +592,13 @@ namespace UVtools.Core.Operations } else { - if (currentX + xPixels + _leftRightMargin >= Resolution.Width) + if (currentX + xPixels + _leftRightMargin >= SlicerFile.Resolution.Width) { currentX = startX; currentY += yPixels + PartMargin; } - if (currentY + yPixels + _topBottomMargin >= Resolution.Height) + if (currentY + yPixels + _topBottomMargin >= SlicerFile.Resolution.Height) { return false; // Insufficient size } @@ -704,24 +725,17 @@ namespace UVtools.Core.Operations return thumbnail; } - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(ProgressAction, LayerCount); - slicerFile.SuppressRebuildProperties = true; + progress.ItemCount = LayerCount; var newLayers = new Layer[LayerCount]; - slicerFile.LayerHeight = (float)LayerHeight; - slicerFile.BottomExposureTime = (float)BottomExposure; - slicerFile.ExposureTime = (float)NormalExposure; - slicerFile.BottomLayerCount = BottomLayers; - var layers = GetLayers(); Parallel.For(0, LayerCount, layerIndex => { - newLayers[layerIndex] = new Layer((uint)layerIndex, layers[layerIndex], slicerFile.LayerManager); + newLayers[layerIndex] = new Layer((uint)layerIndex, layers[layerIndex], SlicerFile.LayerManager); layers[layerIndex].Dispose(); lock (progress) { @@ -729,22 +743,23 @@ namespace UVtools.Core.Operations } }); - slicerFile.LayerManager.Layers = newLayers; - slicerFile.LayerManager.RebuildLayersProperties(); + if (SlicerFile.ThumbnailsCount > 0) + SlicerFile.SetThumbnails(GetThumbnail()); - var moveOp = new OperationMove(slicerFile.LayerManager.BoundingRectangle, slicerFile.Resolution) - { - IsCutMove = true, - LayerIndexEnd = slicerFile.LayerCount - 1 - }; - moveOp.Execute(slicerFile, progress); + SlicerFile.SuppressRebuildProperties = true; + SlicerFile.LayerHeight = (float)LayerHeight; + SlicerFile.BottomExposureTime = (float)BottomExposure; + SlicerFile.ExposureTime = (float)NormalExposure; + SlicerFile.BottomLayerCount = BottomLayers; + SlicerFile.LayerManager.Layers = newLayers; + SlicerFile.LayerManager.RebuildLayersProperties(); + SlicerFile.SuppressRebuildProperties = false; - if (slicerFile.ThumbnailsCount > 0) - slicerFile.SetThumbnails(GetThumbnail()); + var moveOp = new OperationMove(SlicerFile); + moveOp.Execute(progress); - slicerFile.SuppressRebuildProperties = false; - return true; + return !progress.Token.IsCancellationRequested; } #endregion diff --git a/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs b/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs index 3c5f4bd..a36d4a9 100644 --- a/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs +++ b/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs @@ -118,9 +118,6 @@ namespace UVtools.Core.Operations #region Properties - [XmlIgnore] - public Size Resolution { get; set; } = Size.Empty; - public decimal DisplayWidth { get => _displayWidth; @@ -141,8 +138,8 @@ namespace UVtools.Core.Operations } } - public decimal Xppmm => DisplayWidth > 0 ? Math.Round(Resolution.Width / DisplayWidth, 2) : 0; - public decimal Yppmm => DisplayHeight > 0 ? Math.Round(Resolution.Height / DisplayHeight, 2) : 0; + public decimal Xppmm => DisplayWidth > 0 ? Math.Round(SlicerFile.Resolution.Width / DisplayWidth, 2) : 0; + public decimal Yppmm => DisplayHeight > 0 ? Math.Round(SlicerFile.Resolution.Height / DisplayHeight, 2) : 0; public decimal LayerHeight { @@ -447,6 +444,30 @@ namespace UVtools.Core.Operations #endregion + #region Constructor + + public OperationCalibrateXYZAccuracy() { } + + public OperationCalibrateXYZAccuracy(FileFormat slicerFile) : base(slicerFile) + { + _layerHeight = (decimal)slicerFile.LayerHeight; + _bottomLayers = slicerFile.BottomLayerCount; + _bottomExposure = (decimal)slicerFile.BottomExposureTime; + _normalExposure = (decimal)slicerFile.ExposureTime; + _mirrorOutput = slicerFile.MirrorDisplay; + } + + public override void InitWithSlicerFile() + { + base.InitWithSlicerFile(); + if (SlicerFile.DisplayWidth > 0) + DisplayWidth = (decimal)SlicerFile.DisplayWidth; + if (SlicerFile.DisplayHeight > 0) + DisplayHeight = (decimal)SlicerFile.DisplayHeight; + } + + #endregion + #region Enums #endregion @@ -465,7 +486,7 @@ namespace UVtools.Core.Operations #endregion #region Equality - + private bool Equals(OperationCalibrateXYZAccuracy other) { return _layerHeight == other._layerHeight && _bottomLayers == other._bottomLayers && _bottomExposure == other._bottomExposure && _normalExposure == other._normalExposure && _topBottomMargin == other._topBottomMargin && _leftRightMargin == other._leftRightMargin && _xSize == other._xSize && _ySize == other._ySize && _zSize == other._zSize && _centerHoleRelief == other._centerHoleRelief && _hollowModel == other._hollowModel && _wallThickness == other._wallThickness && _observedXSize == other._observedXSize && _observedYSize == other._observedYSize && _observedZSize == other._observedZSize && _outputTLObject == other._outputTLObject && _outputTCObject == other._outputTCObject && _outputTRObject == other._outputTRObject && _outputMLObject == other._outputMLObject && _outputMCObject == other._outputMCObject && _outputMRObject == other._outputMRObject && _outputBLObject == other._outputBLObject && _outputBCObject == other._outputBCObject && _outputBRObject == other._outputBRObject && _mirrorOutput == other._mirrorOutput; @@ -586,7 +607,7 @@ namespace UVtools.Core.Operations var layers = new Mat[2]; for (byte i = 0; i < layers.Length; i++) { - layers[i] = EmguExtensions.InitMat(Resolution); + layers[i] = EmguExtensions.InitMat(SlicerFile.Resolution); } @@ -610,11 +631,11 @@ namespace UVtools.Core.Operations positionYStr = "T"; break; case 1: - currentY = (int)(Resolution.Height / 2 - YPixels / 2); + currentY = (int)(SlicerFile.Resolution.Height / 2 - YPixels / 2); positionYStr = "M"; break; case 2: - currentY = (int)(Resolution.Height - YPixels - _topBottomMargin); + currentY = (int)(SlicerFile.Resolution.Height - YPixels - _topBottomMargin); positionYStr = "B"; break; } @@ -627,11 +648,11 @@ namespace UVtools.Core.Operations positionStr = $"{positionYStr}L"; break; case 1: - currentX = (int)(Resolution.Width / 2 - XPixels / 2); + currentX = (int)(SlicerFile.Resolution.Width / 2 - XPixels / 2); positionStr = $"{positionYStr}C"; break; case 2: - currentX = (int)(Resolution.Width - XPixels - _leftRightMargin); + currentX = (int)(SlicerFile.Resolution.Width - XPixels - _leftRightMargin); positionStr = $"{positionYStr}R"; break; } @@ -716,34 +737,27 @@ namespace UVtools.Core.Operations return thumbnail; } - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(ProgressAction, LayerCount); + progress.ItemCount = LayerCount; - slicerFile.SuppressRebuildProperties = true; var newLayers = new Layer[LayerCount]; - slicerFile.LayerHeight = (float)LayerHeight; - slicerFile.BottomExposureTime = (float)BottomExposure; - slicerFile.ExposureTime = (float)NormalExposure; - slicerFile.BottomLayerCount = BottomLayers; - var layers = GetLayers(); - var bottomLayer = new Layer(0, layers[0], slicerFile.LayerManager) + var bottomLayer = new Layer(0, layers[0], SlicerFile.LayerManager) { IsModified = true }; - var layer = new Layer(0, layers[1], slicerFile.LayerManager) + var layer = new Layer(0, layers[1], SlicerFile.LayerManager) { IsModified = true }; for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { - newLayers[layerIndex] = slicerFile.GetInitialLayerValueOrNormal(layerIndex, bottomLayer.Clone(), layer.Clone()); + newLayers[layerIndex] = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, bottomLayer.Clone(), layer.Clone()); progress++; } @@ -752,13 +766,19 @@ namespace UVtools.Core.Operations mat.Dispose(); } - if (slicerFile.ThumbnailsCount > 0) - slicerFile.SetThumbnails(GetThumbnail()); + if (SlicerFile.ThumbnailsCount > 0) + SlicerFile.SetThumbnails(GetThumbnail()); + + SlicerFile.SuppressRebuildProperties = true; + SlicerFile.LayerHeight = (float)LayerHeight; + SlicerFile.BottomExposureTime = (float)BottomExposure; + SlicerFile.ExposureTime = (float)NormalExposure; + SlicerFile.BottomLayerCount = BottomLayers; + SlicerFile.LayerManager.Layers = newLayers; + SlicerFile.LayerManager.RebuildLayersProperties(); + SlicerFile.SuppressRebuildProperties = false; - slicerFile.LayerManager.Layers = newLayers; - slicerFile.LayerManager.RebuildLayersProperties(); - slicerFile.SuppressRebuildProperties = false; - return true; + return !progress.Token.IsCancellationRequested; } #endregion diff --git a/UVtools.Core/Operations/OperationChangeResolution.cs b/UVtools.Core/Operations/OperationChangeResolution.cs index fe4d3cf..4a020c9 100644 --- a/UVtools.Core/Operations/OperationChangeResolution.cs +++ b/UVtools.Core/Operations/OperationChangeResolution.cs @@ -64,25 +64,25 @@ namespace UVtools.Core.Operations public override string ConfirmationText => "change print resolution " + - $"from {OldResolution.Width}x{OldResolution.Height} " + + $"from {SlicerFile.ResolutionX}x{SlicerFile.ResolutionY} " + $"to {NewResolutionX}x{NewResolutionY}?"; public override string ProgressTitle => - $"Changing print resolution from ({OldResolution.Width}x{OldResolution.Height}) to ({NewResolutionX}x{NewResolutionY})"; + $"Changing print resolution from ({SlicerFile.ResolutionX}x{SlicerFile.ResolutionY}) to ({NewResolutionX}x{NewResolutionY})"; public override string ProgressAction => "Changed layers"; public override StringTag Validate(params object[] parameters) { var sb = new StringBuilder(); - if (OldResolution.Width == NewResolutionX && OldResolution.Height == NewResolutionY) + if (SlicerFile.ResolutionX == NewResolutionX && SlicerFile.ResolutionY == NewResolutionY) { - sb.AppendLine($"The new resolution must be different from current resolution ({OldResolution.Width} x {OldResolution.Height})."); + sb.AppendLine($"The new resolution must be different from current resolution ({SlicerFile.ResolutionX} x {SlicerFile.ResolutionY})."); } - if (NewResolutionX < VolumeBonds.Width || NewResolutionY < VolumeBonds.Height) + if (NewResolutionX < SlicerFile.BoundingRectangle.Width || NewResolutionY < SlicerFile.BoundingRectangle.Height) { - sb.AppendLine($"The new resolution ({NewResolutionX} x {NewResolutionY}) is not large enough to hold the model volume ({VolumeBonds.Width} x {VolumeBonds.Height}), continuing operation would clip the model"); + sb.AppendLine($"The new resolution ({NewResolutionX} x {NewResolutionY}) is not large enough to hold the model volume ({SlicerFile.BoundingRectangle.Width} x {SlicerFile.BoundingRectangle.Height}), continuing operation would clip the model"); sb.AppendLine("To fix this, try to rotate the object and/or resize to fit on this new resolution."); } @@ -99,8 +99,6 @@ namespace UVtools.Core.Operations #endregion #region Properties - public Size OldResolution { get; } - public uint NewResolutionX { get => _newResolutionX; @@ -113,25 +111,20 @@ namespace UVtools.Core.Operations set => RaiseAndSetIfChanged(ref _newResolutionY, value); } - public Rectangle VolumeBonds { get; } - - public Size VolumeBondsSize => VolumeBonds.Size; + public Size VolumeBondsSize => SlicerFile.BoundingRectangle.Size; #endregion #region Constructor - public OperationChangeResolution() - { - } + public OperationChangeResolution() { } - public OperationChangeResolution(Size oldResolution, Rectangle volumeBonds) + public OperationChangeResolution(FileFormat slicerFile) : base(slicerFile) { - OldResolution = oldResolution; - VolumeBonds = volumeBonds; - NewResolutionX = (uint) oldResolution.Width; - NewResolutionY = (uint) oldResolution.Height; + NewResolutionX = SlicerFile.ResolutionX; + NewResolutionY = SlicerFile.ResolutionY; } + #endregion #region Methods @@ -159,25 +152,24 @@ namespace UVtools.Core.Operations public static Resolution[] Presets => GetResolutions(); - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(ProgressAction, slicerFile.LayerCount); + progress.ItemCount = SlicerFile.LayerCount; - Parallel.For(0, slicerFile.LayerCount, layerIndex => + Parallel.For(0, SlicerFile.LayerCount, layerIndex => { if (progress.Token.IsCancellationRequested) return; - using var mat = slicerFile[layerIndex].LayerMat; - using var matRoi = new Mat(mat, VolumeBonds); + using var mat = SlicerFile[layerIndex].LayerMat; + using var matRoi = new Mat(mat, SlicerFile.BoundingRectangle); using var matDst = new Mat(new Size((int)NewResolutionX, (int)NewResolutionY), mat.Depth, mat.NumberOfChannels); using var matDstRoi = new Mat(matDst, - new Rectangle((int)(NewResolutionX / 2 - VolumeBonds.Width / 2), - (int)NewResolutionY / 2 - VolumeBonds.Height / 2, - VolumeBonds.Width, VolumeBonds.Height)); + new Rectangle((int)(NewResolutionX / 2 - SlicerFile.BoundingRectangle.Width / 2), + (int)NewResolutionY / 2 - SlicerFile.BoundingRectangle.Height / 2, + SlicerFile.BoundingRectangle.Width, SlicerFile.BoundingRectangle.Height)); matRoi.CopyTo(matDstRoi); //Execute(mat); - slicerFile[layerIndex].LayerMat = matDst; + SlicerFile[layerIndex].LayerMat = matDst; lock (progress.Mutex) { @@ -187,10 +179,10 @@ namespace UVtools.Core.Operations progress.Token.ThrowIfCancellationRequested(); - slicerFile.ResolutionX = NewResolutionX; - slicerFile.ResolutionY = NewResolutionY; + SlicerFile.ResolutionX = NewResolutionX; + SlicerFile.ResolutionY = NewResolutionY; - return true; + return !progress.Token.IsCancellationRequested; } /*public override bool Execute(Mat mat, params object[] arguments) diff --git a/UVtools.Core/Operations/OperationDynamicLayerHeight.cs b/UVtools.Core/Operations/OperationDynamicLayerHeight.cs new file mode 100644 index 0000000..3615714 --- /dev/null +++ b/UVtools.Core/Operations/OperationDynamicLayerHeight.cs @@ -0,0 +1,760 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Drawing; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Serialization; +using Emgu.CV; +using Emgu.CV.CvEnum; +using UVtools.Core.Extensions; +using UVtools.Core.FileFormats; +using UVtools.Core.Objects; + +namespace UVtools.Core.Operations +{ + [Serializable] + public sealed class OperationDynamicLayerHeight : Operation + { + #region Sub Classes + public sealed class Report + { + public uint OldLayerCount { get; set; } + public uint NewLayerCount { get; set; } + public uint StackedLayers { get; set; } + public uint ReusedLayers => OldLayerCount - StackedLayers; + public float MaximumLayerHeight { get; set; } + public float OldPrintTime { get; set; } + public float NewPrintTime { get; set; } + public double SparedPrintTime => Math.Round(OldPrintTime - NewPrintTime, 2); + + public double CompressionRatio => Math.Round((double)OldLayerCount / NewLayerCount * 100.0, 2); + + public override string ToString() + { + var oldTime = TimeSpan.FromSeconds(OldPrintTime); + var newTime = TimeSpan.FromSeconds(NewPrintTime); + var sparedTime = TimeSpan.FromSeconds(SparedPrintTime); + return + $"From {OldLayerCount} layers, {ReusedLayers} got reused, {StackedLayers} got stacked and optimized with dynamic layer height's\n" + + $"Resultant layers: {NewLayerCount}\n" + + $"Compression ratio: {CompressionRatio}%\n" + + $"Maximum layer height reached: {MaximumLayerHeight}mm\n" + + $"Print time: {oldTime.Hours}h{oldTime.Minutes}m{oldTime.Seconds}s -> {newTime.Hours}h{newTime.Minutes}m{newTime.Seconds}s (- {sparedTime.Hours}h{sparedTime.Minutes}m{sparedTime.Seconds}s)"; + } + } + + [Serializable] + public sealed class ExposureItem : BindableBase + { + private decimal _layerHeight; + private decimal _bottomExposure; + private decimal _exposure; + + + /// <summary> + /// Gets or sets the layer height in millimeters + /// </summary> + public decimal LayerHeight + { + get => _layerHeight; + set => RaiseAndSetIfChanged(ref _layerHeight, Math.Round(value, 2)); + } + + + /// <summary> + /// Gets or sets the bottom exposure in seconds + /// </summary> + public decimal BottomExposure + { + get => _bottomExposure; + set => RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2)); + } + + /// <summary> + /// Gets or sets the bottom exposure in seconds + /// </summary> + public decimal Exposure + { + get => _exposure; + set => RaiseAndSetIfChanged(ref _exposure, Math.Round(value, 2)); + } + + public ExposureItem() { } + + public ExposureItem(decimal layerHeight, decimal bottomExposure = 0, decimal exposure = 0) + { + _layerHeight = Math.Round(layerHeight, 2); + _bottomExposure = Math.Round(bottomExposure, 2); + _exposure = Math.Round(exposure, 2); + } + + public override string ToString() + { + return $"{nameof(LayerHeight)}: {LayerHeight}mm, {nameof(BottomExposure)}: {BottomExposure}s, {nameof(Exposure)}: {Exposure}s"; + } + } + #endregion + + #region Constants + public const byte ObjectsPerCache = 2; + #endregion + + #region Members + + //private decimal _displayWidth; + //private decimal _displayHeight; + private decimal _minimumLayerHeight = 0.03m; + private decimal _maximumLayerHeight = 0.10m; + private decimal _cacheRamSize = 1.5m; + private ExposureSetTypes _exposureSetType = ExposureSetTypes.Linear; + private decimal _bottomExposureStep = 0.5m; + private decimal _exposureStep = 0.2m; + private ObservableCollection<ExposureItem> _automaticExposureTable = new(); + private ObservableCollection<ExposureItem> _manualExposureTable = new(); + + #endregion + + #region Overrides + + public override bool CanROI => false; + + public override string Title => "Dynamic layer height"; + + public override string Description => + "Analyze and optimize the model with dynamic layer heights, larger angles will slice at lower layer height" + + " while more straight angles will slice larger layer height.\n" + + "Note: The model should be sliced at the lowest layer height possible (0.01mm).\n" + + "After this, do not apply any modification which reconstruct the z positions of the layers. " + + "Only few printers support this, make sure your is supported or else it will print a malformed model."; + + public override string ConfirmationText => + $"dynamic layers from layers {LayerIndexStart} through {LayerIndexEnd}?"; + + public override string ProgressTitle => + $"Analyzing and optimizing layers height from layers {LayerIndexStart} through {LayerIndexEnd}"; + + public override string ProgressAction => "Processed layers"; + + public override StringTag Validate(params object[] parameters) + { + var sb = new StringBuilder(); + + /*if (XYResolutionUm <= 0) + { + sb.AppendLine($"Display width and height must be a positive value."); + }*/ + + decimal layerHeight = (decimal) SlicerFile.LayerHeight; + if (_minimumLayerHeight < layerHeight) + { + sb.AppendLine( + $"Minimum layer height ({_minimumLayerHeight}mm) must be equal or higher than file layer height ({layerHeight}mm)"); + } + if (_minimumLayerHeight > _maximumLayerHeight) + { + sb.AppendLine( + $"Minimum layer height ({_minimumLayerHeight}mm) can't be higher than maximum layer height ({_maximumLayerHeight}mm)"); + } + if (layerHeight >= _maximumLayerHeight) + { + sb.AppendLine( + $"Maximum layer height ({_maximumLayerHeight}mm) can't be the same or less than current file layer height ({SlicerFile.LayerHeight}mm)"); + } + + var exposureTable = ExposureTableDictionary; + + for (layerHeight = (decimal) SlicerFile.LayerHeight; + layerHeight <= _maximumLayerHeight; + layerHeight = layerHeight + (decimal) SlicerFile.LayerHeight) + { + layerHeight = Math.Round(layerHeight, 2); + if (exposureTable.TryGetValue(layerHeight, out var exposure)) + { + if (exposure.BottomExposure <= 0 || exposure.Exposure <= 0) + { + sb.AppendLine($"Layer height {layerHeight}mm exposures must be a positive value, current: {exposure.BottomExposure}s/{exposure.Exposure}s"); + } + } + else + { + sb.AppendLine($"Layer height {layerHeight}mm exposures are missing."); + } + } + + return new StringTag(sb.ToString()); + } + + public override string ToString() + { + var result = $"[Layer Height: Min: {_minimumLayerHeight}mm Max: {_maximumLayerHeight}mm] [RAM: {_cacheRamSize}Gb] [Exposure type: {_exposureSetType}, Steps: {_bottomExposureStep}s/{_exposureStep}s]" + LayerRangeString; + if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}"; + return result; + } + + + #endregion + + #region Enums + + public enum ExposureSetTypes: byte + { + Linear, + Multiplier, + Manual + } + + public static Array ExposureSetTypeItems => Enum.GetValues(typeof(ExposureSetTypes)); + + #endregion + + #region Properties + + /*public decimal DisplayWidth + { + get => _displayWidth; + set + { + if (!RaiseAndSetIfChanged(ref _displayWidth, Math.Round(value, 2))) return; + //RaisePropertyChanged(nameof(XYResolutionUm)); + } + } + + public decimal DisplayHeight + { + get => _displayHeight; + set + { + if (!RaiseAndSetIfChanged(ref _displayHeight, Math.Round(value, 2))) return; + //RaisePropertyChanged(nameof(XYResolutionUm)); + } + } + + public decimal XYResolutionUm => DisplayWidth > 0 || DisplayHeight > 0 ? + Math.Round(Math.Max( + DisplayWidth / SlicerFile.ResolutionX, + DisplayHeight / SlicerFile.ResolutionY + ), 3) * 1000 + : 0; + */ + + public decimal MinimumLayerHeight + { + get => _minimumLayerHeight; + set + { + if (!RaiseAndSetIfChanged(ref _minimumLayerHeight, Math.Round(value, 2))) return; + //RaisePropertyChanged(nameof(ExposureData)); + //if (!IsExposureSetTypeManual) RebuildAutoExposureTable(); + } + } + + public decimal MaximumLayerHeight + { + get => _maximumLayerHeight; + set + { + if(!RaiseAndSetIfChanged(ref _maximumLayerHeight, Math.Round(value, 2))) return; + //RaisePropertyChanged(nameof(ExposureData)); + if(!IsExposureSetTypeManual) RebuildAutoExposureTable(); + } + } + + public uint CacheObjectCount => (uint) (_cacheRamSize * 1000000000L / SlicerFile.Resolution.GetArea() / ObjectsPerCache); + + public decimal CacheRAMSize + { + get => _cacheRamSize; + set + { + if(!RaiseAndSetIfChanged(ref _cacheRamSize, Math.Round(value, 2))) return; + RaisePropertyChanged(nameof(CacheObjectCount)); + } + } + + public ExposureSetTypes ExposureSetType + { + get => _exposureSetType; + set + { + if(!RaiseAndSetIfChanged(ref _exposureSetType, value)) return; + RaisePropertyChanged(nameof(IsExposureSetTypeManual)); + RaisePropertyChanged(nameof(ExposureTable)); + RaisePropertyChanged(nameof(ExposureTableDictionary)); + //RaisePropertyChanged(nameof(ExposureData)); + if (!IsExposureSetTypeManual) RebuildAutoExposureTable(); + } + } + + public bool IsExposureSetTypeManual => _exposureSetType == ExposureSetTypes.Manual; + + public decimal BottomExposureStep + { + get => _bottomExposureStep; + set + { + if(!RaiseAndSetIfChanged(ref _bottomExposureStep, value)) return; + //RaisePropertyChanged(nameof(ExposureData)); + if (!IsExposureSetTypeManual) RebuildAutoExposureTable(); + } + } + + public decimal ExposureStep + { + get => _exposureStep; + set + { + if(!RaiseAndSetIfChanged(ref _exposureStep, value)) return; + //RaisePropertyChanged(nameof(ExposureData)); + if (!IsExposureSetTypeManual) RebuildAutoExposureTable(); + } + } + + [XmlIgnore] + public ObservableCollection<ExposureItem> AutomaticExposureTable + { + get + { + if(_automaticExposureTable.Count == 0) RebuildAutoExposureTable(); + return _automaticExposureTable; + } + set => RaiseAndSetIfChanged(ref _automaticExposureTable, value); + } + + public ObservableCollection<ExposureItem> ManualExposureTable + { + get => _manualExposureTable; + set => RaiseAndSetIfChanged(ref _manualExposureTable, value); + } + + [XmlIgnore] + public ObservableCollection<ExposureItem> ExposureTable => IsExposureSetTypeManual ? _manualExposureTable : AutomaticExposureTable; + + /// <summary> + /// Gets the exposure table into a dictionary where key is the layer height + /// </summary> + [XmlIgnore] + public Dictionary<decimal, ExposureItem> ExposureTableDictionary + { + get + { + Dictionary<decimal, ExposureItem> dictionary = new(); + foreach (var exposure in ExposureTable) + { + dictionary.TryAdd(exposure.LayerHeight, exposure); + } + + return dictionary; + } + } + + public string ExposureData + { + get + { + StringBuilder sb = new(); + byte count = 0; + for (decimal layerHeight = (decimal) SlicerFile.LayerHeight; layerHeight <= _maximumLayerHeight; layerHeight+= (decimal)SlicerFile.LayerHeight) + { + decimal bottomExposure = 0; + decimal exposure = 0; + switch (_exposureSetType) + { + case ExposureSetTypes.Linear: + bottomExposure = (decimal)SlicerFile.BottomExposureTime + count * _bottomExposureStep; + exposure = (decimal)SlicerFile.ExposureTime + count * _exposureStep; + break; + case ExposureSetTypes.Multiplier: + bottomExposure = (decimal)SlicerFile.BottomExposureTime + (decimal)SlicerFile.BottomExposureTime * count * layerHeight * _bottomExposureStep; + exposure = (decimal)SlicerFile.BottomExposureTime + (decimal)SlicerFile.ExposureTime * count * layerHeight * _exposureStep; + break; + case ExposureSetTypes.Manual: + break; + default: + throw new ArgumentOutOfRangeException(); + } + sb.AppendLine($"{layerHeight:F2}mm: {bottomExposure:F2}s / {exposure:F2}s"); + count++; + } + return sb.ToString(); + } + } + + #endregion + + #region Constructor + + public OperationDynamicLayerHeight() + { + //InitManualTable(); + } + + public OperationDynamicLayerHeight(FileFormat slicerFile) : base(slicerFile) + { + InitManualTable(); + } + + public override void InitWithSlicerFile() + { + base.InitWithSlicerFile(); + + /*if (SlicerFile.DisplayWidth > 0) + _displayWidth = (decimal)SlicerFile.DisplayWidth; + if (SlicerFile.DisplayHeight > 0) + _displayHeight = (decimal)SlicerFile.DisplayHeight;*/ + + var layerHeight = (decimal)SlicerFile.LayerHeight; + if (_minimumLayerHeight < layerHeight) + { + _minimumLayerHeight = layerHeight; + } + if (layerHeight * 2 > _maximumLayerHeight) + { + _maximumLayerHeight = Math.Min((decimal) FileFormat.MaximumLayerHeight, _maximumLayerHeight*2); + } + } + + public void InitManualTable() + { + for (decimal layerHeight = (decimal)FileFormat.MinimumLayerHeight; + layerHeight <= (decimal) FileFormat.MaximumLayerHeight; + layerHeight += (decimal)FileFormat.MinimumLayerHeight) + { + var item = new ExposureItem(layerHeight); + if (layerHeight == (decimal) SlicerFile.LayerHeight) + { + item.BottomExposure = (decimal) SlicerFile.BottomExposureTime; + item.Exposure = (decimal) SlicerFile.ExposureTime; + } + _manualExposureTable.Add(item); + } + } + + #endregion + + #region Equality + + private bool Equals(OperationDynamicLayerHeight other) + { + return _minimumLayerHeight == other._minimumLayerHeight && _maximumLayerHeight == other._maximumLayerHeight && _cacheRamSize == other._cacheRamSize && _exposureSetType == other._exposureSetType && _bottomExposureStep == other._bottomExposureStep && _exposureStep == other._exposureStep && Equals(_manualExposureTable, other._manualExposureTable); + } + + public override bool Equals(object obj) + { + return ReferenceEquals(this, obj) || obj is OperationDynamicLayerHeight other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(_minimumLayerHeight, _maximumLayerHeight, _cacheRamSize, (int) _exposureSetType, _bottomExposureStep, _exposureStep, _manualExposureTable); + } + + #endregion + + #region Methods + + public void RebuildAutoExposureTable() + { + if (SlicerFile is null) return; + _automaticExposureTable.Clear(); + byte count = 0; + for (decimal layerHeight = (decimal)SlicerFile.LayerHeight; layerHeight <= _maximumLayerHeight; layerHeight += (decimal)SlicerFile.LayerHeight) + { + decimal bottomExposure = 0; + decimal exposure = 0; + switch (_exposureSetType) + { + case ExposureSetTypes.Linear: + bottomExposure = (decimal)SlicerFile.BottomExposureTime + count * _bottomExposureStep; + exposure = (decimal)SlicerFile.ExposureTime + count * _exposureStep; + break; + case ExposureSetTypes.Multiplier: + bottomExposure = (decimal)SlicerFile.BottomExposureTime + (decimal)SlicerFile.BottomExposureTime * count * layerHeight * _bottomExposureStep; + exposure = (decimal)SlicerFile.BottomExposureTime + (decimal)SlicerFile.ExposureTime * count * layerHeight * _exposureStep; + break; + case ExposureSetTypes.Manual: + break; + default: + throw new ArgumentOutOfRangeException(); + } + _automaticExposureTable.Add(new ExposureItem(layerHeight, Math.Round(bottomExposure, 2), Math.Round(exposure, 2))); + count++; + } + } + + protected override bool ExecuteInternally(OperationProgress progress) + { + Report report = new() + { + OldLayerCount = SlicerFile.LayerCount, + OldPrintTime = SlicerFile.PrintTimeOrComputed + }; + + var anchor = new Point(-1, -1); + using var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor); + + var matCache = new Mat[SlicerFile.LayerCount]; + var matThresholdCache = new Mat[SlicerFile.LayerCount]; + + List<Layer> layers = new(); + + using Mat matXor = new(); + Mat matXorSum = null; + Mat matSum = null; + + const byte _maximumErodes = 10; + //decimal xyResolutionUm = XYResolutionUm; + + //const double xyRes = 35; + //var stepAngle = Math.Atan(SlicerFile.LayerHeight*1000 / xyRes) * (180 / Math.PI); + //byte maximumErodes = (byte) (_maximumLayerHeight * 100 - (decimal) (slicerFile.LayerHeight * 100f)); + + void CacheLayers(uint fromLayerIndex) + { + for (int layerIndex = (int) fromLayerIndex-2; layerIndex >= 0; layerIndex--) // clean up only the required layers + { + if (matCache[layerIndex] is null) break; + matCache[layerIndex].Dispose(); + matCache[layerIndex] = null; + matThresholdCache[layerIndex].Dispose(); + matThresholdCache[layerIndex] = null; + } + Parallel.For(fromLayerIndex, Math.Min(fromLayerIndex + CacheObjectCount, SlicerFile.LayerCount), layerIndex => + { + if (matCache[layerIndex] is not null) return; // Already cached + matCache[layerIndex] = SlicerFile[layerIndex].LayerMat; + matThresholdCache[layerIndex] = new Mat(); + + // Clean AA + CvInvoke.Threshold(matCache[layerIndex], matThresholdCache[layerIndex], 128, 255, ThresholdType.Binary); + }); + } + + float GetLastPositionZ(float layerHeight) => layers.Count > 0 ? (float)Math.Round(layers[^1].PositionZ + layerHeight, 2) : layerHeight; + + (Mat, Mat) GetLayer(uint layerIndex) + { + if (matCache[layerIndex] is null) + { + CacheLayers(layerIndex); + } + + return (matCache[layerIndex], matThresholdCache[layerIndex]); + } + + void AddNewLayer(Mat mat, float layerHeight) + { + report.MaximumLayerHeight = Math.Max(report.MaximumLayerHeight, layerHeight); + var positionZ = GetLastPositionZ(layerHeight); + var layer = new Layer((uint) layers.Count, mat, SlicerFile) + { + IsModified = true, + PositionZ = positionZ + + }; + layers.Add(layer); + } + + void ReUseLayer(uint layerIndex) + { + SlicerFile[layerIndex].PositionZ = GetLastPositionZ(SlicerFile.LayerHeight); + SlicerFile[layerIndex].Index = (uint) layers.Count; + SlicerFile[layerIndex].IsModified = true; + layers.Add(SlicerFile[layerIndex]); + } + + for (uint layerIndex = 0; layerIndex < LayerIndexStart; layerIndex++) // Skip layers and re-use layers + { + ReUseLayer(layerIndex); + } + + for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; ) + { + Debug.WriteLine($"Head Layer: {layerIndex} ({SlicerFile.LayerHeight}mm)"); + + if (layerIndex == LayerIndexEnd) + { + ReUseLayer(layerIndex); + break; + } + + var currentLayerHeight = SlicerFile.LayerHeight; + byte layerSum = 1; + byte erodeCount = 0; + //byte maxErodeCount = 0; + //double maxSlop = 90; + matSum?.Dispose(); + matSum = null; + matXorSum?.Dispose(); + matXorSum = null; + + while (true) + { + progress.Token.ThrowIfCancellationRequested(); + progress.ProcessedItems = layerIndex; + + if (currentLayerHeight >= (float)_maximumLayerHeight || layerIndex == LayerIndexEnd) + { + // Cant perform any additional stack. Maximum layer height reached! + // Break this cycle and restart from same layer + matSum ??= GetLayer(layerIndex).Item1.Clone(); // This only happen when layer height is already the maximum supported layer height + layerIndex++; + break; + } + + var previousLayerHeight = currentLayerHeight; + currentLayerHeight = (float)Math.Round(currentLayerHeight + SlicerFile.LayerHeight, 2); + + var (mat1, mat1Threshold) = GetLayer(layerIndex); + var (mat2, mat2Threshold) = GetLayer(++layerIndex); + + Debug.Write($" Stacking layer: {layerIndex} ({currentLayerHeight}mm)"); + + matSum ??= mat1.Clone(); + + CvInvoke.BitwiseXor(mat1Threshold, mat2Threshold, matXor); + if (matXorSum is null) + { + matXorSum = matXor.Clone(); + } + else + { + CvInvoke.Add(matXorSum, matXor, matXorSum); + } + + var currentLayerHeigthUm = currentLayerHeight * 1000.0; + + if (CvInvoke.CountNonZero(matXorSum) > 0) // Layers are different + { + bool meetRequirement = false; + for (; erodeCount <= _maximumErodes; ) + { + erodeCount++; + //maxErodeCount = Math.Max(maxErodeCount, erodeCount); + + /*var slope = Math.Atan(currentLayerHeigthUm / (double) (xyResolutionUm * erodeCount)) * (180 / Math.PI); + var stepover = Math.Round(currentLayerHeigthUm / Math.Tan(slope * (Math.PI / 180))); + Debug.Write($" [Slope: {slope:F2} Stepover: {stepover} <= {xyResolutionUm} = {stepover <= (double) xyResolutionUm}]"); + + if (stepover > (double) xyResolutionUm) + { + meetRequirement = false; + break; + }*/ + + CvInvoke.Erode(matXorSum, matXor, kernel, anchor, 1, BorderType.Reflect101, default); + if (CvInvoke.CountNonZero(matXor) == 0) // Image pixels exhausted and got empty image, can pack and go next + { + meetRequirement = true; + break; + } + } + + //if ((!meetRequirement || erodeCount >= _maximumErodes) && _minimumLayerHeight < (decimal) currentLayerHeight + // To many pixels, image still not blank, pack the previous group and start again from current height + if (!meetRequirement && _minimumLayerHeight < (decimal) currentLayerHeight) + { + currentLayerHeight = previousLayerHeight; + Debug.WriteLine(string.Empty); + break; + } + } + else + { + //erodeCount++; // Safe check + Debug.Write(" [Equal layer]"); + } + + layerSum++; + CvInvoke.Add(matSum, mat2, matSum); + Debug.WriteLine(string.Empty); + } + + if (layerSum > 1) report.StackedLayers += layerSum; + Debug.WriteLine($" Packing {layerSum} layers with {currentLayerHeight}mm"); + // Add the result + /*var thisPosZ = Math.Round(layers[^1].PositionZ + currentLayerHeight, 2); + if (layers.Count > 0 && thisPosZ != slicerFile[layerIndex].PositionZ) + { + Debug.WriteLine($"{layerIndex}: ({thisPosZ} != {slicerFile[layerIndex].PositionZ}) Height mismatch!!"); + }*/ + var positionZ = GetLastPositionZ(currentLayerHeight); + if ((decimal)positionZ != (decimal)SlicerFile[layerIndex-1].PositionZ) + { + Debug.WriteLine($"{layerIndex}: ({positionZ}mm != {SlicerFile[layerIndex-1].PositionZ}mm) Height mismatch!!"); + throw new InvalidOperationException($"Model height integrity has been violated at layer {layerIndex}/{layers.Count} ({positionZ}mm != {SlicerFile[layerIndex - 1].PositionZ}mm), this operation will not proceed."); + } + AddNewLayer(matSum, currentLayerHeight); + } + + for (int i = (int) LayerIndexEnd; i >= 0; i--) + { + if(matCache[i] is null) break; + matCache[i].Dispose(); + matThresholdCache[i].Dispose(); + } + + for (uint layerIndex = LayerIndexEnd+1; layerIndex < SlicerFile.LayerCount; layerIndex++) // Add left-overs + { + ReUseLayer(layerIndex); + } + + SlicerFile.SuppressRebuildProperties = true; + SlicerFile.LayerManager.Layers = layers.ToArray(); + SlicerFile.LayerManager.RebuildLayersProperties(false); + + // Set exposures times + var exposureDictionary = ExposureTableDictionary; + for (uint layerIndex = 0; layerIndex < SlicerFile.LayerCount; layerIndex++) + { + float bottomExposure = SlicerFile.BottomExposureTime; + float exposure = SlicerFile.ExposureTime; + if(exposureDictionary.TryGetValue((decimal)SlicerFile[layerIndex].LayerHeight, out var item)) + { + bottomExposure = (float) item.BottomExposure; + exposure = (float) item.Exposure; + } + + SlicerFile[layerIndex].ExposureTime = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, bottomExposure, exposure); + } + //var layer = slicerFile.LayerManager.Layers[^1]; + + /*Debug.WriteLine(layer.ExposureTime); + Debug.WriteLine(layer.Index); + Debug.WriteLine(layer.Filename); + Debug.WriteLine(layer.IsNormalLayer); + Debug.WriteLine(layer.IsBottomLayer); + Debug.WriteLine(layer.LiftHeight); + Debug.WriteLine(layer.LiftSpeed); + Debug.WriteLine(layer.LightOffDelay); + Debug.WriteLine(layer.LightPWM); + Debug.WriteLine(layer.BoundingRectangle);*/ + //Debug.WriteLine(layer.LayerHeight); + //Debug.WriteLine(layer); + /*Debug.WriteLine(slicerFile.LayerManager); + foreach (var layer in slicerFile) + { + Debug.WriteLine(layer.Index); + }*/ + + SlicerFile.SuppressRebuildProperties = false; + + report.NewLayerCount = SlicerFile.LayerCount; + report.NewPrintTime = SlicerFile.PrintTimeOrComputed; + Tag = report; + + return true; + } + + #endregion + } +} diff --git a/UVtools.Core/Operations/OperationEditParameters.cs b/UVtools.Core/Operations/OperationEditParameters.cs index b31e017..b59de70 100644 --- a/UVtools.Core/Operations/OperationEditParameters.cs +++ b/UVtools.Core/Operations/OperationEditParameters.cs @@ -19,6 +19,9 @@ namespace UVtools.Core.Operations { #region Members private bool _perLayerOverride; + private uint _setNumberOfLayer = 1; + private uint _skipNumberOfLayer = 0; + #endregion #region Overrides @@ -92,23 +95,73 @@ namespace UVtools.Core.Operations get => _perLayerOverride; set => RaiseAndSetIfChanged(ref _perLayerOverride, value); } - #endregion - public OperationEditParameters() + /// <summary> + /// Gets or sets the number of sequential layers to set the parameters + /// </summary> + public uint SetNumberOfLayer { + get => _setNumberOfLayer; + set => RaiseAndSetIfChanged(ref _setNumberOfLayer, value); } - public OperationEditParameters(FileFormat.PrintParameterModifier[] modifiers) + /// <summary> + /// Gets or sets the number of sequential layers to skip after set a layer + /// </summary> + public uint SkipNumberOfLayer { - Modifiers = modifiers; + get => _skipNumberOfLayer; + set => RaiseAndSetIfChanged(ref _skipNumberOfLayer, value); } + #endregion + + #region Constructor + + public OperationEditParameters() { } + + public OperationEditParameters(FileFormat slicerFile) : base(slicerFile) { } + + public override void InitWithSlicerFile() + { + base.InitWithSlicerFile(); + SlicerFile.RefreshPrintParametersModifiersValues(); + Modifiers = SlicerFile.PrintParameterModifiers; + } + + #endregion + #region Methods - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - slicerFile.EditPrintParameters(this); - return true; + if (PerLayerOverride) + { + uint setLayers = 0; + for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++) + { + SlicerFile[layerIndex].SetValuesFromPrintParametersModifiers(Modifiers); + if (SkipNumberOfLayer <= 0) continue; + setLayers++; + if (setLayers >= SetNumberOfLayer) + { + setLayers = 0; + layerIndex += SkipNumberOfLayer; + } + } + + foreach (var modifier in Modifiers) + { + modifier.OldValue = modifier.NewValue; + } + SlicerFile.RebuildGCode(); + } + else + { + SlicerFile.SetValuesFromPrintParametersModifiers(); + } + + return !progress.Token.IsCancellationRequested; } #endregion diff --git a/UVtools.Core/Operations/OperationFlip.cs b/UVtools.Core/Operations/OperationFlip.cs index 5381d1b..26fbf18 100644 --- a/UVtools.Core/Operations/OperationFlip.cs +++ b/UVtools.Core/Operations/OperationFlip.cs @@ -87,6 +87,14 @@ namespace UVtools.Core.Operations } #endregion + #region Constructor + + public OperationFlip() { } + + public OperationFlip(FileFormat slicerFile) : base(slicerFile) { } + + #endregion + #region Equality protected bool Equals(OperationFlip other) @@ -113,24 +121,22 @@ namespace UVtools.Core.Operations #endregion #region Methods - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(ProgressAction, LayerRangeCount); Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex => { if (progress.Token.IsCancellationRequested) return; - using var mat = slicerFile[layerIndex].LayerMat; + using var mat = SlicerFile[layerIndex].LayerMat; Execute(mat); - slicerFile[layerIndex].LayerMat = mat; + SlicerFile[layerIndex].LayerMat = mat; lock (progress.Mutex) { progress++; } }); - progress.Token.ThrowIfCancellationRequested(); - return true; + + return !progress.Token.IsCancellationRequested; } public override bool Execute(Mat mat, params object[] arguments) diff --git a/UVtools.Core/Operations/OperationInfill.cs b/UVtools.Core/Operations/OperationInfill.cs index b05891e..a655c71 100644 --- a/UVtools.Core/Operations/OperationInfill.cs +++ b/UVtools.Core/Operations/OperationInfill.cs @@ -97,24 +97,44 @@ namespace UVtools.Core.Operations #endregion + #region Constructor + + public OperationInfill() { } + + public OperationInfill(FileFormat slicerFile) : base(slicerFile) { } + + #endregion + #region Equality + private bool Equals(OperationInfill other) + { + return _infillType == other._infillType && _wallThickness == other._wallThickness && _infillThickness == other._infillThickness && _infillSpacing == other._infillSpacing && _infillBrightness == other._infillBrightness; + } + + public override bool Equals(object obj) + { + return ReferenceEquals(this, obj) || obj is OperationInfill other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine((int) _infillType, _wallThickness, _infillThickness, _infillSpacing, _infillBrightness); + } + #endregion #region Methods - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(ProgressAction, LayerRangeCount); - Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex => { if (progress.Token.IsCancellationRequested) return; - using var mat = slicerFile[layerIndex].LayerMat; + using var mat = SlicerFile[layerIndex].LayerMat; Execute(mat, layerIndex); - slicerFile[layerIndex].LayerMat = mat; + SlicerFile[layerIndex].LayerMat = mat; lock (progress.Mutex) { @@ -122,7 +142,7 @@ namespace UVtools.Core.Operations } }); - return true; + return !progress.Token.IsCancellationRequested; } public override bool Execute(Mat mat, params object[] arguments) diff --git a/UVtools.Core/Operations/OperationLayerClone.cs b/UVtools.Core/Operations/OperationLayerClone.cs index 5ec7c28..9eca95c 100644 --- a/UVtools.Core/Operations/OperationLayerClone.cs +++ b/UVtools.Core/Operations/OperationLayerClone.cs @@ -76,11 +76,19 @@ namespace UVtools.Core.Operations #endregion + #region Constructor + + public OperationLayerClone() { } + + public OperationLayerClone(FileFormat slicerFile) : base(slicerFile) { } + + #endregion + #region Methods - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - var oldLayers = slicerFile.LayerManager.Layers; + var oldLayers = SlicerFile.LayerManager.Layers; uint totalClones = (LayerIndexEnd - LayerIndexStart + 1) * Clones; uint newLayerCount = (uint) (oldLayers.Length + totalClones); @@ -107,11 +115,9 @@ namespace UVtools.Core.Operations newLayerIndex++; } - slicerFile.LayerManager.Layers = layers; - - progress.Token.ThrowIfCancellationRequested(); + SlicerFile.LayerManager.Layers = layers; - return true; + return !progress.Token.IsCancellationRequested; } #endregion diff --git a/UVtools.Core/Operations/OperationLayerImport.cs b/UVtools.Core/Operations/OperationLayerImport.cs index a540004..86b2b94 100644 --- a/UVtools.Core/Operations/OperationLayerImport.cs +++ b/UVtools.Core/Operations/OperationLayerImport.cs @@ -39,12 +39,14 @@ namespace UVtools.Core.Operations } #endregion + #region Members private ImportTypes _importType = ImportTypes.Stack; private uint _startLayerIndex; private bool _extendBeyondLayerCount = true; private bool _discardUnmodifiedLayers; private ushort _stackMargin = 50; private ObservableCollection<StringTag> _files = new(); + #endregion #region Overrides @@ -115,9 +117,6 @@ namespace UVtools.Core.Operations #region Properties - [XmlIgnore] - public Size FileResolution { get; } - public ImportTypes ImportType { get => _importType; @@ -172,14 +171,11 @@ namespace UVtools.Core.Operations #region Constructor - public OperationLayerImport() - { - } + public OperationLayerImport() { } + + public OperationLayerImport(FileFormat slicerFile) : base(slicerFile) { } + - public OperationLayerImport(Size fileResolution) - { - FileResolution = fileResolution; - } #endregion #region Methods @@ -224,10 +220,10 @@ namespace UVtools.Core.Operations return result; } - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - progress ??= new OperationProgress(); - slicerFile.SuppressRebuildProperties = true; + progress.ItemCount = 0; + SlicerFile.SuppressRebuildProperties = true; List<FileFormat> fileFormats = new(); List<KeyValuePair<uint, string>> keyImage = new(); @@ -289,7 +285,7 @@ namespace UVtools.Core.Operations if (_importType == ImportTypes.Stack) { - new OperationMove(slicerFile, Enumerations.Anchor.TopLeft){LayerIndexEnd = slicerFile.LastLayerIndex}.Execute(slicerFile, progress); + new OperationMove(SlicerFile, Enumerations.Anchor.TopLeft).Execute(progress); } foreach (var fileFormat in fileFormats) @@ -299,7 +295,7 @@ namespace UVtools.Core.Operations fileFormat.Decode(fileFormat.FileFullPath, progress); } - var boundingRectangle = slicerFile.LayerManager.GetBoundingRectangle(progress); + var boundingRectangle = SlicerFile.LayerManager.GetBoundingRectangle(progress); var fileFormatBoundingRectangle = fileFormat.LayerManager.GetBoundingRectangle(progress); var roiRectangle = Rectangle.Empty; @@ -307,26 +303,26 @@ namespace UVtools.Core.Operations switch (_importType) { case ImportTypes.Insert: - if (slicerFile.Resolution != fileFormat.Resolution && - (slicerFile.Resolution.Width < fileFormat.LayerManager.BoundingRectangle.Width || - slicerFile.Resolution.Height < fileFormat.LayerManager.BoundingRectangle.Height)) continue; - slicerFile.LayerManager.Reallocate(_startLayerIndex, fileFormat.LayerCount); + if (SlicerFile.Resolution != fileFormat.Resolution && + (SlicerFile.Resolution.Width < fileFormat.LayerManager.BoundingRectangle.Width || + SlicerFile.Resolution.Height < fileFormat.LayerManager.BoundingRectangle.Height)) continue; + SlicerFile.LayerManager.Reallocate(_startLayerIndex, fileFormat.LayerCount); break; case ImportTypes.Replace: case ImportTypes.Stack: - if (slicerFile.Resolution != fileFormat.Resolution && - (slicerFile.Resolution.Width < fileFormat.LayerManager.BoundingRectangle.Width || - slicerFile.Resolution.Height < fileFormat.LayerManager.BoundingRectangle.Height)) continue; + if (SlicerFile.Resolution != fileFormat.Resolution && + (SlicerFile.Resolution.Width < fileFormat.LayerManager.BoundingRectangle.Width || + SlicerFile.Resolution.Height < fileFormat.LayerManager.BoundingRectangle.Height)) continue; - if(fileFormatBoundingRectangle.Width >= slicerFile.ResolutionX || fileFormatBoundingRectangle.Height >= slicerFile.ResolutionY) + if(fileFormatBoundingRectangle.Width >= SlicerFile.ResolutionX || fileFormatBoundingRectangle.Height >= SlicerFile.ResolutionY) continue; int x = 0; int y = 0; if (boundingRectangle.Right + _stackMargin + fileFormatBoundingRectangle.Width < - slicerFile.ResolutionX) + SlicerFile.ResolutionX) { x = boundingRectangle.Right + _stackMargin; } @@ -335,29 +331,29 @@ namespace UVtools.Core.Operations y = boundingRectangle.Bottom + _stackMargin; } - if (y >= slicerFile.ResolutionY) + if (y >= SlicerFile.ResolutionY) continue; roiRectangle = new Rectangle(x, y, fileFormatBoundingRectangle.Width, fileFormatBoundingRectangle.Height); if (_extendBeyondLayerCount) { - int layerCountDifference = (int) (_startLayerIndex + fileFormat.LayerCount - slicerFile.LayerCount); + int layerCountDifference = (int) (_startLayerIndex + fileFormat.LayerCount - SlicerFile.LayerCount); if (layerCountDifference > 0) { - slicerFile.LayerManager.ReallocateEnd((uint) layerCountDifference, _importType == ImportTypes.Stack); + SlicerFile.LayerManager.ReallocateEnd((uint) layerCountDifference, _importType == ImportTypes.Stack); } } break; case ImportTypes.Merge: - if (slicerFile.Resolution != fileFormat.Resolution) continue; + if (SlicerFile.Resolution != fileFormat.Resolution) continue; if (_extendBeyondLayerCount) { - int layerCountDifference = (int)(_startLayerIndex + fileFormat.LayerCount - slicerFile.LayerCount); + int layerCountDifference = (int)(_startLayerIndex + fileFormat.LayerCount - SlicerFile.LayerCount); if (layerCountDifference > 0) { - slicerFile.LayerManager.ReallocateEnd((uint)layerCountDifference, true); + SlicerFile.LayerManager.ReallocateEnd((uint)layerCountDifference, true); } } break; @@ -365,7 +361,7 @@ namespace UVtools.Core.Operations case ImportTypes.BitwiseAnd: case ImportTypes.BitwiseOr: case ImportTypes.BitwiseXOr: - if (slicerFile.Resolution != fileFormat.Resolution) continue; + if (SlicerFile.Resolution != fileFormat.Resolution) continue; break; } @@ -381,88 +377,88 @@ namespace UVtools.Core.Operations { case ImportTypes.Insert: { - if (layerIndex >= slicerFile.LayerCount) return; - if (slicerFile.Resolution == fileFormat.Resolution) + if (layerIndex >= SlicerFile.LayerCount) return; + if (SlicerFile.Resolution == fileFormat.Resolution) { - slicerFile[layerIndex] = fileFormat[i]; + SlicerFile[layerIndex] = fileFormat[i]; break; } using var layer = fileFormat[i].LayerMat; - using var layerRoi = layer.RoiFromCenter(slicerFile.Resolution); - slicerFile[layerIndex] = new Layer(layerIndex, layerRoi, slicerFile); + using var layerRoi = layer.RoiFromCenter(SlicerFile.Resolution); + SlicerFile[layerIndex] = new Layer(layerIndex, layerRoi, SlicerFile); break; } case ImportTypes.Replace: { - if (layerIndex >= slicerFile.LayerCount) return; - if (slicerFile.Resolution == fileFormat.Resolution) + if (layerIndex >= SlicerFile.LayerCount) return; + if (SlicerFile.Resolution == fileFormat.Resolution) { - slicerFile[layerIndex] = fileFormat[i]; + SlicerFile[layerIndex] = fileFormat[i]; break; } using var layer = fileFormat[i].LayerMat; - using var layerRoi = layer.RoiFromCenter(slicerFile.Resolution); - slicerFile[layerIndex] = new Layer(layerIndex, layerRoi, slicerFile); + using var layerRoi = layer.RoiFromCenter(SlicerFile.Resolution); + SlicerFile[layerIndex] = new Layer(layerIndex, layerRoi, SlicerFile); break; } case ImportTypes.Stack: { - if (layerIndex >= slicerFile.LayerCount) return; - using var mat = slicerFile[layerIndex].LayerMat; + if (layerIndex >= SlicerFile.LayerCount) return; + using var mat = SlicerFile[layerIndex].LayerMat; using var importMat = fileFormat[i].LayerMat; var matRoi = new Mat(mat, roiRectangle); var importMatRoi = new Mat(importMat, fileFormatBoundingRectangle); importMatRoi.CopyTo(matRoi); - slicerFile[layerIndex].LayerMat = mat; + SlicerFile[layerIndex].LayerMat = mat; break; } case ImportTypes.Merge: { - if (layerIndex >= slicerFile.LayerCount) return; - using var originalMat = slicerFile[layerIndex].LayerMat; + if (layerIndex >= SlicerFile.LayerCount) return; + using var originalMat = SlicerFile[layerIndex].LayerMat; using var newMat = fileFormat[i].LayerMat; CvInvoke.Add(originalMat, newMat, newMat); - slicerFile[layerIndex].LayerMat = newMat; + SlicerFile[layerIndex].LayerMat = newMat; break; } case ImportTypes.Subtract: { - if (layerIndex >= slicerFile.LayerCount) return; - using var originalMat = slicerFile[layerIndex].LayerMat; + if (layerIndex >= SlicerFile.LayerCount) return; + using var originalMat = SlicerFile[layerIndex].LayerMat; using var newMat = fileFormat[i].LayerMat; CvInvoke.Subtract(originalMat, newMat, newMat); - slicerFile[layerIndex].LayerMat = newMat; + SlicerFile[layerIndex].LayerMat = newMat; break; } case ImportTypes.BitwiseAnd: { - if (layerIndex >= slicerFile.LayerCount) return; - using var originalMat = slicerFile[layerIndex].LayerMat; + if (layerIndex >= SlicerFile.LayerCount) return; + using var originalMat = SlicerFile[layerIndex].LayerMat; using var newMat = fileFormat[i].LayerMat; CvInvoke.BitwiseAnd(originalMat, newMat, newMat); - slicerFile[layerIndex].LayerMat = newMat; + SlicerFile[layerIndex].LayerMat = newMat; break; } case ImportTypes.BitwiseOr: { - if (layerIndex >= slicerFile.LayerCount) return; - using var originalMat = slicerFile[layerIndex].LayerMat; + if (layerIndex >= SlicerFile.LayerCount) return; + using var originalMat = SlicerFile[layerIndex].LayerMat; using var newMat = fileFormat[i].LayerMat; CvInvoke.BitwiseOr(originalMat, newMat, newMat); - slicerFile[layerIndex].LayerMat = newMat; + SlicerFile[layerIndex].LayerMat = newMat; break; } case ImportTypes.BitwiseXOr: { - if (layerIndex >= slicerFile.LayerCount) return; - using var originalMat = slicerFile[layerIndex].LayerMat; + if (layerIndex >= SlicerFile.LayerCount) return; + using var originalMat = SlicerFile[layerIndex].LayerMat; using var newMat = fileFormat[i].LayerMat; CvInvoke.BitwiseXor(originalMat, newMat, newMat); - slicerFile[layerIndex].LayerMat = newMat; + SlicerFile[layerIndex].LayerMat = newMat; break; } default: @@ -483,20 +479,20 @@ namespace UVtools.Core.Operations if (_importType == ImportTypes.Stack) { - new OperationMove(slicerFile) { LayerIndexEnd = slicerFile.LastLayerIndex }.Execute(slicerFile, progress); + new OperationMove(SlicerFile).Execute(progress); } if (lastProcessedLayerIndex <= -1) return false; - if (lastProcessedLayerIndex + 1 < slicerFile.LayerCount && _discardUnmodifiedLayers) + if (lastProcessedLayerIndex + 1 < SlicerFile.LayerCount && _discardUnmodifiedLayers) { - slicerFile.LayerManager.ReallocateRange(0, (uint) lastProcessedLayerIndex); + SlicerFile.LayerManager.ReallocateRange(0, (uint) lastProcessedLayerIndex); } - slicerFile.LayerManager.RebuildLayersProperties(); - slicerFile.SuppressRebuildProperties = false; + SlicerFile.LayerManager.RebuildLayersProperties(); + SlicerFile.SuppressRebuildProperties = false; - return true; + return !progress.Token.IsCancellationRequested; } #endregion diff --git a/UVtools.Core/Operations/OperationLayerReHeight.cs b/UVtools.Core/Operations/OperationLayerReHeight.cs index bcd93d5..a68e367 100644 --- a/UVtools.Core/Operations/OperationLayerReHeight.cs +++ b/UVtools.Core/Operations/OperationLayerReHeight.cs @@ -99,6 +99,14 @@ namespace UVtools.Core.Operations } #endregion + #region Constructor + + public OperationLayerReHeight() { } + + public OperationLayerReHeight(FileFormat slicerFile) : base(slicerFile) { } + + #endregion + #region Subclasses public class OperationLayerReHeightItem { @@ -124,12 +132,11 @@ namespace UVtools.Core.Operations #endregion #region Methods - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(ProgressAction, Item.LayerCount); + progress.ItemCount = Item.LayerCount; - var oldLayers = slicerFile.LayerManager.Layers; + var oldLayers = SlicerFile.LayerManager.Layers; var layers = new Layer[Item.LayerCount]; @@ -171,10 +178,10 @@ namespace UVtools.Core.Operations } } - slicerFile.LayerManager.Layers = layers; - slicerFile.LayerHeight = (float)Item.LayerHeight; + SlicerFile.LayerManager.Layers = layers; + SlicerFile.LayerHeight = (float)Item.LayerHeight; - return true; + return !progress.Token.IsCancellationRequested; } #endregion } diff --git a/UVtools.Core/Operations/OperationLayerRemove.cs b/UVtools.Core/Operations/OperationLayerRemove.cs index 5fa1907..117712a 100644 --- a/UVtools.Core/Operations/OperationLayerRemove.cs +++ b/UVtools.Core/Operations/OperationLayerRemove.cs @@ -47,18 +47,26 @@ namespace UVtools.Core.Operations #endregion + #region Constructor + + public OperationLayerRemove() { } + + public OperationLayerRemove(FileFormat slicerFile) : base(slicerFile) { } + + #endregion + #region Methods - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - progress ??= new OperationProgress(false); + progress.CanCancel = false; var layersRemove = new List<uint>(); for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++) { layersRemove.Add(layerIndex); } - return RemoveLayers(slicerFile, layersRemove, progress); + return RemoveLayers(SlicerFile, layersRemove, progress); } public static bool RemoveLayers(FileFormat slicerFile, List<uint> layersRemove, OperationProgress progress = null) @@ -70,7 +78,7 @@ namespace UVtools.Core.Operations progress.Reset("removed layers", (uint)layersRemove.Count); var oldLayers = slicerFile.LayerManager.Layers; - float layerHeight = slicerFile.LayerHeight; + var layerHeight = slicerFile.LayerHeight; var layers = new Layer[oldLayers.Length - layersRemove.Count]; @@ -92,7 +100,7 @@ namespace UVtools.Core.Operations } else { - posZ = (float)Math.Round(layers[newLayerIndex - 1].PositionZ + layerHeight, 2); + posZ = layers[newLayerIndex - 1].PositionZ + layerHeight; } } diff --git a/UVtools.Core/Operations/OperationMask.cs b/UVtools.Core/Operations/OperationMask.cs index 972157f..3309e2f 100644 --- a/UVtools.Core/Operations/OperationMask.cs +++ b/UVtools.Core/Operations/OperationMask.cs @@ -57,6 +57,14 @@ namespace UVtools.Core.Operations public bool HaveMask => !(Mask is null); #endregion + #region Constructor + + public OperationMask() { } + + public OperationMask(FileFormat slicerFile) : base(slicerFile) { } + + #endregion + #region Methods public void InvertMask() @@ -65,25 +73,21 @@ namespace UVtools.Core.Operations CvInvoke.BitwiseNot(Mask, Mask); } - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(ProgressAction, LayerRangeCount); - Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex => { if (progress.Token.IsCancellationRequested) return; - using var mat = slicerFile[layerIndex].LayerMat; + using var mat = SlicerFile[layerIndex].LayerMat; Execute(mat); - slicerFile[layerIndex].LayerMat = mat; + SlicerFile[layerIndex].LayerMat = mat; lock (progress.Mutex) { progress++; } }); - progress.Token.ThrowIfCancellationRequested(); - return true; + return !progress.Token.IsCancellationRequested; } public override bool Execute(Mat mat, params object[] arguments) diff --git a/UVtools.Core/Operations/OperationMorph.cs b/UVtools.Core/Operations/OperationMorph.cs index 4ff99ad..4c0ef1a 100644 --- a/UVtools.Core/Operations/OperationMorph.cs +++ b/UVtools.Core/Operations/OperationMorph.cs @@ -109,6 +109,14 @@ namespace UVtools.Core.Operations #endregion + #region Constructor + + public OperationMorph() { } + + public OperationMorph(FileFormat slicerFile) : base(slicerFile) { } + + #endregion + #region Equality private bool Equals(OperationMorph other) @@ -137,11 +145,8 @@ namespace UVtools.Core.Operations #region Methods - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(ProgressAction, LayerRangeCount); - var isFade = Chamfer; LayerManager.MutateGetVarsIterationChamfer( LayerIndexStart, @@ -160,17 +165,17 @@ namespace UVtools.Core.Operations if (progress.Token.IsCancellationRequested) return; int iterations = LayerManager.MutateGetIterationVar(isFade, (int)IterationsStart, (int)IterationsEnd, iterationSteps, maxIteration, LayerIndexStart, (uint)layerIndex); - using var mat = slicerFile[layerIndex].LayerMat; + using var mat = SlicerFile[layerIndex].LayerMat; Execute(mat, iterations); - slicerFile[layerIndex].LayerMat = mat; + SlicerFile[layerIndex].LayerMat = mat; lock (progress.Mutex) { progress++; } }); - progress.Token.ThrowIfCancellationRequested(); - return true; + + return !progress.Token.IsCancellationRequested; } public override bool Execute(Mat mat, params object[] arguments) diff --git a/UVtools.Core/Operations/OperationMove.cs b/UVtools.Core/Operations/OperationMove.cs index 12bcae0..30a3dee 100644 --- a/UVtools.Core/Operations/OperationMove.cs +++ b/UVtools.Core/Operations/OperationMove.cs @@ -170,6 +170,31 @@ namespace UVtools.Core.Operations #endregion + #region Constructor + + public OperationMove() { } + + public OperationMove(FileFormat slicerFile, Enumerations.Anchor anchor = Enumerations.Anchor.MiddleCenter) : base(slicerFile) + { + ROI = slicerFile.LayerManager.BoundingRectangle; + _imageWidth = slicerFile.ResolutionX; + _imageHeight = slicerFile.ResolutionY; + _anchor = anchor; + } + + public OperationMove(FileFormat slicerFile, Rectangle srcRoi, Enumerations.Anchor anchor = Enumerations.Anchor.MiddleCenter) : this(slicerFile, anchor) + { + if(!srcRoi.IsEmpty) ROI = srcRoi; + } + + /*public OperationMove(FileFormat slicerFile, Rectangle srcRoi, Mat mat, Enumerations.Anchor anchor = Enumerations.Anchor.MiddleCenter) : this(slicerFile, srcRoi, anchor) + { + ImageWidth = (uint) mat.Width; + ImageHeight = (uint) mat.Height; + }*/ + + #endregion + #region Methods public void CalculateDstRoi() { @@ -214,42 +239,15 @@ namespace UVtools.Core.Operations _dstRoi.Y += MarginTop; _dstRoi.Y -= MarginBottom; - IsWithinBoundary = !(DstRoi.IsEmpty || DstRoi.X < 0 || DstRoi.Y < 0 || - DstRoi.Width == 0 || DstRoi.Right > ImageWidth || - DstRoi.Height == 0 || DstRoi.Bottom > ImageHeight); + IsWithinBoundary = !(_dstRoi.IsEmpty || _dstRoi.X < 0 || _dstRoi.Y < 0 || + _dstRoi.Width == 0 || _dstRoi.Right > ImageWidth || + _dstRoi.Height == 0 || _dstRoi.Bottom > ImageHeight); RaisePropertyChanged(nameof(DstRoi)); RaisePropertyChanged(nameof(LocationXStr)); RaisePropertyChanged(nameof(LocationYStr)); } - public OperationMove() - { - } - - public OperationMove(FileFormat slicerFile, Enumerations.Anchor anchor = Enumerations.Anchor.MiddleCenter) - { - ROI = slicerFile.LayerManager.BoundingRectangle; - ImageWidth = slicerFile.ResolutionX; - ImageHeight = slicerFile.ResolutionY; - Anchor = anchor; - } - - public OperationMove(Rectangle srcRoi, uint imageWidth, uint imageHeight, Enumerations.Anchor anchor = Enumerations.Anchor.MiddleCenter) - { - ROI = srcRoi; - ImageWidth = imageWidth; - ImageHeight = imageHeight; - Anchor = anchor; - } - - public OperationMove(Rectangle srcRoi, Size resolution, Enumerations.Anchor anchor = Enumerations.Anchor.MiddleCenter) - { - ROI = srcRoi; - ImageWidth = (uint)resolution.Width; - ImageHeight = (uint)resolution.Height; - Anchor = anchor; - } public void Reset() { @@ -271,21 +269,19 @@ namespace UVtools.Core.Operations return IsWithinBoundary; } - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(ProgressAction, LayerRangeCount); - - if (ROI == Rectangle.Empty) ROI = slicerFile.LayerManager.GetBoundingRectangle(progress); + if (ROI.IsEmpty) ROI = SlicerFile.LayerManager.GetBoundingRectangle(progress); + CalculateDstRoi(); Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex => { if (progress.Token.IsCancellationRequested) return; - using (var mat = slicerFile[layerIndex].LayerMat) + using (var mat = SlicerFile[layerIndex].LayerMat) { Execute(mat); - slicerFile[layerIndex].LayerMat = mat; + SlicerFile[layerIndex].LayerMat = mat; } lock (progress.Mutex) @@ -294,18 +290,15 @@ namespace UVtools.Core.Operations } }); - slicerFile.LayerManager.BoundingRectangle = Rectangle.Empty; + SlicerFile.LayerManager.BoundingRectangle = Rectangle.Empty; - progress.Token.ThrowIfCancellationRequested(); - - return true; + return !progress.Token.IsCancellationRequested; } public override bool Execute(Mat mat, params object[] arguments) { - - if (ImageWidth == 0) ImageWidth = (uint) mat.Width; - if (ImageHeight == 0) ImageHeight = (uint) mat.Height; + if (_imageWidth == 0) _imageWidth = (uint) mat.Width; + if (_imageHeight == 0) _imageHeight = (uint) mat.Height; using var srcRoi = new Mat(mat, ROI); using var dstRoi = new Mat(mat, DstRoi); diff --git a/UVtools.Core/Operations/OperationPattern.cs b/UVtools.Core/Operations/OperationPattern.cs index 97d75f7..3cd36bf 100644 --- a/UVtools.Core/Operations/OperationPattern.cs +++ b/UVtools.Core/Operations/OperationPattern.cs @@ -186,19 +186,21 @@ namespace UVtools.Core.Operations public Size GetPatternVolume => new(Cols * ROI.Width + (Cols - 1) * ColSpacing, Rows * ROI.Height + (Rows - 1) * RowSpacing); #endregion - public OperationPattern() - { - } + #region Constructor - public OperationPattern(Rectangle srcRoi, Size resolution) + public OperationPattern() { } + + public OperationPattern(FileFormat slicerFile, Rectangle srcRoi = default) : base(slicerFile) { - ImageWidth = (uint) resolution.Width; - ImageHeight = (uint) resolution.Height; + ImageWidth = slicerFile.ResolutionX; + ImageHeight = slicerFile.ResolutionY; - SetRoi(srcRoi); + SetRoi(srcRoi.IsEmpty ? slicerFile.BoundingRectangle : srcRoi); Fill(); } + #endregion + #region Methods public void SetAnchor(byte value) { @@ -272,16 +274,13 @@ namespace UVtools.Core.Operations return result; } - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(ProgressAction, LayerRangeCount); - Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex => { if (progress.Token.IsCancellationRequested) return; - using var mat = slicerFile[layerIndex].LayerMat; + using var mat = SlicerFile[layerIndex].LayerMat; using var layerRoi = new Mat(mat, ROI); using var dstLayer = mat.CloneBlank(); for (ushort col = 0; col < Cols; col++) @@ -292,7 +291,7 @@ namespace UVtools.Core.Operations layerRoi.CopyTo(dstRoi); } //Execute(mat); - slicerFile[layerIndex].LayerMat = dstLayer; + SlicerFile[layerIndex].LayerMat = dstLayer; lock (progress.Mutex) { @@ -300,19 +299,19 @@ namespace UVtools.Core.Operations } }); - slicerFile.LayerManager.BoundingRectangle = Rectangle.Empty; + SlicerFile.LayerManager.BoundingRectangle = Rectangle.Empty; progress.Token.ThrowIfCancellationRequested(); if (Anchor == Enumerations.Anchor.None) return true; - var operationMove = new OperationMove(slicerFile.LayerManager.BoundingRectangle, ImageWidth, ImageHeight, Anchor) + var operationMove = new OperationMove(SlicerFile, Anchor) { LayerIndexStart = LayerIndexStart, LayerIndexEnd = LayerIndexEnd }; - operationMove.Execute(slicerFile, progress); + operationMove.Execute(progress); - return true; + return !progress.Token.IsCancellationRequested; } /*public override bool Execute(Mat mat, params object[] arguments) diff --git a/UVtools.Core/Operations/OperationPixelDimming.cs b/UVtools.Core/Operations/OperationPixelDimming.cs index 25c16e8..4e40285 100644 --- a/UVtools.Core/Operations/OperationPixelDimming.cs +++ b/UVtools.Core/Operations/OperationPixelDimming.cs @@ -138,6 +138,14 @@ namespace UVtools.Core.Operations } #endregion + #region Constructor + + public OperationPixelDimming() { } + + public OperationPixelDimming(FileFormat slicerFile) : base(slicerFile) { } + + #endregion + #region Properties public uint WallThicknessStart @@ -534,11 +542,8 @@ namespace UVtools.Core.Operations } } - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(ProgressAction, LayerRangeCount); - if (Pattern is null) { Pattern = new Matrix<byte>(2, 2) @@ -560,7 +565,7 @@ namespace UVtools.Core.Operations AlternatePattern ??= Pattern; - using var blankMat = EmguExtensions.InitMat(slicerFile.Resolution); + using var blankMat = EmguExtensions.InitMat(SlicerFile.Resolution); using var matPattern = blankMat.CloneBlank(); using var matAlternatePattern = blankMat.CloneBlank(); var target = GetRoiOrDefault(blankMat); @@ -573,9 +578,9 @@ namespace UVtools.Core.Operations Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex => { if (progress.Token.IsCancellationRequested) return; - using var mat = slicerFile[layerIndex].LayerMat; + using var mat = SlicerFile[layerIndex].LayerMat; Execute(mat, layerIndex, patternMask, alternatePatternMask); - slicerFile[layerIndex].LayerMat = mat; + SlicerFile[layerIndex].LayerMat = mat; lock (progress.Mutex) { @@ -583,8 +588,7 @@ namespace UVtools.Core.Operations } }); - progress.Token.ThrowIfCancellationRequested(); - return true; + return !progress.Token.IsCancellationRequested; } public override bool Execute(Mat mat, params object[] arguments) diff --git a/UVtools.Core/Operations/OperationProgress.cs b/UVtools.Core/Operations/OperationProgress.cs index 077c4ba..4eea251 100644 --- a/UVtools.Core/Operations/OperationProgress.cs +++ b/UVtools.Core/Operations/OperationProgress.cs @@ -58,7 +58,7 @@ namespace UVtools.Core.Operations _canCancel = canCancel; } - public Stopwatch StopWatch { get; } = new Stopwatch(); + public Stopwatch StopWatch { get; } = new (); /// <summary> /// Gets or sets if operation can be cancelled @@ -82,7 +82,8 @@ namespace UVtools.Core.Operations set => RaiseAndSetIfChanged(ref _title, value); } - public string ElapsedTimeStr => $"{StopWatch.Elapsed.Minutes}m {StopWatch.Elapsed.Seconds}s {StopWatch.Elapsed.Milliseconds}ms"; + public string ElapsedTimeStr => $"{StopWatch.Elapsed.Minutes}m {StopWatch.Elapsed.Seconds}s"; + //{StopWatch.Elapsed.Milliseconds} ms /// <summary> /// Gets or sets the item name for the operation @@ -124,20 +125,25 @@ namespace UVtools.Core.Operations } /// <summary> + /// Gets or sets an tag + /// </summary> + public object Tag { get; set; } + + /// <summary> /// Gets the remaining items to be processed /// </summary> - public uint RemainingItems => ItemCount - ProcessedItems; + public uint RemainingItems => _itemCount - _processedItems; public int ProgressStep => (int)ProgressPercent; public string Description => ToString(); - public bool IsIndeterminate => ItemCount == 0; + public bool IsIndeterminate => _itemCount == 0; /// <summary> /// Gets the progress from 0 to 100% /// </summary> - public double ProgressPercent => ItemCount == 0 ? 0 : Math.Round(ProcessedItems * 100.0 / ItemCount, 2).Clamp(0, 100); + public double ProgressPercent => _itemCount == 0 ? 0 : Math.Round(_processedItems * 100.0 / _itemCount, 2).Clamp(0, 100); public static OperationProgress operator +(OperationProgress progress, uint value) { @@ -167,9 +173,9 @@ namespace UVtools.Core.Operations public override string ToString() { - return ItemCount == 0 ? - $"{ProcessedItems}/? {ItemName}" : - $"{ProcessedItems}/{ItemCount} {ItemName} | {ProgressPercent:0.00}%"; + return _itemCount == 0 ? +$"{_processedItems}/? {_itemName}" : +$"{_processedItems.ToString().PadLeft(_itemCount.ToString().Length, '0')}/{_itemCount} {_itemName} | {ProgressPercent:0.00}%"; } public void TriggerRefresh() diff --git a/UVtools.Core/Operations/OperationRaftRelief.cs b/UVtools.Core/Operations/OperationRaftRelief.cs index c76b55a..d5634c6 100644 --- a/UVtools.Core/Operations/OperationRaftRelief.cs +++ b/UVtools.Core/Operations/OperationRaftRelief.cs @@ -63,6 +63,14 @@ namespace UVtools.Core.Operations } #endregion + #region Constructor + + public OperationRaftRelief() { } + + public OperationRaftRelief(FileFormat slicerFile) : base(slicerFile) { } + + #endregion + #region Properties public static Array RaftReliefItems => Enum.GetValues(typeof(RaftReliefTypes)); @@ -149,11 +157,10 @@ namespace UVtools.Core.Operations #region Methods - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { + progress.ItemCount = 0; const uint minLength = 5; - progress ??= new OperationProgress(); - //progress.Reset(operation.ProgressAction); Mat supportsMat = null; var anchor = new Point(-1, -1); @@ -161,11 +168,11 @@ namespace UVtools.Core.Operations uint firstSupportLayerIndex = 0; - for (; firstSupportLayerIndex < slicerFile.LayerCount; firstSupportLayerIndex++) + for (; firstSupportLayerIndex < SlicerFile.LayerCount; firstSupportLayerIndex++) { - progress.Reset("Tracing raft", slicerFile.LayerCount, firstSupportLayerIndex); + progress.Reset("Tracing raft", SlicerFile.LayerCount, firstSupportLayerIndex); if (progress.Token.IsCancellationRequested) return false; - supportsMat = GetRoiOrDefault(slicerFile[firstSupportLayerIndex].LayerMat); + supportsMat = GetRoiOrDefault(SlicerFile[firstSupportLayerIndex].LayerMat); var circles = CvInvoke.HoughCircles(supportsMat, HoughModes.Gradient, 1, 20, 100, 30, 5, 200); if (circles.Length >= minLength) break; @@ -187,7 +194,7 @@ namespace UVtools.Core.Operations switch (ReliefType) { - case OperationRaftRelief.RaftReliefTypes.Relief: + case RaftReliefTypes.Relief: patternMat = EmguExtensions.InitMat(supportsMat.Size); int shapeSize = HoleDiameter + HoleSpacing; using (var shape = EmguExtensions.InitMat(new Size(shapeSize, shapeSize))) @@ -208,7 +215,7 @@ namespace UVtools.Core.Operations } break; - case OperationRaftRelief.RaftReliefTypes.Dimming: + case RaftReliefTypes.Dimming: patternMat = EmguExtensions.InitMat(supportsMat.Size, color); break; } @@ -216,7 +223,8 @@ namespace UVtools.Core.Operations progress.Reset(ProgressAction, firstSupportLayerIndex); Parallel.For(IgnoreFirstLayers, firstSupportLayerIndex, layerIndex => { - using (Mat dst = slicerFile[layerIndex].LayerMat) + if (progress.Token.IsCancellationRequested) return; + using (Mat dst = SlicerFile[layerIndex].LayerMat) { var target = GetRoiOrDefault(dst); @@ -242,7 +250,7 @@ namespace UVtools.Core.Operations } - slicerFile[layerIndex].LayerMat = dst; + SlicerFile[layerIndex].LayerMat = dst; } lock (progress.Mutex) @@ -255,7 +263,7 @@ namespace UVtools.Core.Operations supportsMat.Dispose(); patternMat?.Dispose(); - return true; + return !progress.Token.IsCancellationRequested; } #endregion diff --git a/UVtools.Core/Operations/OperationRedrawModel.cs b/UVtools.Core/Operations/OperationRedrawModel.cs index 7f156b1..7cb6da3 100644 --- a/UVtools.Core/Operations/OperationRedrawModel.cs +++ b/UVtools.Core/Operations/OperationRedrawModel.cs @@ -81,6 +81,14 @@ namespace UVtools.Core.Operations } #endregion + #region Constructor + + public OperationRedrawModel() { } + + public OperationRedrawModel(FileFormat slicerFile) : base(slicerFile) { } + + #endregion + #region Properties [XmlIgnore] @@ -151,21 +159,20 @@ namespace UVtools.Core.Operations public FileFormat IsFileValid(bool returnNewInstance = false) => FileFormat.FindByExtension(_filePath, true, returnNewInstance); - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - progress ??= new OperationProgress(); - var otherFile = IsFileValid(true); otherFile.Decode(_filePath, progress); progress.Reset(ProgressAction, otherFile.LayerCount); - int startLayerIndex = (int)(slicerFile.LayerCount - otherFile.LayerCount); + int startLayerIndex = (int)(SlicerFile.LayerCount - otherFile.LayerCount); if (startLayerIndex < 0) return false; Parallel.For(0, otherFile.LayerCount, layerIndex => { + if (progress.Token.IsCancellationRequested) return; var fullMatLayerIndex = startLayerIndex + layerIndex; - using var fullMat = slicerFile[fullMatLayerIndex].LayerMat; + using var fullMat = SlicerFile[fullMatLayerIndex].LayerMat; using var bodyMat = otherFile[layerIndex].LayerMat; using var fullMatRoi = GetRoiOrDefault(fullMat); using var bodyMatRoi = GetRoiOrDefault(bodyMat); @@ -232,7 +239,7 @@ namespace UVtools.Core.Operations if (modified) { - slicerFile[fullMatLayerIndex].LayerMat = fullMat; + SlicerFile[fullMatLayerIndex].LayerMat = fullMat; } lock (progress.Mutex) @@ -241,7 +248,7 @@ namespace UVtools.Core.Operations } }); - return true; + return !progress.Token.IsCancellationRequested; } #endregion diff --git a/UVtools.Core/Operations/OperationRepairLayers.cs b/UVtools.Core/Operations/OperationRepairLayers.cs index 9b48ab5..68c84b0 100644 --- a/UVtools.Core/Operations/OperationRepairLayers.cs +++ b/UVtools.Core/Operations/OperationRepairLayers.cs @@ -16,7 +16,6 @@ using System.Threading.Tasks; using System.Xml.Serialization; using Emgu.CV; using Emgu.CV.CvEnum; -using Emgu.CV.Structure; using Emgu.CV.Util; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; @@ -64,6 +63,14 @@ namespace UVtools.Core.Operations } #endregion + #region Constructor + + public OperationRepairLayers() { } + + public OperationRepairLayers(FileFormat slicerFile) : base(slicerFile) { } + + #endregion + #region Properties public bool RepairIslands { @@ -116,10 +123,8 @@ namespace UVtools.Core.Operations #region Methods - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - progress ??= new OperationProgress(); - // Remove islands if (!ReferenceEquals(Issues, null) && !ReferenceEquals(IslandDetectionConfig, null) @@ -127,7 +132,7 @@ namespace UVtools.Core.Operations && RemoveIslandsBelowEqualPixelCount > 0 && RemoveIslandsRecursiveIterations != 1) { - progress.Reset("Removed recursive islands", 0); + progress.Reset("Removed recursive islands"); ushort limit = RemoveIslandsRecursiveIterations == 0 ? ushort.MaxValue : RemoveIslandsRecursiveIterations; @@ -152,7 +157,7 @@ namespace UVtools.Core.Operations .Select(grp => grp.First()) .ToList();*/ islandConfig.WhiteListLayers = islandsToRecompute.ToList(); - recursiveIssues = slicerFile.LayerManager.GetAllIssues(islandConfig, overhangConfig, resinTrapsConfig, touchingBoundsConfig, emptyLayersConfig); + recursiveIssues = SlicerFile.LayerManager.GetAllIssues(islandConfig, overhangConfig, resinTrapsConfig, touchingBoundsConfig, emptyLayersConfig); //Debug.WriteLine(i); } @@ -167,7 +172,7 @@ namespace UVtools.Core.Operations Parallel.ForEach(issuesGroup, group => { if (progress.Token.IsCancellationRequested) return; - Layer layer = slicerFile[group.Key]; + Layer layer = SlicerFile[group.Key]; Mat image = layer.LayerMat; Span<byte> bytes = image.GetPixelSpan<byte>(); foreach (var issue in group) @@ -184,7 +189,7 @@ namespace UVtools.Core.Operations } var nextLayerIndex = group.Key + 1; - if (nextLayerIndex < slicerFile.LayerCount) + if (nextLayerIndex < SlicerFile.LayerCount) islandsToRecompute.Add(nextLayerIndex); layer.LayerMat = image; @@ -200,7 +205,7 @@ namespace UVtools.Core.Operations Parallel.For(LayerIndexStart, LayerIndexEnd, layerIndex => { if (progress.Token.IsCancellationRequested) return; - Layer layer = slicerFile[layerIndex]; + Layer layer = SlicerFile[layerIndex]; Mat image = null; void initImage() @@ -294,7 +299,7 @@ namespace UVtools.Core.Operations List<uint> removeLayers = new List<uint>(); for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++) { - if (slicerFile[layerIndex].NonZeroPixelCount == 0) + if (SlicerFile[layerIndex].NonZeroPixelCount == 0) { removeLayers.Add(layerIndex); } @@ -302,13 +307,11 @@ namespace UVtools.Core.Operations if (removeLayers.Count > 0) { - OperationLayerRemove.RemoveLayers(slicerFile, removeLayers, progress); + OperationLayerRemove.RemoveLayers(SlicerFile, removeLayers, progress); } } - progress.Token.ThrowIfCancellationRequested(); - - return true; + return !progress.Token.IsCancellationRequested; } #endregion diff --git a/UVtools.Core/Operations/OperationResize.cs b/UVtools.Core/Operations/OperationResize.cs index 0f909e5..1917696 100644 --- a/UVtools.Core/Operations/OperationResize.cs +++ b/UVtools.Core/Operations/OperationResize.cs @@ -103,9 +103,13 @@ namespace UVtools.Core.Operations } #endregion - public OperationResize() - { - } + #region Constructor + + public OperationResize() { } + + public OperationResize(FileFormat slicerFile) : base(slicerFile) { } + + #endregion public override string ToString() { @@ -145,11 +149,9 @@ namespace UVtools.Core.Operations #region Methods - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { if (X == 1m && Y == 1m) return false; - progress ??= new OperationProgress(); - progress.Reset(ProgressAction, LayerRangeCount); decimal xSteps = Math.Abs(X - 1) / (LayerIndexEnd - LayerIndexStart); decimal ySteps = Math.Abs(Y - 1) / (LayerIndexEnd - LayerIndexStart); @@ -195,12 +197,12 @@ namespace UVtools.Core.Operations if (newX == 1.0m && newY == 1.0m) return; - using var mat = slicerFile[layerIndex].LayerMat; + using var mat = SlicerFile[layerIndex].LayerMat; Execute(mat, newX / 100m, newY / 100m); - slicerFile[layerIndex].LayerMat = mat; + SlicerFile[layerIndex].LayerMat = mat; }); - progress.Token.ThrowIfCancellationRequested(); - return true; + + return !progress.Token.IsCancellationRequested; } public override bool Execute(Mat mat, params object[] arguments) diff --git a/UVtools.Core/Operations/OperationRotate.cs b/UVtools.Core/Operations/OperationRotate.cs index f9a864e..c428b6c 100644 --- a/UVtools.Core/Operations/OperationRotate.cs +++ b/UVtools.Core/Operations/OperationRotate.cs @@ -42,6 +42,14 @@ namespace UVtools.Core.Operations } #endregion + #region Constructor + + public OperationRotate() { } + + public OperationRotate(FileFormat slicerFile) : base(slicerFile) { } + + #endregion + #region Properties public decimal AngleDegrees { @@ -74,24 +82,21 @@ namespace UVtools.Core.Operations #region Methods - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(ProgressAction, LayerRangeCount); Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex => { if (progress.Token.IsCancellationRequested) return; - using var mat = slicerFile[layerIndex].LayerMat; + using var mat = SlicerFile[layerIndex].LayerMat; Execute(mat); - slicerFile[layerIndex].LayerMat = mat; + SlicerFile[layerIndex].LayerMat = mat; lock (progress.Mutex) { progress++; } }); - progress.Token.ThrowIfCancellationRequested(); - return true; + return !progress.Token.IsCancellationRequested; } public override bool Execute(Mat mat, params object[] arguments) diff --git a/UVtools.Core/Operations/OperationSolidify.cs b/UVtools.Core/Operations/OperationSolidify.cs index ca6577f..e7e8e4d 100644 --- a/UVtools.Core/Operations/OperationSolidify.cs +++ b/UVtools.Core/Operations/OperationSolidify.cs @@ -20,13 +20,18 @@ namespace UVtools.Core.Operations [Serializable] public sealed class OperationSolidify : Operation { + #region Enums public enum AreaCheckTypes { More, Less } + #endregion + + #region Members private uint _minimumArea = 1; private AreaCheckTypes _areaCheckType = AreaCheckTypes.More; + #endregion #region Overrides @@ -70,25 +75,31 @@ namespace UVtools.Core.Operations #endregion + #region Constructor + + public OperationSolidify() { } + + public OperationSolidify(FileFormat slicerFile) : base(slicerFile) { } + + #endregion + #region Methods - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(ProgressAction, LayerRangeCount); Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex => { if (progress.Token.IsCancellationRequested) return; - using var mat = slicerFile[layerIndex].LayerMat; + using var mat = SlicerFile[layerIndex].LayerMat; Execute(mat); - slicerFile[layerIndex].LayerMat = mat; + SlicerFile[layerIndex].LayerMat = mat; lock (progress.Mutex) { progress++; } }); - progress.Token.ThrowIfCancellationRequested(); - return true; + + return !progress.Token.IsCancellationRequested; } public override bool Execute(Mat mat, params object[] arguments) diff --git a/UVtools.Core/Operations/OperationThreshold.cs b/UVtools.Core/Operations/OperationThreshold.cs index f3776a4..b85d6b4 100644 --- a/UVtools.Core/Operations/OperationThreshold.cs +++ b/UVtools.Core/Operations/OperationThreshold.cs @@ -47,6 +47,14 @@ namespace UVtools.Core.Operations } #endregion + #region Constructor + + public OperationThreshold() { } + + public OperationThreshold(FileFormat slicerFile) : base(slicerFile) { } + + #endregion + #region Properties public byte Threshold { @@ -71,17 +79,15 @@ namespace UVtools.Core.Operations #region Methods - public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + protected override bool ExecuteInternally(OperationProgress progress) { - progress ??= new OperationProgress(); - progress.Reset(ProgressAction, LayerRangeCount); Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex => { if (progress.Token.IsCancellationRequested) return; - using (var mat = slicerFile[layerIndex].LayerMat) + using (var mat = SlicerFile[layerIndex].LayerMat) { Execute(mat); - slicerFile[layerIndex].LayerMat = mat; + SlicerFile[layerIndex].LayerMat = mat; } lock (progress.Mutex) @@ -89,9 +95,8 @@ namespace UVtools.Core.Operations progress++; } }); - progress.Token.ThrowIfCancellationRequested(); - return true; + return !progress.Token.IsCancellationRequested; } public override bool Execute(Mat mat, params object[] arguments) diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index 9bfaefd..2ca19df 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -10,7 +10,7 @@ <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> <PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl> <Description>MSLA/DLP, file analysis, calibration, repair, conversion and manipulation</Description> - <Version>2.3.2</Version> + <Version>2.4.0</Version> <Copyright>Copyright © 2020 PTRTECH</Copyright> <PackageIcon>UVtools.png</PackageIcon> <Platforms>AnyCPU;x64</Platforms> @@ -45,7 +45,7 @@ <ItemGroup> <PackageReference Include="BinarySerializer" Version="8.5.3" /> - <PackageReference Include="Emgu.CV" Version="4.4.0.4099" /> + <PackageReference Include="Emgu.CV" Version="4.5.1.4349" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="System.Memory" Version="4.5.4" /> <PackageReference Include="System.Reflection.TypeExtensions" Version="4.7.0" /> diff --git a/UVtools.InstallerMM/UVtools.InstallerMM.wxs b/UVtools.InstallerMM/UVtools.InstallerMM.wxs index f7bceba..0bda982 100644 --- a/UVtools.InstallerMM/UVtools.InstallerMM.wxs +++ b/UVtools.InstallerMM/UVtools.InstallerMM.wxs @@ -152,6 +152,9 @@ <Component Id="owc27A5D1506739847D9D7688256879A937" Guid="30f2e89f-1e95-77b9-1c3b-90bd4d875160"> <File Id="owf27A5D1506739847D9D7688256879A937" Source="$(var.SourceDir)\Avalonia.DesktopRuntime.dll" KeyPath="yes" /> </Component> + <Component Id="owcAA73F8E2C0EE78F802BAA8B361043211" Guid="536d2de0-51e7-19c8-3a8e-466146777c64"> + <File Id="owfAA73F8E2C0EE78F802BAA8B361043211" Source="$(var.SourceDir)\Avalonia.Diagnostics.dll" KeyPath="yes" /> + </Component> <Component Id="owc6C15ADD7A5EE7A0CBEFD35B0027BBEB7" Guid="9c4a8ab3-598c-8e7c-0295-5c068334ab67"> <File Id="owf6C15ADD7A5EE7A0CBEFD35B0027BBEB7" Source="$(var.SourceDir)\Avalonia.Dialogs.dll" KeyPath="yes" /> </Component> @@ -227,12 +230,18 @@ <Component Id="owc80EC8AEA1BFB8077812CDD06F9128A53" Guid="afbd2d79-629c-90a1-57a9-d68edc2d1437"> <File Id="owf80EC8AEA1BFB8077812CDD06F9128A53" Source="$(var.SourceDir)\clrjit.dll" KeyPath="yes" /> </Component> + <Component Id="owcD89DA7DE2D69AFC05DD5D46B6072C05C" Guid="ff72b568-c40c-3d50-c5af-049f64b476ea"> + <File Id="owfD89DA7DE2D69AFC05DD5D46B6072C05C" Source="$(var.SourceDir)\concrt140.dll" KeyPath="yes" /> + </Component> <Component Id="owc6135A13B8E29883ED57BF3FE20578EED" Guid="cd1aae2e-a4de-1bd1-9680-014bccf80e32"> <File Id="owf6135A13B8E29883ED57BF3FE20578EED" Source="$(var.SourceDir)\coreclr.dll" KeyPath="yes" /> </Component> <Component Id="owc5E8FBF42F9BCF40914849582C19906B8" Guid="f7a08052-6afc-fe5e-291c-07137533ab79"> <File Id="owf5E8FBF42F9BCF40914849582C19906B8" Source="$(var.SourceDir)\createdump.exe" KeyPath="yes" /> </Component> + <Component Id="owcF9E82F0BAE95DEABBEA8012A0994676C" Guid="edd1fddd-c05b-dc01-33b2-52e715b79615"> + <File Id="owfF9E82F0BAE95DEABBEA8012A0994676C" Source="$(var.SourceDir)\cvextern.dll" KeyPath="yes" /> + </Component> <Component Id="owc3883419A2BFFE7013C2194A99728AB11" Guid="dd137337-4b74-c76b-c87e-6b5f42697dd4"> <File Id="owf3883419A2BFFE7013C2194A99728AB11" Source="$(var.SourceDir)\dbgshim.dll" KeyPath="yes" /> </Component> @@ -263,6 +272,18 @@ <Component Id="owc6262D65DF7B3DF80C034B72467CAE882" Guid="4941cd5b-5666-87fe-5a6a-5ebdd5ee2524"> <File Id="owf6262D65DF7B3DF80C034B72467CAE882" Source="$(var.SourceDir)\MessageBox.Avalonia.dll" KeyPath="yes" /> </Component> + <Component Id="owcBED5500FCECB7551175681E93EC9B191" Guid="e84e4591-8227-e9c2-b052-dc4583553c9a"> + <File Id="owfBED5500FCECB7551175681E93EC9B191" Source="$(var.SourceDir)\Microsoft.CodeAnalysis.CSharp.dll" KeyPath="yes" /> + </Component> + <Component Id="owcD48DD5DDA2BA4C83F4A98D4BE87BA462" Guid="effc6a72-2186-a73f-ee7f-c05c95de6c6e"> + <File Id="owfD48DD5DDA2BA4C83F4A98D4BE87BA462" Source="$(var.SourceDir)\Microsoft.CodeAnalysis.CSharp.Scripting.dll" KeyPath="yes" /> + </Component> + <Component Id="owcDC0782CCA5E596145FD1FDFB67EDC2AF" Guid="8bfcc8b6-6693-60fe-2af9-d0e34ab6f421"> + <File Id="owfDC0782CCA5E596145FD1FDFB67EDC2AF" Source="$(var.SourceDir)\Microsoft.CodeAnalysis.dll" KeyPath="yes" /> + </Component> + <Component Id="owcB9CA98C685BDD7D3E9DD3EBD5C5A3B84" Guid="434ec6c6-a47b-5321-63d1-6d30a2f8a9bf"> + <File Id="owfB9CA98C685BDD7D3E9DD3EBD5C5A3B84" Source="$(var.SourceDir)\Microsoft.CodeAnalysis.Scripting.dll" KeyPath="yes" /> + </Component> <Component Id="owc27AE8A53D94DD8A3E4AEAF6F7C27FB60" Guid="80e5ef0e-84a5-2bce-9923-0fd54d243e45"> <File Id="owf27AE8A53D94DD8A3E4AEAF6F7C27FB60" Source="$(var.SourceDir)\Microsoft.CSharp.dll" KeyPath="yes" /> </Component> @@ -299,12 +320,30 @@ <Component Id="owcE382B0CF482F0E7C3AF33E6EEEBD26B0" Guid="7433845c-c763-9b7e-e2c9-7b06986570bf"> <File Id="owfE382B0CF482F0E7C3AF33E6EEEBD26B0" Source="$(var.SourceDir)\mscorrc.dll" KeyPath="yes" /> </Component> + <Component Id="owc4EDD870826B23A7EFC3FACD1E199E883" Guid="587324f3-ed59-f000-c241-8dc6a66501de"> + <File Id="owf4EDD870826B23A7EFC3FACD1E199E883" Source="$(var.SourceDir)\msvcp140.dll" KeyPath="yes" /> + </Component> + <Component Id="owc57B8051D45DF8A094B6F6F64CD58D9E6" Guid="b19c1e08-c2ee-ae27-0014-d5180ae56935"> + <File Id="owf57B8051D45DF8A094B6F6F64CD58D9E6" Source="$(var.SourceDir)\msvcp140_1.dll" KeyPath="yes" /> + </Component> + <Component Id="owcE2F8C74ADC2AF2357936D93D9E417BB4" Guid="01e22654-976b-0b52-7f65-17a93b4d19ab"> + <File Id="owfE2F8C74ADC2AF2357936D93D9E417BB4" Source="$(var.SourceDir)\msvcp140_2.dll" KeyPath="yes" /> + </Component> + <Component Id="owcD748ECCC1ECBC6ACAF796527AC8E18D3" Guid="53347251-3752-30e8-051d-71050378624d"> + <File Id="owfD748ECCC1ECBC6ACAF796527AC8E18D3" Source="$(var.SourceDir)\msvcp140_atomic_wait.dll" KeyPath="yes" /> + </Component> + <Component Id="owc3FDD5303B44770B8890DCDE0685AF1EB" Guid="50c83bf3-3054-7dd7-6eda-a39108edb2c2"> + <File Id="owf3FDD5303B44770B8890DCDE0685AF1EB" Source="$(var.SourceDir)\msvcp140_codecvt_ids.dll" KeyPath="yes" /> + </Component> <Component Id="owc79156F385239CEA4DEA3C87C1DA3E1D8" Guid="68380c80-6708-5eb6-415d-10e278417679"> <File Id="owf79156F385239CEA4DEA3C87C1DA3E1D8" Source="$(var.SourceDir)\netstandard.dll" KeyPath="yes" /> </Component> <Component Id="owc3FC8434C1A84605AEEBC6A100CA2F3E1" Guid="47d7e276-db6d-424e-d797-1a69e1e24b3b"> <File Id="owf3FC8434C1A84605AEEBC6A100CA2F3E1" Source="$(var.SourceDir)\Newtonsoft.Json.dll" KeyPath="yes" /> </Component> + <Component Id="owc29161E83FC02784120326979FF285358" Guid="a8d30e75-1063-4d27-60b6-13d4bd4f52eb"> + <File Id="owf29161E83FC02784120326979FF285358" Source="$(var.SourceDir)\opencv_videoio_ffmpeg451_64.dll" KeyPath="yes" /> + </Component> <Component Id="owcBFCA0BB8CBE4A68414FA088B4F7F3D22" Guid="babeb74f-9861-b222-a49a-ec268c3cd3ca"> <File Id="owfBFCA0BB8CBE4A68414FA088B4F7F3D22" Source="$(var.SourceDir)\ReactiveUI.dll" KeyPath="yes" /> </Component> @@ -811,9 +850,9 @@ </Component> <Component Id="owc21B8C8C0F69E3CD1398B9A0674DF07BD" Guid="1e472543-e636-904d-da5b-cddaac2d36b1"> <File Id="owf21B8C8C0F69E3CD1398B9A0674DF07BD" Source="$(var.SourceDir)\UVtools.exe" KeyPath="yes"> - <Shortcut Id="sc218BF75801887335D1B30BAFB94BA631" Name="UVtools" Directory="scd220707349D4C8FA275285514283F3E2A" WorkingDirectory="MergeRedirectFolder" Description="MSLA/DLP, file analysis, calibration, repair, conversion and manipulation" /> <Shortcut Id="sc2F2092C02FDD8EB21E756F58C31B70A7" Name="UVtools" Directory="StartMenuFolder" WorkingDirectory="MergeRedirectFolder" Description="MSLA/DLP, file analysis, calibration, repair, conversion and manipulation" /> - <Shortcut Id="sc6327849DA5C02D2396E91B8B1892E03C" Name="UVtools" Directory="DesktopFolder" Description="MSLA/DLP, file analysis, calibration, repair, conversion and manipulation" WorkingDirectory="MergeRedirectFolder" /> + <Shortcut Id="sc218BF75801887335D1B30BAFB94BA631" Name="UVtools" Directory="scd220707349D4C8FA275285514283F3E2A" WorkingDirectory="MergeRedirectFolder" Description="MSLA/DLP, file analysis, calibration, repair, conversion and manipulation" /> + <Shortcut Id="sc6327849DA5C02D2396E91B8B1892E03C" Name="UVtools" Directory="DesktopFolder" WorkingDirectory="MergeRedirectFolder" Description="MSLA/DLP, file analysis, calibration, repair, conversion and manipulation" /> </File> </Component> <Component Id="owc0C1077539E2B54F2460D2C0993613EFB" Guid="fc6ae2db-2865-9eb4-5753-4f2f941fdf16"> @@ -825,6 +864,15 @@ <Component Id="owc1FE1E0B70F7D880A2FB9AFA8E0E36EF5" Guid="545300b7-5398-2ce2-788f-02287a83e8a7"> <File Id="owf1FE1E0B70F7D880A2FB9AFA8E0E36EF5" Source="$(var.SourceDir)\UVtools_demo_file.sl1" KeyPath="yes" /> </Component> + <Component Id="owc507468650B23000B702AAAE48E658343" Guid="dba8edf4-02a3-c04a-50fc-896025548077"> + <File Id="owf507468650B23000B702AAAE48E658343" Source="$(var.SourceDir)\vccorlib140.dll" KeyPath="yes" /> + </Component> + <Component Id="owcEB2D9343E9BF3366381E9864468EFB9A" Guid="c4f85169-dfa6-1369-3b95-34e28199fa0f"> + <File Id="owfEB2D9343E9BF3366381E9864468EFB9A" Source="$(var.SourceDir)\vcruntime140.dll" KeyPath="yes" /> + </Component> + <Component Id="owc41A749EF7C055C135F6C13882684CE39" Guid="d0346e8a-b7e9-6a98-fefd-48957d30e340"> + <File Id="owf41A749EF7C055C135F6C13882684CE39" Source="$(var.SourceDir)\vcruntime140_1.dll" KeyPath="yes" /> + </Component> <Component Id="owc9AF7D2C52438BF24F3FA3132D7C0E6A9" Guid="892f88e7-cf94-fad1-08d3-b26c1b5913d3"> <File Id="owf9AF7D2C52438BF24F3FA3132D7C0E6A9" Source="$(var.SourceDir)\WindowsBase.dll" KeyPath="yes" /> </Component> @@ -1090,59 +1138,186 @@ </Component> </Directory> </Directory> - <Directory Id="owd37EA32A018DF9B31395A1FCE42233062" Name="x64"> - <Component Id="owcCD1854F2CC6C7F17F61397E23B6C598A" Guid="0a98d274-6f18-ee97-f14c-410e8fd2060d"> - <File Id="owfCD1854F2CC6C7F17F61397E23B6C598A" Source="$(var.SourceDir)\x64\concrt140.dll" KeyPath="yes" /> + <Directory Id="owd799F60633DE3314CD962556CD118A45D" Name="cs"> + <Component Id="owc99CE72FDB30C8FD0902CE01843ED4D88" Guid="c4c2d2ce-4eae-325d-f83e-f5d17907fa7d"> + <File Id="owf99CE72FDB30C8FD0902CE01843ED4D88" Source="$(var.SourceDir)\cs\Microsoft.CodeAnalysis.CSharp.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owc5A8A23B0047B0D17D2E388C7647E2495" Guid="0bc611bd-4c59-08df-c64a-8fa2236aa7a5"> + <File Id="owf5A8A23B0047B0D17D2E388C7647E2495" Source="$(var.SourceDir)\cs\Microsoft.CodeAnalysis.CSharp.Scripting.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owc86B8E7B458701723CB9EC03C1690DC6E" Guid="cdd72de0-864d-fb89-212f-4bd06f0dcd10"> + <File Id="owf86B8E7B458701723CB9EC03C1690DC6E" Source="$(var.SourceDir)\cs\Microsoft.CodeAnalysis.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owc8370BED3D17A58E88E193350B14A5450" Guid="cc521992-26e9-bf12-e777-68f8e2102405"> + <File Id="owf8370BED3D17A58E88E193350B14A5450" Source="$(var.SourceDir)\cs\Microsoft.CodeAnalysis.Scripting.resources.dll" KeyPath="yes" /> + </Component> + </Directory> + <Directory Id="owd17C6A2D314297F8FF3C608803909F7BE" Name="de"> + <Component Id="owc6A8D7823467C066EA1EC2F740C66084D" Guid="82c152c6-97bf-f75d-44fc-294474d9a7cb"> + <File Id="owf6A8D7823467C066EA1EC2F740C66084D" Source="$(var.SourceDir)\de\Microsoft.CodeAnalysis.CSharp.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owc9116F5B9D0D06FDD8B9116F3E0951A24" Guid="05d40bda-9c56-78d7-90ac-dd3c0b6e32c8"> + <File Id="owf9116F5B9D0D06FDD8B9116F3E0951A24" Source="$(var.SourceDir)\de\Microsoft.CodeAnalysis.CSharp.Scripting.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owc201B77F8A5FFED898E7D6ABA98A1C719" Guid="db330e27-4a94-1662-79dc-50425953f203"> + <File Id="owf201B77F8A5FFED898E7D6ABA98A1C719" Source="$(var.SourceDir)\de\Microsoft.CodeAnalysis.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owcF790A17350C2437C0E1E354B9D980161" Guid="627f2dc4-5b88-d3f7-3acf-7630111e30e8"> + <File Id="owfF790A17350C2437C0E1E354B9D980161" Source="$(var.SourceDir)\de\Microsoft.CodeAnalysis.Scripting.resources.dll" KeyPath="yes" /> + </Component> + </Directory> + <Directory Id="owdF542F1E9B1CF46F5EC5BF0998941ED93" Name="es"> + <Component Id="owcDD2F3A9E1BC6C55110A95C76B075BB2A" Guid="1082627f-113d-241a-abee-cb25d4b696fe"> + <File Id="owfDD2F3A9E1BC6C55110A95C76B075BB2A" Source="$(var.SourceDir)\es\Microsoft.CodeAnalysis.CSharp.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owcAF0B9E9C8764DB03240C3233FA91FC3F" Guid="61f4d403-eb00-dd15-8880-b8ae920ff3b3"> + <File Id="owfAF0B9E9C8764DB03240C3233FA91FC3F" Source="$(var.SourceDir)\es\Microsoft.CodeAnalysis.CSharp.Scripting.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owcA9E6FC4859E34CE57D4623D60AB82100" Guid="6e39d6b9-6efc-9ed5-3392-971358498061"> + <File Id="owfA9E6FC4859E34CE57D4623D60AB82100" Source="$(var.SourceDir)\es\Microsoft.CodeAnalysis.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owcB59CB618361B113CFA57D75E883608EA" Guid="ddc56db3-e6a2-c7aa-dc21-3b247964758c"> + <File Id="owfB59CB618361B113CFA57D75E883608EA" Source="$(var.SourceDir)\es\Microsoft.CodeAnalysis.Scripting.resources.dll" KeyPath="yes" /> + </Component> + </Directory> + <Directory Id="owdA426C3729D5D59516430C8F667ED8DE8" Name="fr"> + <Component Id="owcFECC4FDE99CBE3816D982D1FF8E5E196" Guid="703ebe68-adaa-3f9c-606b-b42fec5159e8"> + <File Id="owfFECC4FDE99CBE3816D982D1FF8E5E196" Source="$(var.SourceDir)\fr\Microsoft.CodeAnalysis.CSharp.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owcDDEF75A5FE4B5C43A5794EC0494F1E2C" Guid="56a9d29d-436d-4319-1a0d-cf5306a85f60"> + <File Id="owfDDEF75A5FE4B5C43A5794EC0494F1E2C" Source="$(var.SourceDir)\fr\Microsoft.CodeAnalysis.CSharp.Scripting.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owcF108244D2CA24AF9CCC6DA95FF283CA3" Guid="8a5a7aca-1013-145f-3a57-49946c024af2"> + <File Id="owfF108244D2CA24AF9CCC6DA95FF283CA3" Source="$(var.SourceDir)\fr\Microsoft.CodeAnalysis.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owcE8E57F27B5A0C2D7A27A164639AA95F9" Guid="caac4561-da90-4c7b-02cd-bdfca5be1694"> + <File Id="owfE8E57F27B5A0C2D7A27A164639AA95F9" Source="$(var.SourceDir)\fr\Microsoft.CodeAnalysis.Scripting.resources.dll" KeyPath="yes" /> + </Component> + </Directory> + <Directory Id="owd74DAE01CDA8C981B3918AC92ED360AA1" Name="it"> + <Component Id="owc4DD3418A4620DABCBBB6CCAA8ECA2707" Guid="421c92b6-85d7-3733-b9a5-ed10229c89a5"> + <File Id="owf4DD3418A4620DABCBBB6CCAA8ECA2707" Source="$(var.SourceDir)\it\Microsoft.CodeAnalysis.CSharp.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owc70A38979F7888C6B171A3C37DBD35061" Guid="d19c3295-b474-4fab-9af6-93c9257dfd18"> + <File Id="owf70A38979F7888C6B171A3C37DBD35061" Source="$(var.SourceDir)\it\Microsoft.CodeAnalysis.CSharp.Scripting.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owcE089DC47E89BFE9044FEC67CE8379D17" Guid="15a0d64c-a68c-9780-ddd0-e40920cd6a68"> + <File Id="owfE089DC47E89BFE9044FEC67CE8379D17" Source="$(var.SourceDir)\it\Microsoft.CodeAnalysis.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owc8B74032559230C1C0C2B5082475A5CD4" Guid="39eabb54-8dd8-65e8-fe00-359ad09cae2a"> + <File Id="owf8B74032559230C1C0C2B5082475A5CD4" Source="$(var.SourceDir)\it\Microsoft.CodeAnalysis.Scripting.resources.dll" KeyPath="yes" /> + </Component> + </Directory> + <Directory Id="owd939A7C952EF97FFE1E5F44BC8281E343" Name="ja"> + <Component Id="owc51FE3ECF820BD499CDE7DBB22A69167D" Guid="3201321c-b8c7-5cbb-2ad8-f8bf5b451b3d"> + <File Id="owf51FE3ECF820BD499CDE7DBB22A69167D" Source="$(var.SourceDir)\ja\Microsoft.CodeAnalysis.CSharp.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owc8549DE5E566B16C0AD0E61BE120D8555" Guid="8b4fc791-bf38-333f-a84f-d78fedc6efe3"> + <File Id="owf8549DE5E566B16C0AD0E61BE120D8555" Source="$(var.SourceDir)\ja\Microsoft.CodeAnalysis.CSharp.Scripting.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owcD50AC7A96B0FC09970B32B3681AF3FF4" Guid="1f8ff321-6287-1fbe-4f8d-86eb390bf641"> + <File Id="owfD50AC7A96B0FC09970B32B3681AF3FF4" Source="$(var.SourceDir)\ja\Microsoft.CodeAnalysis.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owc0A9733AEBD37AD14413D6F786BB5FA8A" Guid="f57405c2-169e-0504-01db-e115d156226b"> + <File Id="owf0A9733AEBD37AD14413D6F786BB5FA8A" Source="$(var.SourceDir)\ja\Microsoft.CodeAnalysis.Scripting.resources.dll" KeyPath="yes" /> + </Component> + </Directory> + <Directory Id="owd26824DD7BA688994CEC324C96033C66B" Name="ko"> + <Component Id="owc82B969E8D4D535732FCF01718B7E606D" Guid="993f9efe-f89f-7b95-8443-2341e293e7a6"> + <File Id="owf82B969E8D4D535732FCF01718B7E606D" Source="$(var.SourceDir)\ko\Microsoft.CodeAnalysis.CSharp.resources.dll" KeyPath="yes" /> </Component> - <Component Id="owcE1790624963D540C427806C2B2809EBA" Guid="00de945a-e306-4840-1dd3-b8e968184b74"> - <File Id="owfE1790624963D540C427806C2B2809EBA" Source="$(var.SourceDir)\x64\cvextern.dll" KeyPath="yes" /> + <Component Id="owcABF888E5C49C7DDDF2FB5AA8071F21D9" Guid="13a4540c-353f-31db-f76e-3017c15fe9df"> + <File Id="owfABF888E5C49C7DDDF2FB5AA8071F21D9" Source="$(var.SourceDir)\ko\Microsoft.CodeAnalysis.CSharp.Scripting.resources.dll" KeyPath="yes" /> </Component> - <Component Id="owc5F1F538D5D53D69031C503A5A647FA86" Guid="4e9e9fdc-8d0c-3ba0-a240-9e6ef5842194"> - <File Id="owf5F1F538D5D53D69031C503A5A647FA86" Source="$(var.SourceDir)\x64\msvcp140.dll" KeyPath="yes" /> + <Component Id="owc1F4AA2DE2CA82FAFEC017FB9E5159A16" Guid="b96179dd-f81f-6158-e087-b01eeb552325"> + <File Id="owf1F4AA2DE2CA82FAFEC017FB9E5159A16" Source="$(var.SourceDir)\ko\Microsoft.CodeAnalysis.resources.dll" KeyPath="yes" /> </Component> - <Component Id="owc8F40E3E29777D025A9F18E5B6042F30D" Guid="e05981f2-643b-ca90-e066-2434e056751a"> - <File Id="owf8F40E3E29777D025A9F18E5B6042F30D" Source="$(var.SourceDir)\x64\msvcp140_1.dll" KeyPath="yes" /> + <Component Id="owc49136EC48B42F29310D5FAB96EB39C77" Guid="7d59fb12-e36d-3b98-1b54-8182dc102913"> + <File Id="owf49136EC48B42F29310D5FAB96EB39C77" Source="$(var.SourceDir)\ko\Microsoft.CodeAnalysis.Scripting.resources.dll" KeyPath="yes" /> </Component> - <Component Id="owc0881D1B926887713CEA1FB8CE1244020" Guid="3baf74a4-81ca-d0a7-70ea-912a08d3dc3c"> - <File Id="owf0881D1B926887713CEA1FB8CE1244020" Source="$(var.SourceDir)\x64\msvcp140_2.dll" KeyPath="yes" /> + </Directory> + <Directory Id="owdC3B37D0EB36BEDA1B06A324A062DC94D" Name="pl"> + <Component Id="owc4D2D720AD4B25046F5C3A8337C58558F" Guid="f9f595eb-39b5-e326-d983-3aca9a6bb500"> + <File Id="owf4D2D720AD4B25046F5C3A8337C58558F" Source="$(var.SourceDir)\pl\Microsoft.CodeAnalysis.CSharp.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owc247996509C92190D211D51C687663973" Guid="d3d661d6-e99e-91e3-9512-9a35c05b5d78"> + <File Id="owf247996509C92190D211D51C687663973" Source="$(var.SourceDir)\pl\Microsoft.CodeAnalysis.CSharp.Scripting.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owc75290668850934E95D9C36037C6CA10D" Guid="8be98fcf-1714-97ea-9e3f-6337e8a79c4a"> + <File Id="owf75290668850934E95D9C36037C6CA10D" Source="$(var.SourceDir)\pl\Microsoft.CodeAnalysis.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owc01C7CE26E845E06F3A50D520FB278BCF" Guid="d1ddafed-c698-3973-d2a4-4c4d00620030"> + <File Id="owf01C7CE26E845E06F3A50D520FB278BCF" Source="$(var.SourceDir)\pl\Microsoft.CodeAnalysis.Scripting.resources.dll" KeyPath="yes" /> + </Component> + </Directory> + <Directory Id="owdE3101CE52C8B10FCC8A2CC81C4C96F39" Name="pt-BR"> + <Component Id="owc66101D80AE2357408A5C5CDB0D87BEB0" Guid="9fa0b68e-9b5f-699d-79d5-b93a4344d2eb"> + <File Id="owf66101D80AE2357408A5C5CDB0D87BEB0" Source="$(var.SourceDir)\pt-BR\Microsoft.CodeAnalysis.CSharp.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owcFF7C74C94543248FAB00F923E96F0218" Guid="4dbc0468-33ed-1ad7-80c8-e7589d99ef83"> + <File Id="owfFF7C74C94543248FAB00F923E96F0218" Source="$(var.SourceDir)\pt-BR\Microsoft.CodeAnalysis.CSharp.Scripting.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owc537BEF6F05522283FE8F47F86399668A" Guid="960db198-afc4-40f9-f20e-a5d537c5702a"> + <File Id="owf537BEF6F05522283FE8F47F86399668A" Source="$(var.SourceDir)\pt-BR\Microsoft.CodeAnalysis.resources.dll" KeyPath="yes" /> </Component> - <Component Id="owc2FF8EDA65751914E3C80D405856A37CE" Guid="283d40b2-5948-d5e0-9173-53dbd88d6e13"> - <File Id="owf2FF8EDA65751914E3C80D405856A37CE" Source="$(var.SourceDir)\x64\msvcp140_codecvt_ids.dll" KeyPath="yes" /> + <Component Id="owc233F59943A3FA33488E78259D09163C6" Guid="e99cb644-a268-a6af-face-e2e7834333cb"> + <File Id="owf233F59943A3FA33488E78259D09163C6" Source="$(var.SourceDir)\pt-BR\Microsoft.CodeAnalysis.Scripting.resources.dll" KeyPath="yes" /> </Component> - <Component Id="owcA66BA6B6AE2CB062F1DEE173EBAC6013" Guid="2c9e44cd-17bb-ac7b-b524-b8d94f9cf180"> - <File Id="owfA66BA6B6AE2CB062F1DEE173EBAC6013" Source="$(var.SourceDir)\x64\opencv_videoio_ffmpeg440_64.dll" KeyPath="yes" /> + </Directory> + <Directory Id="owdEAE4E7B6D44EEB507A9E5B7539B1C34A" Name="ru"> + <Component Id="owcB065F45686216D7224FE9E57071535B1" Guid="e96c2dc1-7b2a-91cf-dfb2-add81e62ac2b"> + <File Id="owfB065F45686216D7224FE9E57071535B1" Source="$(var.SourceDir)\ru\Microsoft.CodeAnalysis.CSharp.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owc1810998919C2A9C80EFE093BE92CBEF2" Guid="88c73b6a-b6a5-87f8-ffed-c028f0b6c0e4"> + <File Id="owf1810998919C2A9C80EFE093BE92CBEF2" Source="$(var.SourceDir)\ru\Microsoft.CodeAnalysis.CSharp.Scripting.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owc56892873DDC66705A32DD13C1A40B9F3" Guid="b92e9a16-6e47-a12b-354b-4624ba27ebfd"> + <File Id="owf56892873DDC66705A32DD13C1A40B9F3" Source="$(var.SourceDir)\ru\Microsoft.CodeAnalysis.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owc53575A00E0697BB0E07983D26C103FEA" Guid="64e7fa95-8227-2c96-6a82-e914b6ce047c"> + <File Id="owf53575A00E0697BB0E07983D26C103FEA" Source="$(var.SourceDir)\ru\Microsoft.CodeAnalysis.Scripting.resources.dll" KeyPath="yes" /> </Component> - <Component Id="owc8C50C7464818ADC2267594B33B7B1EE6" Guid="049c0f0d-c410-daa8-b6ec-1c515fdfce03"> - <File Id="owf8C50C7464818ADC2267594B33B7B1EE6" Source="$(var.SourceDir)\x64\vcruntime140.dll" KeyPath="yes" /> + </Directory> + <Directory Id="owdFCC3FD9F99A4D5BEDEAC61F3322D2D93" Name="tr"> + <Component Id="owcEC06D7EB1988F1B550C8B3569B0261D3" Guid="07379a43-cb0f-ea5f-0cfe-d71fddfe14e6"> + <File Id="owfEC06D7EB1988F1B550C8B3569B0261D3" Source="$(var.SourceDir)\tr\Microsoft.CodeAnalysis.CSharp.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owcBA8A623686D1AEABEB47E9B2FB674C3A" Guid="14f5bbbe-61a0-2e13-aa01-612beea284ea"> + <File Id="owfBA8A623686D1AEABEB47E9B2FB674C3A" Source="$(var.SourceDir)\tr\Microsoft.CodeAnalysis.CSharp.Scripting.resources.dll" KeyPath="yes" /> </Component> - <Component Id="owcBF69387AE1C9C06910622B5629DAAA62" Guid="051ca95f-4aa8-18b1-56da-7788687d06e0"> - <File Id="owfBF69387AE1C9C06910622B5629DAAA62" Source="$(var.SourceDir)\x64\vcruntime140_1.dll" KeyPath="yes" /> + <Component Id="owc618B2DBE7EB4D4F7E675C5D04CD64E7E" Guid="197802f2-556a-5b71-650c-faf644c38863"> + <File Id="owf618B2DBE7EB4D4F7E675C5D04CD64E7E" Source="$(var.SourceDir)\tr\Microsoft.CodeAnalysis.resources.dll" KeyPath="yes" /> + </Component> + <Component Id="owc91567B2B4E8154089395D1A78358BE89" Guid="1c327c0f-a510-b334-7130-5c9e6609dc7b"> + <File Id="owf91567B2B4E8154089395D1A78358BE89" Source="$(var.SourceDir)\tr\Microsoft.CodeAnalysis.Scripting.resources.dll" KeyPath="yes" /> </Component> </Directory> - <Directory Id="owd3BCA71FC1DDF940F5FD535BAC750E17B" Name="x86"> - <Component Id="owcFBC3B4CD533BC131080BCB572CEE8474" Guid="8b12ad7e-4019-3cca-c71d-82e5f3e6a080"> - <File Id="owfFBC3B4CD533BC131080BCB572CEE8474" Source="$(var.SourceDir)\x86\concrt140.dll" KeyPath="yes" /> + <Directory Id="owd7142663FADDFFB05CCE01DC370DE4995" Name="zh-Hans"> + <Component Id="owc28B8853BEB28067D4AB79B73216A3AC8" Guid="f6b6af15-bb3a-3eda-972d-47cb28af6c03"> + <File Id="owf28B8853BEB28067D4AB79B73216A3AC8" Source="$(var.SourceDir)\zh-Hans\Microsoft.CodeAnalysis.CSharp.resources.dll" KeyPath="yes" /> </Component> - <Component Id="owc326B123F4EAB21E3B400B7357CCFCB69" Guid="7acba6ad-3e71-b056-9a79-7e53f7d4585b"> - <File Id="owf326B123F4EAB21E3B400B7357CCFCB69" Source="$(var.SourceDir)\x86\cvextern.dll" KeyPath="yes" /> + <Component Id="owcDA343DBA19E680B537006D75B50A616D" Guid="898f5dc8-48bd-913f-968d-10e37f8347f7"> + <File Id="owfDA343DBA19E680B537006D75B50A616D" Source="$(var.SourceDir)\zh-Hans\Microsoft.CodeAnalysis.CSharp.Scripting.resources.dll" KeyPath="yes" /> </Component> - <Component Id="owcEBF7D14133500BEDB79449CCE2A5B412" Guid="3cfe7baa-8f60-50d4-0c7c-41e7f7409b69"> - <File Id="owfEBF7D14133500BEDB79449CCE2A5B412" Source="$(var.SourceDir)\x86\msvcp140.dll" KeyPath="yes" /> + <Component Id="owc074201B7ECF4E55183EF1FC13FC03B70" Guid="ef2d2267-19a2-0443-6ae4-16cfed6b8091"> + <File Id="owf074201B7ECF4E55183EF1FC13FC03B70" Source="$(var.SourceDir)\zh-Hans\Microsoft.CodeAnalysis.resources.dll" KeyPath="yes" /> </Component> - <Component Id="owcA62A9E2DCF3C1092572B9BA8A90AF2CC" Guid="a5901167-af03-d482-e4a2-55e2fd2dab37"> - <File Id="owfA62A9E2DCF3C1092572B9BA8A90AF2CC" Source="$(var.SourceDir)\x86\msvcp140_1.dll" KeyPath="yes" /> + <Component Id="owc5371E4C6A9B06D411E8EAC3E5A48E7EA" Guid="a7649dfb-52b9-08d3-6f03-f7aed4d2cce3"> + <File Id="owf5371E4C6A9B06D411E8EAC3E5A48E7EA" Source="$(var.SourceDir)\zh-Hans\Microsoft.CodeAnalysis.Scripting.resources.dll" KeyPath="yes" /> </Component> - <Component Id="owc4E55EDBDDABF300437BC351C60EA1BBB" Guid="253ff45b-49b3-cf8c-4b98-7ac426bdb997"> - <File Id="owf4E55EDBDDABF300437BC351C60EA1BBB" Source="$(var.SourceDir)\x86\msvcp140_2.dll" KeyPath="yes" /> + </Directory> + <Directory Id="owdA86DB5E08AB73906A10F69BC4AA81DE2" Name="zh-Hant"> + <Component Id="owc18EA70FFACC7303C11A678723F8E1919" Guid="71c1ee6c-af04-89be-a2f7-20205b5f371e"> + <File Id="owf18EA70FFACC7303C11A678723F8E1919" Source="$(var.SourceDir)\zh-Hant\Microsoft.CodeAnalysis.CSharp.resources.dll" KeyPath="yes" /> </Component> - <Component Id="owcDE8398D0FE76287635F64D20414A83A9" Guid="b3a19e5c-5f5b-984b-06fc-91ec029138fe"> - <File Id="owfDE8398D0FE76287635F64D20414A83A9" Source="$(var.SourceDir)\x86\msvcp140_codecvt_ids.dll" KeyPath="yes" /> + <Component Id="owc7B45E327235A25974959071E8D015643" Guid="ebc62531-8adf-6e26-4b07-8c8ae1b1645c"> + <File Id="owf7B45E327235A25974959071E8D015643" Source="$(var.SourceDir)\zh-Hant\Microsoft.CodeAnalysis.CSharp.Scripting.resources.dll" KeyPath="yes" /> </Component> - <Component Id="owcB3105100F274DC6C121EB91815CE430E" Guid="17155558-2ffd-5452-640e-b77c2e2b86e5"> - <File Id="owfB3105100F274DC6C121EB91815CE430E" Source="$(var.SourceDir)\x86\opencv_videoio_ffmpeg440.dll" KeyPath="yes" /> + <Component Id="owc35485D602BBBDC5C5F40469EC390B90C" Guid="48279d95-5026-06e5-350f-7fb5d19fe1c1"> + <File Id="owf35485D602BBBDC5C5F40469EC390B90C" Source="$(var.SourceDir)\zh-Hant\Microsoft.CodeAnalysis.resources.dll" KeyPath="yes" /> </Component> - <Component Id="owcFE2AD2B6C739ABAFD86DE40F4D5B83C9" Guid="30610dbe-339b-b1be-a4a6-626e8fc07490"> - <File Id="owfFE2AD2B6C739ABAFD86DE40F4D5B83C9" Source="$(var.SourceDir)\x86\vcruntime140.dll" KeyPath="yes" /> + <Component Id="owc505D5854210A555AEF782935C164E813" Guid="778f403b-5deb-df93-7dc7-44b5fa099c25"> + <File Id="owf505D5854210A555AEF782935C164E813" Source="$(var.SourceDir)\zh-Hant\Microsoft.CodeAnalysis.Scripting.resources.dll" KeyPath="yes" /> </Component> </Directory> </Directory> diff --git a/UVtools.Platforms/arch-x64/libcvextern.so b/UVtools.Platforms/arch-x64/libcvextern.so Binary files differindex b8b0375..4027368 100644 --- a/UVtools.Platforms/arch-x64/libcvextern.so +++ b/UVtools.Platforms/arch-x64/libcvextern.so diff --git a/UVtools.Platforms/osx-x64/libcvextern.dylib b/UVtools.Platforms/osx-x64/libcvextern.dylib Binary files differindex 1af1f8c..1b40298 100644 --- a/UVtools.Platforms/osx-x64/libcvextern.dylib +++ b/UVtools.Platforms/osx-x64/libcvextern.dylib diff --git a/UVtools.Scripts/Erode-Bottom.ps1 b/UVtools.Scripts/Erode-Bottom.ps1 index 3087662..5f72bcd 100644 --- a/UVtools.Scripts/Erode-Bottom.ps1 +++ b/UVtools.Scripts/Erode-Bottom.ps1 @@ -110,19 +110,19 @@ $slicerFile.Decode($inputFile, $progress); ################################################### # Morph bottom erode Write-Output "Eroding bottoms with ${iterations} iterations, please wait..." -$morph = New-Object UVtools.Core.Operations.OperationMorph +$morph = [UVtools.Core.Operations.OperationMorph]::new($slicerFile) $morph.MorphOperation = [Emgu.CV.CvEnum.MorphOp]::Erode $morph.IterationsStart = $iterations -$morph.LayerIndexEnd = $slicerFile.BottomLayerCount - 1 -if(!$morph.Execute($slicerFile, $progress)){ return; } +$morph.SelectBottomLayers() +if(!$morph.Execute($progress)){ return; } ############## # Dont touch # ############## # Save file with _modified name appended -$filePath = [System.IO.Path]::GetDirectoryName($inputFile); -$fileExt = [System.IO.Path]::GetExtension($inputFile); +$filePath = [System.IO.Path]::GetDirectoryName($inputFile) +$fileExt = [System.IO.Path]::GetExtension($inputFile) $fileNoExt = [System.IO.Path]::GetFileNameWithoutExtension($inputFile) $fileOutput = "${filePath}${dirSeparator}${fileNoExt}_modified${fileExt}" Write-Output "Saving as ${fileNoExt}_modified${fileExt}, please wait..." diff --git a/UVtools.Scripts/README.md b/UVtools.Scripts/README.md index 447a3f9..b89927b 100644 --- a/UVtools.Scripts/README.md +++ b/UVtools.Scripts/README.md @@ -79,31 +79,30 @@ Take **[Erode-Bottom.ps1](https://github.com/sn4k3/UVtools/blob/master/UVtools.S * Example: ```Powershell # Erode bottom layers - $morph = New-Object UVtools.Core.Operations.OperationMorph + $morph = [UVtools.Core.Operations.OperationMorph]::new($slicerFile) $morph.MorphOperation = [Emgu.CV.CvEnum.MorphOp]::Erode $morph.IterationsStart = $iterations - $morph.LayerIndexEnd = $slicerFile.BottomLayerCount - 1 - if(!$morph.Execute($slicerFile, $progress)){ return; } + $morph.SelectBottomLayers() + if(!$morph.Execute($progress)){ return } # Reuse object and erode normal layers with 1 less iteration $morph.IterationsStart = $iterations - 1 - $morph.LayerIndexStart = $slicerFile.BottomLayerCount - $morph.LayerIndexEnd = $slicerFile.LayerCount - 1 - if(!$morph.Execute($slicerFile, $progress)){ return } + $morph.SelectNormalLayers() + if(!$morph.Execute($progress)){ return } - # Rotate layer 0, 45 - $rotate = New-Object UVtools.Core.Operations.OperationRotate + # Rotate all layers, 45 + $rotate = [UVtools.Core.Operations.OperationRotate]::new($slicerFile) $rotate.AngleDegrees = 45; - if(!$rotate.Execute($slicerFile, $progress)){ return } + if(!$rotate.Execute($progress)){ return } # Rotate layer 1, 90 $rotate.LayerIndexStart = 1 $rotate.LayerIndexEnd = 1 $rotate.AngleDegrees = 90 - if(!$rotate.Execute($slicerFile, $progress)){ return } + if(!$rotate.Execute($progress)){ return } ``` ## Contribute with your scripts -If you make a usefull script and want to contribute you can share and publish your script under [github - issues](https://github.com/sn4k3/UVtools/issues/new?assignees=sn4k3&labels=script&template=script.md&title=%5BSCRIPT%5D+). +If you make a usefull script and want to contribute you can share and publish your script under [github - discussions - scripts](https://github.com/sn4k3/UVtools/discussions/categories/scripts). After analyzation it will be published on the repository
\ No newline at end of file diff --git a/UVtools.WPF/App.axaml b/UVtools.WPF/App.axaml index 3eb218e..ede140d 100644 --- a/UVtools.WPF/App.axaml +++ b/UVtools.WPF/App.axaml @@ -2,9 +2,10 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="UVtools.WPF.App"> <Application.Styles> - <StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/> - <StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/> - <StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Default.xaml"/> - <StyleInclude Source="avares://ThemeEditor.Controls.ColorPicker/ColorPicker.xaml"/> + <FluentTheme Mode="Light"/> + <StyleInclude Source="avares://ThemeEditor.Controls.ColorPicker/ColorPicker.axaml"/> + <StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/> + <StyleInclude Source="/Assets/Styles/Styles.xaml" /> + <StyleInclude Source="/Assets/Styles/StylesLight.xaml" /> </Application.Styles> </Application> diff --git a/UVtools.WPF/App.axaml.cs b/UVtools.WPF/App.axaml.cs index 440cfff..3e330b0 100644 --- a/UVtools.WPF/App.axaml.cs +++ b/UVtools.WPF/App.axaml.cs @@ -24,13 +24,12 @@ using UVtools.Core; using UVtools.Core.FileFormats; using UVtools.WPF.Extensions; using UVtools.WPF.Structures; -using Size = System.Drawing.Size; namespace UVtools.WPF { public class App : Application { - public static IThemeSelector? ThemeSelector { get; set; } + public static ThemeSelector ThemeSelector { get; set; } public static MainWindow MainWindow; public static FileFormat SlicerFile = null; @@ -53,7 +52,7 @@ namespace UVtools.WPF OperationProfiles.Load(); - ThemeSelector = Avalonia.ThemeManager.ThemeSelector.Create(Path.Combine(ApplicationPath, "Assets", "Themes")); + /*ThemeSelector = ThemeSelector.Create(Path.Combine(ApplicationPath, "Assets", "Themes")); ThemeSelector.LoadSelectedTheme(Path.Combine(UserSettings.SettingsFolder, "selected.theme")); if (ThemeSelector.SelectedTheme.Name == "UVtoolsDark" || ThemeSelector.SelectedTheme.Name == "Light") { @@ -63,13 +62,16 @@ namespace UVtools.WPF theme.ApplyTheme(); break; } - } - - MainWindow = new MainWindow(); + }*/ + MainWindow = new MainWindow(); try { - CvInvoke.CheckLibraryLoaded(); + if(!CvInvoke.Init()) + await MainWindow.MessageBoxError("UVtools can not init OpenCV library\n" + + "Please build or install this dependencies in order to run UVtools\n" + + "Check manual or page at 'Requirements' section for help", + "UVtools can not run"); } catch (Exception e) { @@ -82,8 +84,7 @@ namespace UVtools.WPF } desktop.MainWindow = MainWindow; - desktop.Exit += (sender, e) - => ThemeSelector.SaveSelectedTheme(Path.Combine(UserSettings.SettingsFolder, "selected.theme")); + //desktop.Exit += (sender, e) => ThemeSelector.SaveSelectedTheme(Path.Combine(UserSettings.SettingsFolder, "selected.theme")); } base.OnFrameworkInitializationCompleted(); @@ -191,6 +192,8 @@ namespace UVtools.WPF if (OperatingSystem.IsLinux()) { + var folder1 = $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}{Path.DirectorySeparatorChar}.config{Path.DirectorySeparatorChar}PrusaSlicer"; + if (Directory.Exists(folder1)) return folder1; return $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}{Path.DirectorySeparatorChar}.PrusaSlicer"; } diff --git a/UVtools.WPF/Assets/Icons/dynamic-layers-16x16.png b/UVtools.WPF/Assets/Icons/dynamic-layers-16x16.png Binary files differnew file mode 100644 index 0000000..1644ec4 --- /dev/null +++ b/UVtools.WPF/Assets/Icons/dynamic-layers-16x16.png diff --git a/UVtools.WPF/Assets/Styles/Styles.xaml b/UVtools.WPF/Assets/Styles/Styles.xaml new file mode 100644 index 0000000..f86e52c --- /dev/null +++ b/UVtools.WPF/Assets/Styles/Styles.xaml @@ -0,0 +1,53 @@ +<Styles xmlns="https://github.com/avaloniaui" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + + <Design.PreviewWith> + <Border Padding="20"></Border> + </Design.PreviewWith> + + <Style Selector="Expander[IsExpanded=true] /template/ ToggleButton#PART_toggle /template/ ContentPresenter#PART_ContentPresenter"> + <Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForeground}"/> + </Style> + + <Style Selector="Border.GroupBox"> + <Setter Property="BorderThickness" Value="4" /> + </Style> + + <Style Selector="TextBlock.GroupBoxHeader"> + <Setter Property="Padding" Value="10" /> + <Setter Property="FontWeight" Value="Bold" /> + </Style> + + <Style Selector="Border.FooterActions"> + <Setter Property="Padding" Value="5,20" /> + <Setter Property="Margin" Value="0,10,0, 0" /> + </Style> + + <Style Selector="TextBox.TransparentReadOnly"> + <Setter Property="TextWrapping" Value="Wrap" /> + <Setter Property="IsReadOnly" Value="True" /> + <Setter Property="Background" Value="Transparent" /> + <Setter Property="CaretBrush" Value="Transparent" /> + <Setter Property="BorderBrush" Value="Transparent" /> + <Setter Property="AcceptsReturn" Value="True" /> + <Setter Property="AcceptsTab" Value="True" /> + </Style> + + <Style Selector="RadioButton"> + <Setter Property="MinWidth" Value="30"></Setter> + </Style> + + <Style Selector="CheckBox"> + <Setter Property="MinWidth" Value="30"></Setter> + </Style> + + <Style Selector="NumericUpDown"> + <Setter Property="MinWidth" Value="130"></Setter> + <Setter Property="ClipValueToMinMax" Value="True"></Setter> + </Style> + + <Style Selector="TabControl"> + <Setter Property="Padding" Value="0"></Setter> + </Style> + +</Styles>
\ No newline at end of file diff --git a/UVtools.WPF/Assets/Styles/StylesLight.xaml b/UVtools.WPF/Assets/Styles/StylesLight.xaml new file mode 100644 index 0000000..d030ff4 --- /dev/null +++ b/UVtools.WPF/Assets/Styles/StylesLight.xaml @@ -0,0 +1,20 @@ +<Styles xmlns="https://github.com/avaloniaui" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + + <Design.PreviewWith> + <Border Padding="20"></Border> + </Design.PreviewWith> + + <Style Selector="Border.GroupBox"> + <Setter Property="BorderBrush" Value="LightBlue" /> + </Style> + + <Style Selector="TextBlock.GroupBoxHeader"> + <Setter Property="Background" Value="LightBlue" /> + </Style> + + <Style Selector="Border.FooterActions"> + <Setter Property="Background" Value="LightGray" /> + </Style> + +</Styles>
\ No newline at end of file diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateElephantFootControl.axaml b/UVtools.WPF/Controls/Calibrators/CalibrateElephantFootControl.axaml index 01da1d5..848c04f 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateElephantFootControl.axaml +++ b/UVtools.WPF/Controls/Calibrators/CalibrateElephantFootControl.axaml @@ -10,18 +10,17 @@ <StackPanel Spacing="10"> <Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto" - ColumnDefinitions="Auto,10,100,5,Auto,20,Auto,10,100,5,Auto" + ColumnDefinitions="Auto,10,150,5,Auto,20,Auto,10,150,5,Auto" > <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Text="Layer height:"/> <NumericUpDown Grid.Row="0" Grid.Column="2" - ClipValueToMinMax="True" Increment="0.01" Minimum="0.01" Maximum="0.30" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.LayerHeight}" /> <TextBlock Grid.Row="0" Grid.Column="4" @@ -41,7 +40,7 @@ Increment="1" Minimum="1" Maximum="1000" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.BottomLayers}" /> <TextBlock Grid.Row="2" Grid.Column="4" @@ -60,7 +59,7 @@ Increment="1" Minimum="1" Maximum="1000" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.NormalLayers}" /> <TextBlock Grid.Row="2" Grid.Column="10" @@ -181,7 +180,7 @@ <Grid Margin="0,10,0,0" RowDefinitions="Auto,10,Auto" - ColumnDefinitions="Auto,10,80,5,Auto,5,80,5,Auto,40,Auto,10,80" + ColumnDefinitions="Auto,10,Auto,5,Auto,5,Auto,5,Auto,30,Auto,10,Auto" > <CheckBox Grid.Row="0" Grid.Column="2" @@ -265,7 +264,7 @@ <Grid Margin="0,10,0,0" RowDefinitions="Auto,10,Auto,10,Auto,5,Auto" - ColumnDefinitions="Auto,10,80,5,Auto,5,80,5,Auto,20,Auto,10,80" + ColumnDefinitions="Auto,10,Auto,5,Auto,5,Auto,5,Auto,20,Auto,10,Auto" > <CheckBox Grid.Row="0" Grid.Column="2" diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateElephantFootControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateElephantFootControl.axaml.cs index f854c65..f5ec546 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateElephantFootControl.axaml.cs +++ b/UVtools.WPF/Controls/Calibrators/CalibrateElephantFootControl.axaml.cs @@ -29,16 +29,10 @@ namespace UVtools.WPF.Controls.Calibrators public CalibrateElephantFootControl() { - this.InitializeComponent(); - BaseOperation = new OperationCalibrateElephantFoot(); + InitializeComponent(); + + BaseOperation = new OperationCalibrateElephantFoot(SlicerFile); - if(App.SlicerFile is not null) - { - Operation.LayerHeight = (decimal) App.SlicerFile.LayerHeight; - Operation.BottomExposure = (decimal) App.SlicerFile.BottomExposureTime; - Operation.NormalExposure = (decimal) App.SlicerFile.ExposureTime; - } - _kernelCtrl = this.Find<KernelControl>("KernelCtrl"); _timer = new Timer(20) @@ -60,7 +54,6 @@ namespace UVtools.WPF.Controls.Calibrators { case ToolWindow.Callbacks.Init: case ToolWindow.Callbacks.ProfileLoaded: - Operation.Resolution = App.SlicerFile.Resolution; Operation.PropertyChanged += (sender, e) => { _timer.Stop(); diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml b/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml new file mode 100644 index 0000000..13e29fc --- /dev/null +++ b/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml @@ -0,0 +1,592 @@ +<UserControl xmlns="https://github.com/avaloniaui" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" + x:Class="UVtools.WPF.Controls.Calibrators.CalibrateExposureFinderControl"> + <Grid ColumnDefinitions="Auto,10,380"> + <StackPanel Spacing="10"> + + <Border BorderBrush="Black" BorderThickness="1" Padding="5"> + <Expander IsExpanded="True"> + <Expander.Header> + <TextBlock Text="Commun properties" + FontWeight="Bold" + Cursor="Hand"/> + </Expander.Header> + + <Grid + RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto" + ColumnDefinitions="Auto,10,170,5,Auto,20,Auto,10,170,5,Auto"> + + <TextBlock + Grid.Row="0" Grid.Column="0" + VerticalAlignment="Center" + ToolTip.Tip="The printer display width. Required to calculate the pixels per mm." + Text="Display width:"/> + <NumericUpDown Grid.Row="0" Grid.Column="2" + ClipValueToMinMax="True" + Increment="0.1" + Minimum="0" + Maximum="10000" + FormatString="F02" + Value="{Binding Operation.DisplayWidth}"/> + <TextBlock Grid.Row="0" Grid.Column="4" + VerticalAlignment="Center" + Text="mm"/> + + <TextBlock Grid.Row="0" Grid.Column="6" + VerticalAlignment="Center" + ToolTip.Tip="The printer display height. Required to calculate the pixels per mm." + Text="Display height:"/> + <NumericUpDown Grid.Row="0" Grid.Column="8" + ClipValueToMinMax="True" + Increment="0.1" + Minimum="0" + Maximum="10000" + FormatString="F02" + Value="{Binding Operation.DisplayHeight}"/> + <TextBlock Grid.Row="0" Grid.Column="10" + VerticalAlignment="Center" + Text="mm"/> + + <TextBlock Grid.Row="2" Grid.Column="0" + VerticalAlignment="Center" + Text="Layer height:"/> + <NumericUpDown Grid.Row="2" Grid.Column="2" + Increment="0.01" + Minimum="0.01" + Maximum="0.30" + FormatString="F02" + Value="{Binding Operation.LayerHeight}" + /> + <TextBlock Grid.Row="2" Grid.Column="4" + VerticalAlignment="Center" + Text="mm"/> + + <TextBlock Grid.Row="2" Grid.Column="6" + VerticalAlignment="Center" + Text="Bottom layers:"/> + <NumericUpDown Grid.Row="2" Grid.Column="8" + ClipValueToMinMax="True" + Increment="1" + Minimum="1" + Maximum="1000" + Value="{Binding Operation.BottomLayers}"/> + <TextBlock Grid.Row="2" Grid.Column="8" + VerticalAlignment="Center" + Text="{Binding Operation.BottomHeight, StringFormat=\{0:0.00\}mm}"/> + + <TextBlock Grid.Row="4" Grid.Column="0" + VerticalAlignment="Center" + Text="Bottom exposure:"/> + <NumericUpDown Grid.Row="4" Grid.Column="2" + ClipValueToMinMax="True" + Increment="0.5" + Minimum="0.1" + Maximum="200" + FormatString="F02" + Value="{Binding Operation.BottomExposure}"/> + <TextBlock Grid.Row="4" Grid.Column="4" + VerticalAlignment="Center" + Text="s"/> + + <TextBlock Grid.Row="4" Grid.Column="6" + VerticalAlignment="Center" + Text="Normal exposure:"/> + <NumericUpDown Grid.Row="4" Grid.Column="8" + ClipValueToMinMax="True" + Increment="0.5" + Minimum="0.1" + Maximum="200" + FormatString="F02" + Value="{Binding Operation.NormalExposure}"/> + <TextBlock Grid.Row="4" Grid.Column="10" + VerticalAlignment="Center" + Text="s"/> + + <TextBlock Grid.Row="6" Grid.Column="0" + VerticalAlignment="Center" + Text="Top/bottom margin:"/> + <NumericUpDown Grid.Row="6" Grid.Column="2" + ClipValueToMinMax="True" + Increment="0.5" + Minimum="0" + Maximum="1000" + Value="{Binding Operation.TopBottomMargin}"/> + <TextBlock Grid.Row="6" Grid.Column="4" + VerticalAlignment="Center" + Text="mm"/> + + <TextBlock Grid.Row="6" Grid.Column="6" + VerticalAlignment="Center" + Text="Left/right margin:"/> + <NumericUpDown Grid.Row="6" Grid.Column="8" + ClipValueToMinMax="True" + Increment="0.5" + Minimum="0" + Maximum="1000" + Value="{Binding Operation.LeftRightMargin}"/> + <TextBlock Grid.Row="6" Grid.Column="10" + VerticalAlignment="Center" + Text="mm"/> + + <TextBlock Grid.Row="8" Grid.Column="0" + VerticalAlignment="Center" + Text="Part margin:"/> + <NumericUpDown Grid.Row="8" Grid.Column="2" + ClipValueToMinMax="True" + Increment="1" + Minimum="0" + Maximum="10000" + Value="{Binding Operation.PartMargin}"/> + <TextBlock Grid.Row="8" Grid.Column="4" + VerticalAlignment="Center" + Text="mm"/> + + <TextBlock Grid.Row="10" Grid.Column="0" + VerticalAlignment="Center" + ToolTip.Tip="Chamfer the bottom and top layers" + Text="Chamfer layers:"/> + <NumericUpDown Grid.Row="10" Grid.Column="2" + ClipValueToMinMax="True" + Increment="1" + Minimum="0" + Maximum="255" + IsEnabled="{Binding Operation.ChamferModel}" + Value="{Binding Operation.ChamferLayers}"/> + + <TextBlock Grid.Row="10" Grid.Column="6" + VerticalAlignment="Center" + ToolTip.Tip="Erode bottom iterations to counter the elephant foot" + Text="Erode bottom iter.:"/> + <NumericUpDown Grid.Row="10" Grid.Column="8" + ClipValueToMinMax="True" + Increment="1" + Minimum="0" + Maximum="255" + Value="{Binding Operation.ErodeBottomIterations}"/> + + <CheckBox Grid.Row="12" Grid.Column="2" Grid.ColumnSpan="5" + VerticalAlignment="Center" + IsChecked="{Binding Operation.EnableAntiAliasing}" + Content="Enable Anti-Aliasing"/> + + <CheckBox Grid.Row="12" Grid.Column="8" + Grid.ColumnSpan="3" + ToolTip.Tip="Most of the printers requires a mirror output to print with the correct orientation" + IsChecked="{Binding Operation.MirrorOutput}" + Content="Mirror output" /> + + </Grid> + </Expander> + </Border> + + <Border BorderBrush="Black" BorderThickness="1" Padding="5"> + <Expander IsExpanded="True"> + <Expander.Header> + <TextBlock Text="Object configuration" + FontWeight="Bold" + Cursor="Hand"/> + + </Expander.Header> + + <Grid RowDefinitions="Auto,10,Auto,10,Auto" + ColumnDefinitions="Auto,10,Auto,5,Auto,20,Auto,10,Auto,5,Auto,20,Auto,10,Auto,5,Auto"> + <TextBlock Grid.Row="0" Grid.Column="0" + Text="Base height:" + VerticalAlignment="Center"/> + <NumericUpDown Grid.Row="0" Grid.Column="2" + ClipValueToMinMax="True" + Increment="0.5" + Minimum="0.3" + Maximum="100" + FormatString="F02" + Value="{Binding Operation.BaseHeight}"/> + <TextBlock Grid.Row="0" Grid.Column="4" + VerticalAlignment="Center" + Text="mm"/> + + <TextBlock Grid.Row="0" Grid.Column="6" + Text="Cylinder height:" + VerticalAlignment="Center"/> + <NumericUpDown Grid.Row="0" Grid.Column="8" + ClipValueToMinMax="True" + Increment="0.5" + Minimum="1" + Maximum="100" + FormatString="F02" + Value="{Binding Operation.CylinderHeight}"/> + <TextBlock Grid.Row="0" Grid.Column="10" + VerticalAlignment="Center" + Text="mm"/> + + <TextBlock Grid.Row="0" Grid.Column="12" + Text="Cylinder margin:" + VerticalAlignment="Center"/> + <NumericUpDown Grid.Row="0" Grid.Column="14" + ClipValueToMinMax="True" + Increment="0.5" + Minimum="1" + Maximum="100" + FormatString="F02" + Value="{Binding Operation.CylinderMargin}"/> + <TextBlock Grid.Row="0" Grid.Column="16" + VerticalAlignment="Center" + Text="mm"/> + + <TextBlock Grid.Row="2" Grid.Column="0" + Text="Shape:" + VerticalAlignment="Center"/> + + <ComboBox Grid.Row="2" Grid.Column="2" + HorizontalAlignment="Stretch" + Items="{Binding Operation.ShapesItems}" + SelectedItem="{Binding Operation.Shape}"/> + + <TextBlock Grid.Row="2" Grid.Column="6" + Text="Unit of measure:" + VerticalAlignment="Center"/> + + <ComboBox Grid.Row="2" Grid.Column="8" + HorizontalAlignment="Stretch" + Items="{Binding Operation.MeasuresItems}" + SelectedItem="{Binding Operation.UnitOfMeasure}"/> + + + + <TextBlock Grid.Row="4" Grid.Column="0" + Text="Diameters:" + ToolTip.Tip="Diameters separated by a comma (,). Order doesn't matter." + VerticalAlignment="Center"/> + + <TextBox Grid.Row="4" Grid.Column="2" + Grid.ColumnSpan="13" + IsVisible="{Binding Operation.IsUnitOfMeasureMm}" + Text="{Binding Operation.CylinderDiametersMm}"/> + + <TextBox Grid.Row="4" Grid.Column="2" + Grid.ColumnSpan="13" + IsVisible="{Binding !Operation.IsUnitOfMeasureMm}" + Text="{Binding Operation.CylinderDiametersPx}"/> + + <TextBlock Grid.Row="4" Grid.Column="16" + IsVisible="{Binding Operation.IsUnitOfMeasureMm}" + VerticalAlignment="Center" + Text="mm"/> + + <TextBlock Grid.Row="4" Grid.Column="16" + IsVisible="{Binding !Operation.IsUnitOfMeasureMm}" + VerticalAlignment="Center" + Text="px"/> + </Grid> + + </Expander> + </Border> + + <Border BorderBrush="Black" + BorderThickness="1" + Padding="5" + IsVisible="{Binding CanSupportPerLayerSettings}"> + <Expander IsExpanded="True"> + <Expander.Header> + <TextBlock Text="Multiple layer height" + FontWeight="Bold" + Cursor="Hand"/> + + </Expander.Header> + + <StackPanel Spacing="10"> + + <TextBlock Text="Only few printers support this, make sure your is supported or else it will print a malformed model. +
After this, do not apply any modification which reconstruct the z positions of the layers."/> + + <CheckBox + Content="Enable - For advanced users only!" + IsChecked="{Binding Operation.MultipleLayerHeight}"/> + + <Grid RowDefinitions="Auto" + ColumnDefinitions="Auto,10,Auto,5,Auto,20,Auto,10,Auto,5,Auto,20,Auto,10,Auto,5,Auto" + IsEnabled="{Binding Operation.MultipleLayerHeight}"> + <TextBlock Grid.Row="0" Grid.Column="0" + Text="Start layer height:" + VerticalAlignment="Center"/> + <NumericUpDown Grid.Row="0" Grid.Column="2" + Increment="0.01" + Minimum="0.01" + Maximum="0.30" + FormatString="F02" + Value="{Binding Operation.LayerHeight}"/> + <TextBlock Grid.Row="0" Grid.Column="4" + VerticalAlignment="Center" + Text="mm"/> + + <TextBlock Grid.Row="0" Grid.Column="6" + Text="Increment step:" + VerticalAlignment="Center"/> + <NumericUpDown Grid.Row="0" Grid.Column="8" + Increment="0.01" + Minimum="0.01" + Maximum="0.1" + FormatString="F02" + Value="{Binding Operation.MultipleLayerHeightStep}"/> + <TextBlock Grid.Row="0" Grid.Column="10" + VerticalAlignment="Center" + Text="mm"/> + + <TextBlock Grid.Row="0" Grid.Column="12" + Text="Maximum layer height:" + VerticalAlignment="Center"/> + <NumericUpDown Grid.Row="0" Grid.Column="14" + Increment="0.01" + Minimum="{Binding Operation.LayerHeight}" + Maximum="0.3" + FormatString="F02" + Value="{Binding Operation.MultipleLayerHeightMaximum}"/> + <TextBlock Grid.Row="0" Grid.Column="16" + VerticalAlignment="Center" + Text="mm"/> + </Grid> + </StackPanel> + + </Expander> + </Border> + + <Border BorderBrush="Black" + BorderThickness="1" + Padding="5" + IsVisible="{Binding CanSupportPerLayerSettings}"> + <Expander IsExpanded="True"> + <Expander.Header> + <TextBlock Text="Multiple exposures" + FontWeight="Bold" + Cursor="Hand"/> + + </Expander.Header> + + <StackPanel Spacing="10"> + + <TextBlock Text="Only few printers support this, make sure your is supported or else it will print a malformed model. +
After this, do not apply any modification which reconstruct the z positions of the layers."/> + + <CheckBox + Content="Enable - For advanced users only!" + IsChecked="{Binding Operation.MultipleExposures}"/> + + <TextBlock Text="Automatic exposure generation:" + IsEnabled="{Binding Operation.MultipleExposures}" + FontWeight="Bold"/> + + <Grid RowDefinitions="Auto,10,Auto" + ColumnDefinitions="Auto,10,Auto,5,Auto,20,Auto,10,Auto,5,Auto,20,Auto,10,Auto,5,Auto" + IsEnabled="{Binding Operation.MultipleExposures}"> + + + <TextBlock Grid.Row="0" Grid.Column="0" + ToolTip.Tip="Linear: Current exposure + step * test number.
 +Multiplier: Current exposure * layer height * step * test number." + Text="Generation type:" + VerticalAlignment="Center"/> + <ComboBox Grid.Row="0" Grid.Column="2" + VerticalAlignment="Center" + HorizontalAlignment="Stretch" + Items="{Binding Operation.ExposureGenTypeItems}" + SelectedItem="{Binding Operation.ExposureGenType}"/> + + <CheckBox Grid.Row="0" Grid.Column="6" Grid.ColumnSpan="5" + VerticalAlignment="Center" + Content="Ignore global exposures" + IsChecked="{Binding Operation.ExposureGenIgnoreBaseExposure}"/> + + <Button Grid.Row="0" Grid.Column="12" Grid.ColumnSpan="5" + Content="Generate exposure table" + VerticalContentAlignment="Center" + HorizontalContentAlignment="Center" + HorizontalAlignment="Stretch" + VerticalAlignment="Stretch" + Command="{Binding GenerateExposureTable}"/> + + <TextBlock Grid.Row="2" Grid.Column="0" + ToolTip.Tip="Bottom exposure step" + Text="Bottom step:" + VerticalAlignment="Center"/> + <NumericUpDown Grid.Row="2" Grid.Column="2" + Increment="0.01" + Minimum="0" + Maximum="1000" + FormatString="F02" + Value="{Binding Operation.ExposureGenBottomStep}"/> + <TextBlock Grid.Row="2" Grid.Column="4" + VerticalAlignment="Center" + Text="s"/> + + <TextBlock Grid.Row="2" Grid.Column="6" + ToolTip.Tip="Normal exposure step" + Text="Normal step:" + VerticalAlignment="Center"/> + <NumericUpDown Grid.Row="2" Grid.Column="8" + Increment="0.01" + Minimum="0.01" + Maximum="1000" + FormatString="F02" + Value="{Binding Operation.ExposureGenNormalStep}"/> + <TextBlock Grid.Row="2" Grid.Column="10" + VerticalAlignment="Center" + Text="s"/> + + <TextBlock Grid.Row="2" Grid.Column="12" + ToolTip.Tip="Maximum of generations per layer height" + Text="Maximum generations:" + VerticalAlignment="Center"/> + <NumericUpDown Grid.Row="2" Grid.Column="14" + Increment="1" + Minimum="1" + Maximum="255" + Value="{Binding Operation.ExposureGenTests}"/> + <TextBlock Grid.Row="2" Grid.Column="16" + VerticalAlignment="Center" + Text="tests"/> + </Grid> + + <TextBlock Text="Manual exposure entry:" + IsEnabled="{Binding Operation.MultipleExposures}" + FontWeight="Bold"/> + + <Grid RowDefinitions="Auto,10,Auto" + ColumnDefinitions="Auto,10,Auto,5,Auto,20,Auto,10,Auto,5,Auto,20,Auto,10,Auto,5,Auto,10,Auto" + IsEnabled="{Binding Operation.MultipleExposures}"> + <TextBlock Grid.Row="0" Grid.Column="0" + Text="Layer height:" + VerticalAlignment="Center"/> + <ComboBox Grid.Row="0" Grid.Column="2" + VerticalAlignment="Center" + HorizontalAlignment="Stretch" + MinWidth="100" + Items="{Binding Operation.AvailableLayerHeights}" + SelectedItem="{Binding Operation.ExposureGenManualLayerHeight}"/> + <TextBlock Grid.Row="0" Grid.Column="4" + Text="mm" + VerticalAlignment="Center"/> + + <TextBlock Grid.Row="0" Grid.Column="6" + Text="Bottom exposure:" + VerticalAlignment="Center"/> + <NumericUpDown Grid.Row="0" Grid.Column="8" + Increment="0.1" + Minimum="0.5" + Maximum="1000" + FormatString="F02" + Value="{Binding Operation.ExposureGenManualBottom}"/> + <TextBlock Grid.Row="0" Grid.Column="10" + VerticalAlignment="Center" + Text="s"/> + + <TextBlock Grid.Row="0" Grid.Column="12" + Text="Normal exposure:" + VerticalAlignment="Center"/> + <NumericUpDown Grid.Row="0" Grid.Column="14" + Increment="0.1" + Minimum="0.1" + Maximum="1000" + FormatString="F02" + Value="{Binding Operation.ExposureGenManualNormal}"/> + <TextBlock Grid.Row="0" Grid.Column="16" + VerticalAlignment="Center" + Text="s"/> + + <Button + Grid.Row="0" Grid.Column="18" VerticalContentAlignment="Center" + HorizontalContentAlignment="Center" + VerticalAlignment="Stretch" + IsEnabled="{Binding Operation.ExposureGenManualLayerHeight}" + Command="{Binding ExposureTableAddManual}"> + <StackPanel Orientation="Horizontal" Spacing="10"> + <Image Source="/Assets/Icons/plus-16x16.png"/> + <TextBlock + VerticalAlignment="Center" + Text="Add"/> + </StackPanel> + </Button> + </Grid> + + + + <Grid RowDefinitions="Auto" ColumnDefinitions="Auto,*,Auto" + IsEnabled="{Binding Operation.MultipleExposures}" + Margin="0,10,0,0"> + <TextBlock Text="{Binding Operation.ExposureTable.Count, StringFormat=Exposure table: {0} entries}" FontWeight="Bold"/> + + <Button + Grid.Column="1" VerticalContentAlignment="Center" + HorizontalContentAlignment="Center" + HorizontalAlignment="Right" + IsEnabled="{Binding #ExposureTable.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}" + Command="{Binding ExposureTableRemoveSelectedEntries}"> + <StackPanel Orientation="Horizontal" Spacing="10"> + <Image Source="/Assets/Icons/trash-16x16.png"/> + <TextBlock + VerticalAlignment="Center" + Text="Remove selected entries"/> + </StackPanel> + </Button> + + <Button + Grid.Column="2" VerticalContentAlignment="Center" + Margin="10,0,0,0" + HorizontalContentAlignment="Center" + HorizontalAlignment="Right" + IsEnabled="{Binding Operation.ExposureTable.Count}" + Command="{Binding ExposureTableClearEntries}"> + <StackPanel Orientation="Horizontal" Spacing="10"> + <Image Source="/Assets/Icons/delete-16x16.png"/> + <TextBlock + VerticalAlignment="Center" + Text="{Binding Operation.ExposureTable.Count, StringFormat=Clear {0} entries}"/> + </StackPanel> + </Button> + + </Grid> + + + <DataGrid + Name="ExposureTable" + CanUserReorderColumns="True" + CanUserResizeColumns="True" + CanUserSortColumns="True" + GridLinesVisibility="Horizontal" + IsReadOnly="False" + ClipboardCopyMode="IncludeHeader" + VerticalAlignment="Stretch" + MinHeight="100" + Margin="0,-10,0,0" + IsEnabled="{Binding Operation.MultipleExposures}" + Items="{Binding Operation.ExposureTable}"> + <DataGrid.Columns> + <DataGridTextColumn Header="Layer height (mm)" + Binding="{Binding LayerHeight}" + Width="Auto" /> + <DataGridTextColumn Header="Bottom exposure (s)" + Binding="{Binding BottomExposure}" + Width="Auto" /> + <DataGridTextColumn Header="Exposure (s)" + Binding="{Binding Exposure}" + Width="Auto" /> + </DataGrid.Columns> + + </DataGrid> + + </StackPanel> + + </Expander> + </Border> + + </StackPanel> + + <Image Grid.Column="2" + HorizontalAlignment="Center" + VerticalAlignment="Top" + MaxHeight="800" + Stretch="Uniform" + Source="{Binding PreviewImage}"/> + </Grid> +</UserControl> diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml.cs new file mode 100644 index 0000000..c5be0cf --- /dev/null +++ b/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml.cs @@ -0,0 +1,136 @@ +using System.Linq; +using System.Timers; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.Media.Imaging; +using Avalonia.Threading; +using DynamicData; +using MessageBox.Avalonia.Enums; +using UVtools.Core.FileFormats; +using UVtools.Core.Operations; +using UVtools.WPF.Controls.Tools; +using UVtools.WPF.Extensions; +using UVtools.WPF.Windows; + +namespace UVtools.WPF.Controls.Calibrators +{ + public class CalibrateExposureFinderControl : ToolControl + { + public OperationCalibrateExposureFinder Operation => BaseOperation as OperationCalibrateExposureFinder; + + private Timer _timer; + + private Bitmap _previewImage; + private DataGrid _exposureTable; + + public Bitmap PreviewImage + { + get => _previewImage; + set => RaiseAndSetIfChanged(ref _previewImage, value); + } + + public bool CanSupportPerLayerSettings => SlicerFile.HavePrintParameterPerLayerModifier(FileFormat.PrintParameterModifier.ExposureSeconds); + + public CalibrateExposureFinderControl() + { + InitializeComponent(); + _exposureTable = this.FindControl<DataGrid>("ExposureTable"); + BaseOperation = new OperationCalibrateExposureFinder(SlicerFile); + + _timer = new Timer(100) + { + AutoReset = false + }; + _timer.Elapsed += (sender, e) => Dispatcher.UIThread.InvokeAsync(UpdatePreview); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + public override void Callback(ToolWindow.Callbacks callback) + { + if (App.SlicerFile is null) return; + switch (callback) + { + case ToolWindow.Callbacks.Init: + case ToolWindow.Callbacks.ProfileLoaded: + Operation.PropertyChanged += (sender, e) => + { + _timer.Stop(); + _timer.Start(); + }; + _timer.Stop(); + _timer.Start(); + break; + } + } + + public void UpdatePreview() + { + var layers = Operation.GetLayers(); + _previewImage?.Dispose(); + PreviewImage = layers[^1].ToBitmap(); + foreach (var layer in layers) + { + layer.Dispose(); + } + } + + public async void GenerateExposureTable() + { + if (Operation.ExposureTable.Count > 0) + { + if (await ParentWindow.MessageBoxQuestion( + "This automatic exposure table generation will clear the current table data!\n" + + "Do you want to continue?" + ) != ButtonResult.Yes) return; + } + + Operation.GenerateExposure(); + } + + public async void ExposureTableAddManual() + { + var exposure = Operation.ExposureManualEntry; + if (!exposure.IsValid) + { + await ParentWindow.MessageBoxError( + $"Layer height and exposures must be higher than zero (0).\n{exposure}"); + return; + } + + if (Operation.ExposureTable.Contains(exposure)) + { + await ParentWindow.MessageBoxError( + $"The configured layer height and exposure data already exists on the table.\n{exposure}"); + return; + } + + Operation.ExposureTable.Add(exposure); + Operation.SanitizeExposureTable(); + } + + public async void ExposureTableClearEntries() + { + if (Operation.ExposureTable.Count <= 0) return; + if (await ParentWindow.MessageBoxQuestion( + $"Are you sure you want to all the {Operation.ExposureTable.Count} entries?" + ) != ButtonResult.Yes) return; + + Operation.ExposureTable.Clear(); + } + + public async void ExposureTableRemoveSelectedEntries() + { + if (_exposureTable.SelectedItems.Count <= 0) return; + if (await ParentWindow.MessageBoxQuestion( + $"Are you sure you want to remove the {_exposureTable.SelectedItems.Count} selected entries?" + ) != ButtonResult.Yes) return; + + Operation.ExposureTable.RemoveMany(_exposureTable.SelectedItems.Cast<OperationCalibrateExposureFinder.ExposureItem>()); + } + } +} diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml b/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml index 0b8421f..64dd735 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml +++ b/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml @@ -13,6 +13,8 @@ Content="Photonsters Validation Matrix / Exposure finder" Command="{Binding ButtonClicked}" VerticalAlignment="Center" + HorizontalAlignment="Stretch" + HorizontalContentAlignment="Center" CommandParameter="https://www.thingiverse.com/thing:4707289"/> <Button Grid.Row="1" @@ -21,6 +23,8 @@ Content="Support pull test" Command="{Binding ButtonClicked}" VerticalAlignment="Center" + HorizontalAlignment="Stretch" + HorizontalContentAlignment="Center" CommandParameter="https://www.thingiverse.com/thing:4308175"/> <Button Grid.Row="2" @@ -29,6 +33,8 @@ Content="AmeraLabs town calibration part" Command="{Binding ButtonClicked}" VerticalAlignment="Center" + HorizontalAlignment="Stretch" + HorizontalContentAlignment="Center" CommandParameter="https://www.thingiverse.com/thing:2810666"/> </Grid> diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml.cs index c2ed96a..5030ce3 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml.cs +++ b/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml.cs @@ -12,9 +12,8 @@ namespace UVtools.WPF.Controls.Calibrators public OperationCalibrateExternalTests Operation => BaseOperation as OperationCalibrateExternalTests; public CalibrateExternalTestsControl() { - this.InitializeComponent(); - BaseOperation = new OperationCalibrateExternalTests(); - Debug.WriteLine("asdasdasdasd"); + InitializeComponent(); + BaseOperation = new OperationCalibrateExternalTests(SlicerFile); } private void InitializeComponent() diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateGrayscaleControl.axaml b/UVtools.WPF/Controls/Calibrators/CalibrateGrayscaleControl.axaml index 4e955ec..a3939ca 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateGrayscaleControl.axaml +++ b/UVtools.WPF/Controls/Calibrators/CalibrateGrayscaleControl.axaml @@ -8,7 +8,7 @@ <StackPanel Spacing="10"> <Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto" - ColumnDefinitions="Auto,10,100,5,Auto,20,Auto,10,100,5,Auto" + ColumnDefinitions="Auto,10,150,5,Auto,20,Auto,10,150,5,Auto" > <TextBlock Grid.Row="0" Grid.Column="0" @@ -19,7 +19,7 @@ Increment="0.01" Minimum="0.01" Maximum="0.30" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.LayerHeight}" /> <TextBlock Grid.Row="0" Grid.Column="4" @@ -100,7 +100,7 @@ Increment="0.5" Minimum="0.1" Maximum="200" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.BottomExposure}"/> <TextBlock Grid.Row="6" Grid.Column="4" VerticalAlignment="Center" @@ -114,7 +114,7 @@ Increment="0.5" Minimum="0.1" Maximum="200" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.NormalExposure}"/> <TextBlock Grid.Row="6" Grid.Column="10" VerticalAlignment="Center" @@ -174,8 +174,7 @@ <Grid Margin="0,10,0,0" RowDefinitions="Auto,0,Auto,10,Auto,10,Auto,10,Auto,10,Auto" - ColumnDefinitions="Auto,10,80,5,Auto,5,80,5,Auto,20,Auto,10,80" - > + ColumnDefinitions="Auto,10,Auto,5,Auto,5,Auto,5,Auto,20,Auto,10,Auto"> <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" @@ -235,7 +234,7 @@ IsEnabled="{Binding Operation.EnableCenterHoleRelief}" Value="{Binding Operation.CenterHoleDiameter}"/> - <CheckBox Grid.Row="4" Grid.Column="6" Grid.ColumnSpan="4" + <CheckBox Grid.Row="4" Grid.Column="6" Grid.ColumnSpan="7" VerticalAlignment="Center" Content="Enable center hole relief" IsChecked="{Binding Operation.EnableCenterHoleRelief}"/> diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateGrayscaleControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateGrayscaleControl.axaml.cs index b60ce83..ebf1a31 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateGrayscaleControl.axaml.cs +++ b/UVtools.WPF/Controls/Calibrators/CalibrateGrayscaleControl.axaml.cs @@ -24,17 +24,9 @@ namespace UVtools.WPF.Controls.Calibrators public CalibrateGrayscaleControl() { - this.InitializeComponent(); + InitializeComponent(); - BaseOperation = new OperationCalibrateGrayscale(); - - if (App.SlicerFile is not null) - { - Operation.LayerHeight = (decimal)App.SlicerFile.LayerHeight; - Operation.BottomLayers = App.SlicerFile.BottomLayerCount; - Operation.BottomExposure = (decimal)App.SlicerFile.BottomExposureTime; - Operation.NormalExposure = (decimal)App.SlicerFile.ExposureTime; - } + BaseOperation = new OperationCalibrateGrayscale(SlicerFile); _timer = new Timer(20) { @@ -55,7 +47,6 @@ namespace UVtools.WPF.Controls.Calibrators { case ToolWindow.Callbacks.Init: case ToolWindow.Callbacks.ProfileLoaded: - Operation.Resolution = App.SlicerFile.Resolution; Operation.PropertyChanged += (sender, e) => { _timer.Stop(); diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateStressTowerControl.axaml b/UVtools.WPF/Controls/Calibrators/CalibrateStressTowerControl.axaml index d15b483..4ea86a3 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateStressTowerControl.axaml +++ b/UVtools.WPF/Controls/Calibrators/CalibrateStressTowerControl.axaml @@ -17,7 +17,7 @@ <Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto" - ColumnDefinitions="Auto,10,100,5,Auto,20,Auto,10,100,5,Auto"> + ColumnDefinitions="Auto,10,170,5,Auto,20,Auto,10,170,5,Auto"> <TextBlock Grid.Row="0" Grid.Column="0" @@ -29,7 +29,7 @@ Increment="0.1" Minimum="0" Maximum="10000" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.DisplayWidth}"/> <TextBlock Grid.Row="0" Grid.Column="4" VerticalAlignment="Center" @@ -44,7 +44,7 @@ Increment="0.1" Minimum="0" Maximum="10000" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.DisplayHeight}"/> <TextBlock Grid.Row="0" Grid.Column="10" VerticalAlignment="Center" @@ -58,7 +58,7 @@ Increment="0.01" Minimum="0.01" Maximum="0.30" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.LayerHeight}" /> <TextBlock Grid.Row="2" Grid.Column="4" @@ -86,7 +86,7 @@ Increment="0.5" Minimum="0.1" Maximum="200" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.BottomExposure}"/> <TextBlock Grid.Row="4" Grid.Column="4" VerticalAlignment="Center" @@ -100,7 +100,7 @@ Increment="0.5" Minimum="0.1" Maximum="200" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.NormalExposure}"/> <TextBlock Grid.Row="4" Grid.Column="10" VerticalAlignment="Center" @@ -114,7 +114,7 @@ Increment="1" Minimum="0" Maximum="100" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.BaseHeight}"/> <TextBlock Grid.Row="6" Grid.Column="4" VerticalAlignment="Center" @@ -128,7 +128,7 @@ Increment="1" Minimum="1" Maximum="10000" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.BaseDiameter}"/> <TextBlock Grid.Row="6" Grid.Column="10" VerticalAlignment="Center" @@ -142,7 +142,7 @@ Increment="1" Minimum="0" Maximum="100" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.CeilHeight}"/> <TextBlock Grid.Row="8" Grid.Column="4" VerticalAlignment="Center" @@ -157,7 +157,7 @@ Increment="1" Minimum="0" Maximum="10000" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.BodyHeight}"/> <TextBlock Grid.Row="8" Grid.Column="10" VerticalAlignment="Center" @@ -227,7 +227,7 @@ </Expander.Header> <Grid RowDefinitions="Auto,5,Auto" - ColumnDefinitions="Auto,10,100,5,Auto,20,Auto,10,100,5,Auto"> + ColumnDefinitions="Auto,10,170,5,Auto,20,Auto,10,170,5,Auto"> <TextBlock Grid.Row="0" Grid.Column="0" @@ -248,7 +248,8 @@ Text="Spiral direction:"/> <ComboBox Grid.Row="0" Grid.Column="8" Items="{Binding Operation.SpiralDirectionsItems}" - SelectedItem="{Binding Operation.SpiralDirection}"/> + SelectedItem="{Binding Operation.SpiralDirection}" + HorizontalAlignment="Stretch"/> <TextBlock Grid.Row="2" Grid.Column="0" Text="Spiral diameter:" @@ -258,7 +259,7 @@ Increment="1" Minimum="0.1" Maximum="10000" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.SpiralDiameter}"/> <TextBlock Grid.Row="2" Grid.Column="4" Text="mm" @@ -273,7 +274,7 @@ Increment="1" Minimum="0.01" Maximum="359.99" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.SpiralAngleStepPerLayer}"/> <TextBlock Grid.Row="2" Grid.Column="10" Text="º/layer" diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateStressTowerControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateStressTowerControl.axaml.cs index d06a3b0..4825095 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateStressTowerControl.axaml.cs +++ b/UVtools.WPF/Controls/Calibrators/CalibrateStressTowerControl.axaml.cs @@ -17,15 +17,7 @@ namespace UVtools.WPF.Controls.Calibrators public CalibrateStressTowerControl() { InitializeComponent(); - BaseOperation = new OperationCalibrateStressTower(); - - if (App.SlicerFile is not null) - { - Operation.LayerHeight = (decimal)App.SlicerFile.LayerHeight; - Operation.BottomLayers = App.SlicerFile.BottomLayerCount; - Operation.BottomExposure = (decimal)App.SlicerFile.BottomExposureTime; - Operation.NormalExposure = (decimal)App.SlicerFile.ExposureTime; - } + BaseOperation = new OperationCalibrateStressTower(SlicerFile); } private void InitializeComponent() @@ -33,20 +25,15 @@ namespace UVtools.WPF.Controls.Calibrators AvaloniaXamlLoader.Load(this); } - public override void Callback(ToolWindow.Callbacks callback) + /*public override void Callback(ToolWindow.Callbacks callback) { if (App.SlicerFile is null) return; switch (callback) { case ToolWindow.Callbacks.Init: case ToolWindow.Callbacks.ProfileLoaded: - Operation.Resolution = App.SlicerFile.Resolution; - if (App.SlicerFile.DisplayWidth > 0) - Operation.DisplayWidth = (decimal)App.SlicerFile.DisplayWidth; - if (App.SlicerFile.DisplayHeight > 0) - Operation.DisplayHeight = (decimal)App.SlicerFile.DisplayHeight; break; } - } + }*/ } } diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateToleranceControl.axaml b/UVtools.WPF/Controls/Calibrators/CalibrateToleranceControl.axaml index fed2a9e..126bb72 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateToleranceControl.axaml +++ b/UVtools.WPF/Controls/Calibrators/CalibrateToleranceControl.axaml @@ -17,8 +17,7 @@ <Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto" - ColumnDefinitions="Auto,10,100,5,Auto,20,Auto,10,100,5,Auto" - > + ColumnDefinitions="Auto,10,170,5,Auto,20,Auto,10,170,5,Auto"> <TextBlock Grid.Row="0" Grid.Column="0" @@ -30,7 +29,7 @@ Increment="0.1" Minimum="0" Maximum="10000" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.DisplayWidth}"/> <TextBlock Grid.Row="0" Grid.Column="4" VerticalAlignment="Center" @@ -45,7 +44,7 @@ Increment="0.1" Minimum="0" Maximum="10000" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.DisplayHeight}"/> <TextBlock Grid.Row="0" Grid.Column="10" VerticalAlignment="Center" @@ -59,7 +58,7 @@ Increment="0.01" Minimum="0.01" Maximum="0.30" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.LayerHeight}" /> <TextBlock Grid.Row="2" Grid.Column="4" @@ -87,7 +86,7 @@ Increment="0.5" Minimum="0.1" Maximum="200" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.BottomExposure}"/> <TextBlock Grid.Row="4" Grid.Column="4" VerticalAlignment="Center" @@ -101,7 +100,7 @@ Increment="0.5" Minimum="0.1" Maximum="200" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.NormalExposure}"/> <TextBlock Grid.Row="4" Grid.Column="10" VerticalAlignment="Center" @@ -115,7 +114,7 @@ Increment="1" Minimum="5" Maximum="100" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.ZSize}"/> <TextBlock Grid.Row="6" Grid.Column="4" VerticalAlignment="Center" @@ -254,7 +253,7 @@ </Expander.Header> <Grid RowDefinitions="Auto,5,Auto" - ColumnDefinitions="Auto,10,100,5,Auto,20,Auto,10,100,5,Auto"> + ColumnDefinitions="Auto,10,170,5,Auto,20,Auto,10,170,5,Auto"> <TextBlock Grid.Row="0" Grid.Column="0" @@ -265,7 +264,7 @@ Increment="1.0" Minimum="2" Maximum="1000" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.FemaleDiameter}"/> <TextBlock Grid.Row="0" Grid.Column="4" VerticalAlignment="Center" @@ -279,7 +278,7 @@ Increment="1.0" Minimum="2" Maximum="1000" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.FemaleHoleDiameter}"/> <TextBlock Grid.Row="0" Grid.Column="10" VerticalAlignment="Center" @@ -316,7 +315,7 @@ </Expander.Header> <Grid RowDefinitions="Auto,10,Auto" - ColumnDefinitions="Auto,10,80,5,Auto,20,Auto,10,80,5,Auto,20,Auto,10,80,5,Auto"> + ColumnDefinitions="Auto,10,Auto,5,Auto,20,Auto,10,150,5,Auto,20,Auto,10,150,5,Auto"> <TextBlock Grid.Row="0" Grid.Column="0" @@ -336,7 +335,7 @@ Increment="0.01" Minimum="-1000" Maximum="0" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.MaleThinnerOffset}"/> <TextBlock Grid.Row="0" Grid.Column="10" VerticalAlignment="Center" @@ -350,7 +349,7 @@ Increment="0.01" Minimum="-1000" Maximum="-0.01" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.MaleThinnerStep}"/> <TextBlock Grid.Row="0" Grid.Column="16" VerticalAlignment="Center" @@ -377,7 +376,7 @@ Increment="0.01" Minimum="0" Maximum="1000" - FormatString="\{0:0.00\}" + FormatString="F02" IsEnabled="{Binding !Operation.FuseParts}" Value="{Binding Operation.MaleThickerOffset}"/> <TextBlock Grid.Row="2" Grid.Column="10" @@ -395,7 +394,7 @@ Minimum="0.01" Maximum="1000" IsEnabled="{Binding !Operation.FuseParts}" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.MaleThickerStep}"/> <TextBlock Grid.Row="2" Grid.Column="16" VerticalAlignment="Center" @@ -429,7 +428,7 @@ Minimum="0" Maximum="100" Width="100" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.ObservedXSize}"/> <TextBlock VerticalAlignment="Center" Text="mm"/> @@ -441,7 +440,7 @@ Minimum="0" Maximum="100" Width="100" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.ObservedYSize}"/> <TextBlock VerticalAlignment="Center" Text="mm"/> @@ -453,7 +452,7 @@ Minimum="0" Maximum="100" Width="100" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.ObservedZSize}"/> <TextBlock VerticalAlignment="Center" Text="mm"/> </StackPanel> diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateToleranceControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateToleranceControl.axaml.cs index 7191bfb..19e4103 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateToleranceControl.axaml.cs +++ b/UVtools.WPF/Controls/Calibrators/CalibrateToleranceControl.axaml.cs @@ -39,15 +39,7 @@ namespace UVtools.WPF.Controls.Calibrators public CalibrateToleranceControl() { InitializeComponent(); - BaseOperation = new OperationCalibrateTolerance(); - - if (App.SlicerFile is not null) - { - Operation.LayerHeight = (decimal)App.SlicerFile.LayerHeight; - Operation.BottomLayers = App.SlicerFile.BottomLayerCount; - Operation.BottomExposure = (decimal)App.SlicerFile.BottomExposureTime; - Operation.NormalExposure = (decimal)App.SlicerFile.ExposureTime; - } + BaseOperation = new OperationCalibrateTolerance(SlicerFile); _timer = new Timer(100) { @@ -68,11 +60,6 @@ namespace UVtools.WPF.Controls.Calibrators { case ToolWindow.Callbacks.Init: case ToolWindow.Callbacks.ProfileLoaded: - Operation.Resolution = App.SlicerFile.Resolution; - if (App.SlicerFile.DisplayWidth > 0) - Operation.DisplayWidth = (decimal)App.SlicerFile.DisplayWidth; - if (App.SlicerFile.DisplayHeight > 0) - Operation.DisplayHeight = (decimal)App.SlicerFile.DisplayHeight; Operation.PropertyChanged += (sender, e) => { _timer.Stop(); diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml b/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml index 8070a8d..d7075f8 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml +++ b/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml @@ -17,7 +17,7 @@ <Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto" - ColumnDefinitions="Auto,10,100,5,Auto,20,Auto,10,100,5,Auto" + ColumnDefinitions="Auto,10,150,5,Auto,20,Auto,10,150,5,Auto" > <TextBlock @@ -30,7 +30,7 @@ Increment="0.1" Minimum="0" Maximum="10000" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.DisplayWidth}"/> <TextBlock Grid.Row="0" Grid.Column="4" VerticalAlignment="Center" @@ -45,7 +45,7 @@ Increment="0.1" Minimum="0" Maximum="10000" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.DisplayHeight}"/> <TextBlock Grid.Row="0" Grid.Column="10" VerticalAlignment="Center" @@ -59,7 +59,7 @@ Increment="0.01" Minimum="0.01" Maximum="0.30" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.LayerHeight}" /> <TextBlock Grid.Row="2" Grid.Column="4" @@ -87,7 +87,7 @@ Increment="0.5" Minimum="0.1" Maximum="200" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.BottomExposure}"/> <TextBlock Grid.Row="4" Grid.Column="4" VerticalAlignment="Center" @@ -101,7 +101,7 @@ Increment="0.5" Minimum="0.1" Maximum="200" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.NormalExposure}"/> <TextBlock Grid.Row="4" Grid.Column="10" VerticalAlignment="Center" @@ -115,7 +115,7 @@ Increment="1" Minimum="5" Maximum="100" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.XSize}"/> <TextBlock Grid.Row="6" Grid.Column="4" VerticalAlignment="Center" @@ -129,7 +129,7 @@ Increment="1" Minimum="5" Maximum="100" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.YSize}"/> <TextBlock Grid.Row="6" Grid.Column="10" VerticalAlignment="Center" @@ -143,7 +143,7 @@ Increment="1" Minimum="5" Maximum="100" - FormatString="\{0:0.00\}" + FormatString="F02" Value="{Binding Operation.ZSize}"/> <TextBlock Grid.Row="8" Grid.Column="4" VerticalAlignment="Center" @@ -213,7 +213,7 @@ Increment="0.5" Minimum="0" Maximum="100" - FormatString="\{0:0.00\}" + FormatString="F02" IsEnabled="{Binding Operation.HollowModel}" Value="{Binding Operation.WallThickness}"/> <TextBlock Grid.Row="12" Grid.Column="10" @@ -275,7 +275,6 @@ ToolTip.Tip="Middle Center" VerticalAlignment="Center" HorizontalAlignment="Center" - Margin="5" IsChecked="{Binding Operation.OutputMCObject}" /> <CheckBox @@ -316,9 +315,13 @@ Spacing="5"> <Button Command="{Binding Operation.SelectNoneObjects}" + HorizontalAlignment="Stretch" + HorizontalContentAlignment="Center" Content="Select none"/> <Button Command="{Binding Operation.SelectAllObjects}" + HorizontalAlignment="Stretch" + HorizontalContentAlignment="Center" Content="Select all"/> </StackPanel> @@ -327,9 +330,13 @@ Spacing="5"> <Button Command="{Binding Operation.SelectCrossedObjects}" + HorizontalAlignment="Stretch" + HorizontalContentAlignment="Center" Content="Select crossed"/> <Button Command="{Binding Operation.SelectCenterObject}" + HorizontalAlignment="Stretch" + HorizontalContentAlignment="Center" Content="Select center"/> </StackPanel> @@ -354,8 +361,8 @@ Increment="1" Minimum="0" Maximum="100" - Width="100" - FormatString="\{0:0.00\}" + Width="150" + FormatString="F02" Value="{Binding Operation.ObservedXSize}"/> <TextBlock VerticalAlignment="Center" Text="mm"/> @@ -366,8 +373,8 @@ Increment="1" Minimum="0" Maximum="100" - Width="100" - FormatString="\{0:0.00\}" + Width="150" + FormatString="F02" Value="{Binding Operation.ObservedYSize}"/> <TextBlock VerticalAlignment="Center" Text="mm"/> @@ -378,8 +385,8 @@ Increment="1" Minimum="0" Maximum="100" - Width="100" - FormatString="\{0:0.00\}" + Width="150" + FormatString="F02" Value="{Binding Operation.ObservedZSize}"/> <TextBlock VerticalAlignment="Center" Text="mm"/> </StackPanel> diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml.cs index c9deea5..edbfd18 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml.cs +++ b/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml.cs @@ -40,15 +40,7 @@ namespace UVtools.WPF.Controls.Calibrators public CalibrateXYZAccuracyControl() { InitializeComponent(); - BaseOperation = new OperationCalibrateXYZAccuracy(); - - if (App.SlicerFile is not null) - { - Operation.LayerHeight = (decimal)App.SlicerFile.LayerHeight; - Operation.BottomLayers = App.SlicerFile.BottomLayerCount; - Operation.BottomExposure = (decimal)App.SlicerFile.BottomExposureTime; - Operation.NormalExposure = (decimal)App.SlicerFile.ExposureTime; - } + BaseOperation = new OperationCalibrateXYZAccuracy(SlicerFile); _timer = new Timer(20) { @@ -69,11 +61,6 @@ namespace UVtools.WPF.Controls.Calibrators { case ToolWindow.Callbacks.Init: case ToolWindow.Callbacks.ProfileLoaded: - Operation.Resolution = App.SlicerFile.Resolution; - if(App.SlicerFile.DisplayWidth > 0) - Operation.DisplayWidth = (decimal)App.SlicerFile.DisplayWidth; - if (App.SlicerFile.DisplayHeight > 0) - Operation.DisplayHeight = (decimal)App.SlicerFile.DisplayHeight; Operation.PropertyChanged += (sender, e) => { _timer.Stop(); diff --git a/UVtools.WPF/Controls/KernelControl.axaml b/UVtools.WPF/Controls/KernelControl.axaml index 53d39a6..5bc2a06 100644 --- a/UVtools.WPF/Controls/KernelControl.axaml +++ b/UVtools.WPF/Controls/KernelControl.axaml @@ -27,11 +27,12 @@ HorizontalAlignment="Right" VerticalAlignment="Center" Text="Shape:"/> - <ComboBox - Grid.ColumnSpan="5" + <ComboBox + Grid.Row="0" Grid.ColumnSpan="5" Grid.Column="2" SelectedItem="{Binding SelectedKernelShape}" Items="{Binding KernelShapes}" + Width="160" /> <TextBlock @@ -45,7 +46,6 @@ Grid.Row="2" VerticalAlignment="Center" Minimum="2" - MinWidth="60" Value="{Binding MatrixWidth}" /> <TextBlock @@ -59,7 +59,6 @@ Grid.Row="2" VerticalAlignment="Center" Minimum="2" - MinWidth="60" Value="{Binding MatrixHeight}" /> @@ -75,7 +74,6 @@ Grid.Row="4" VerticalAlignment="Center" Minimum="-1" - MinWidth="60" ToolTip.Tip="X coordinate of the kenel origin, -1 for auto-center." Value="{Binding AnchorX}" /> @@ -90,7 +88,6 @@ Grid.Row="4" VerticalAlignment="Center" Minimum="-1" - MinWidth="60" ToolTip.Tip="Y coordinate of the kernel origin, -1 for auto-center." Value="{Binding AnchorY}" /> @@ -108,6 +105,7 @@ Grid.Column="4" Grid.Row="6" Grid.ColumnSpan="3" + Padding="10" Content="Reset" Command="{Binding ResetKernel}" diff --git a/UVtools.WPF/Controls/SliderEx.cs b/UVtools.WPF/Controls/SliderEx.cs index 4ceb2d8..6752206 100644 --- a/UVtools.WPF/Controls/SliderEx.cs +++ b/UVtools.WPF/Controls/SliderEx.cs @@ -9,8 +9,10 @@ using System; using Avalonia; using Avalonia.Collections; using Avalonia.Controls; +using Avalonia.Controls.Metadata; using Avalonia.Controls.Mixins; using Avalonia.Controls.Primitives; +using Avalonia.Data; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Layout; @@ -19,6 +21,7 @@ using Avalonia.Utilities; namespace UVtools.WPF.Controls { + [PseudoClasses(":vertical", ":horizontal", ":pressed")] public class SliderEx : RangeBase, IStyleable { Type IStyleable.StyleKey => typeof(Slider); @@ -27,31 +30,31 @@ namespace UVtools.WPF.Controls /// Defines the <see cref="Orientation"/> property. /// </summary> public static readonly StyledProperty<Orientation> OrientationProperty = - ScrollBar.OrientationProperty.AddOwner<Slider>(); + ScrollBar.OrientationProperty.AddOwner<SliderEx>(); /// <summary> /// Defines the <see cref="IsSnapToTickEnabled"/> property. /// </summary> public static readonly StyledProperty<bool> IsSnapToTickEnabledProperty = - AvaloniaProperty.Register<Slider, bool>(nameof(IsSnapToTickEnabled), false); + AvaloniaProperty.Register<SliderEx, bool>(nameof(IsSnapToTickEnabled), false); /// <summary> /// Defines the <see cref="TickFrequency"/> property. /// </summary> public static readonly StyledProperty<double> TickFrequencyProperty = - AvaloniaProperty.Register<Slider, double>(nameof(TickFrequency), 0.0); + AvaloniaProperty.Register<SliderEx, double>(nameof(TickFrequency), 0.0); /// <summary> /// Defines the <see cref="TickPlacement"/> property. /// </summary> public static readonly StyledProperty<TickPlacement> TickPlacementProperty = - AvaloniaProperty.Register<TickBar, TickPlacement>(nameof(TickPlacement), 0d); + AvaloniaProperty.Register<SliderEx, TickPlacement>(nameof(TickPlacement), 0d); /// <summary> /// Defines the <see cref="TicksProperty"/> property. /// </summary> public static readonly StyledProperty<AvaloniaList<double>> TicksProperty = - TickBar.TicksProperty.AddOwner<Slider>(); + TickBar.TicksProperty.AddOwner<SliderEx>(); // Slider required parts private bool _isDragging = false; @@ -71,11 +74,13 @@ namespace UVtools.WPF.Controls /// </summary> static SliderEx() { - PressedMixin.Attach<Slider>(); - OrientationProperty.OverrideDefaultValue(typeof(Slider), Orientation.Horizontal); + PressedMixin.Attach<SliderEx>(); + OrientationProperty.OverrideDefaultValue(typeof(SliderEx), Orientation.Horizontal); Thumb.DragStartedEvent.AddClassHandler<SliderEx>((x, e) => x.OnThumbDragStarted(e), RoutingStrategies.Bubble); Thumb.DragCompletedEvent.AddClassHandler<SliderEx>((x, e) => x.OnThumbDragCompleted(e), RoutingStrategies.Bubble); + + ValueProperty.OverrideMetadata<SliderEx>(new DirectPropertyMetadata<double>(enableDataValidation: true)); } /// <summary> @@ -100,8 +105,8 @@ namespace UVtools.WPF.Controls /// </summary> public Orientation Orientation { - get => GetValue(OrientationProperty); - set => SetValue(OrientationProperty, value); + get { return GetValue(OrientationProperty); } + set { SetValue(OrientationProperty, value); } } /// <summary> @@ -109,8 +114,8 @@ namespace UVtools.WPF.Controls /// </summary> public bool IsSnapToTickEnabled { - get => GetValue(IsSnapToTickEnabledProperty); - set => SetValue(IsSnapToTickEnabledProperty, value); + get { return GetValue(IsSnapToTickEnabledProperty); } + set { SetValue(IsSnapToTickEnabledProperty, value); } } /// <summary> @@ -118,8 +123,8 @@ namespace UVtools.WPF.Controls /// </summary> public double TickFrequency { - get => GetValue(TickFrequencyProperty); - set => SetValue(TickFrequencyProperty, value); + get { return GetValue(TickFrequencyProperty); } + set { SetValue(TickFrequencyProperty, value); } } /// <summary> @@ -128,8 +133,8 @@ namespace UVtools.WPF.Controls /// </summary> public TickPlacement TickPlacement { - get => GetValue(TickPlacementProperty); - set => SetValue(TickPlacementProperty, value); + get { return GetValue(TickPlacementProperty); } + set { SetValue(TickPlacementProperty, value); } } /// <inheritdoc/> @@ -182,9 +187,11 @@ namespace UVtools.WPF.Controls private void TrackPressed(object sender, PointerPressedEventArgs e) { - if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) return; - MoveToPoint(e.GetCurrentPoint(_track)); - _isDragging = true; + if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + { + MoveToPoint(e.GetCurrentPoint(_track)); + _isDragging = true; + } } private void MoveToPoint(PointerPoint x) @@ -205,6 +212,14 @@ namespace UVtools.WPF.Controls Value = IsSnapToTickEnabled ? SnapToTick(finalValue) : finalValue; } + protected override void UpdateDataValidation<T>(AvaloniaProperty<T> property, BindingValue<T> value) + { + if (property == ValueProperty) + { + DataValidationErrors.SetError(this, value.Error); + } + } + protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) { base.OnPropertyChanged(change); @@ -233,49 +248,51 @@ namespace UVtools.WPF.Controls _isDragging = false; } - /// <summary> /// Snap the input 'value' to the closest tick. /// </summary> /// <param name="value">Value that want to snap to closest Tick.</param> private double SnapToTick(double value) { - if (!IsSnapToTickEnabled) return value; - double previous = Minimum; - double next = Maximum; + if (IsSnapToTickEnabled) + { + double previous = Minimum; + double next = Maximum; - // This property is rarely set so let's try to avoid the GetValue - var ticks = Ticks; + // This property is rarely set so let's try to avoid the GetValue + var ticks = Ticks; - // If ticks collection is available, use it. - // Note that ticks may be unsorted. - if ((ticks != null) && (ticks.Count > 0)) - { - foreach (var tick in ticks) + // If ticks collection is available, use it. + // Note that ticks may be unsorted. + if ((ticks != null) && (ticks.Count > 0)) { - if (MathUtilities.AreClose(tick, value)) - { - return value; - } - - if (MathUtilities.LessThan(tick, value) && MathUtilities.GreaterThan(tick, previous)) - { - previous = tick; - } - else if (MathUtilities.GreaterThan(tick, value) && MathUtilities.LessThan(tick, next)) + for (int i = 0; i < ticks.Count; i++) { - next = tick; + double tick = ticks[i]; + if (MathUtilities.AreClose(tick, value)) + { + return value; + } + + if (MathUtilities.LessThan(tick, value) && MathUtilities.GreaterThan(tick, previous)) + { + previous = tick; + } + else if (MathUtilities.GreaterThan(tick, value) && MathUtilities.LessThan(tick, next)) + { + next = tick; + } } } - } - else if (MathUtilities.GreaterThan(TickFrequency, 0.0)) - { - previous = Minimum + (Math.Round(((value - Minimum) / TickFrequency)) * TickFrequency); - next = Math.Min(Maximum, previous + TickFrequency); - } + else if (MathUtilities.GreaterThan(TickFrequency, 0.0)) + { + previous = Minimum + (Math.Round(((value - Minimum) / TickFrequency)) * TickFrequency); + next = Math.Min(Maximum, previous + TickFrequency); + } - // Choose the closest value between previous and next. If tie, snap to 'next'. - value = MathUtilities.GreaterThanOrClose(value, (previous + next) * 0.5) ? next : previous; + // Choose the closest value between previous and next. If tie, snap to 'next'. + value = MathUtilities.GreaterThanOrClose(value, (previous + next) * 0.5) ? next : previous; + } return value; } diff --git a/UVtools.WPF/Controls/Tools/ToolArithmeticControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolArithmeticControl.axaml.cs index 623633e..18ad8ff 100644 --- a/UVtools.WPF/Controls/Tools/ToolArithmeticControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolArithmeticControl.axaml.cs @@ -11,14 +11,7 @@ namespace UVtools.WPF.Controls.Tools public ToolArithmeticControl() { InitializeComponent(); - BaseOperation = new OperationArithmetic(); - Operation.PropertyChanged += (sender, e) => - { - if (e.PropertyName == nameof(Operation.Sentence)) - { - ParentWindow.ButtonOkEnabled = !string.IsNullOrWhiteSpace(Operation.Sentence); - } - }; + BaseOperation = new OperationArithmetic(SlicerFile); } private void InitializeComponent() @@ -31,7 +24,15 @@ namespace UVtools.WPF.Controls.Tools switch (callback) { case ToolWindow.Callbacks.Init: - ParentWindow.ButtonOkEnabled = false; + case ToolWindow.Callbacks.ProfileLoaded: + ParentWindow.ButtonOkEnabled = !string.IsNullOrWhiteSpace(Operation.Sentence); + Operation.PropertyChanged += (sender, e) => + { + if (e.PropertyName == nameof(Operation.Sentence)) + { + ParentWindow.ButtonOkEnabled = !string.IsNullOrWhiteSpace(Operation.Sentence); + } + }; break; } } diff --git a/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml b/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml index 36bbe84..e37b992 100644 --- a/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml @@ -19,6 +19,7 @@ HorizontalAlignment="Left" SelectedIndex="{Binding SelectedAlgorithmIndex}" Width="450" + HorizontalContentAlignment="Stretch" Items="{Binding Operation.BlurTypes}" /> @@ -31,7 +32,7 @@ Grid.Row="2" Grid.Column="2" HorizontalAlignment="Left" - Width="80" + Width="150" Minimum="1" IsEnabled="{Binding IsSizeEnabled}" Value="{Binding Operation.Size}" diff --git a/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs index c9ea8c3..89f6322 100644 --- a/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs @@ -41,7 +41,7 @@ namespace UVtools.WPF.Controls.Tools public ToolBlurControl() { InitializeComponent(); - BaseOperation = new OperationBlur(); + BaseOperation = new OperationBlur(SlicerFile); _kernelCtrl = this.Find<KernelControl>("KernelCtrl"); } diff --git a/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml b/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml index 8e3c9aa..d71fd0f 100644 --- a/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml @@ -3,31 +3,31 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - MaxWidth="720" + MaxWidth="800" x:Class="UVtools.WPF.Controls.Tools.ToolCalculatorControl"> <TabControl> <TabItem Header="Millimeters to pixels"> <Grid RowDefinitions="Auto,10,Auto,30,Auto"> <Border Background="White" - BorderBrush="Black" BorderThickness="1" Padding="10"> - <TextBlock VerticalAlignment="Center" - TextWrapping="Wrap"> - <TextBlock.Text> + BorderBrush="Black" BorderThickness="1"> + <TextBox VerticalAlignment="Center" + Classes="TransparentReadOnly" + Padding="10"> + <TextBox.Text> <MultiBinding StringFormat="\{0\}

Formula: \{1\}"> <Binding Path="Operation.CalcMillimetersToPixels.Description"/> <Binding Path="Operation.CalcMillimetersToPixels.Formula"/> </MultiBinding> - </TextBlock.Text> - </TextBlock> + </TextBox.Text> + </TextBox> </Border> <Grid Grid.Row="2" RowDefinitions="Auto,10,Auto" - ColumnDefinitions="Auto,10,100,5,Auto,30,Auto,10,100,5,Auto" - > + ColumnDefinitions="Auto,10,150,5,Auto,30,Auto,10,150,5,Auto"> <!-- Resolution --> <TextBlock @@ -85,7 +85,7 @@ Minimum="0" Maximum="100000" Increment="0.01" - FormatString="{}{0:0.00}" + FormatString="F02" Value="{Binding Operation.CalcMillimetersToPixels.DisplayWidth}" /> <TextBlock @@ -107,7 +107,7 @@ Minimum="0" Maximum="100000" Increment="0.01" - FormatString="{}{0:0.00}" + FormatString="F02" Value="{Binding Operation.CalcMillimetersToPixels.DisplayHeight}" /> <TextBlock @@ -121,7 +121,7 @@ <Grid Grid.Row="4" RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto" - ColumnDefinitions="Auto,10,100,5,Auto" + ColumnDefinitions="Auto,10,150,5,Auto" > <TextBlock Grid.Row="0" @@ -136,7 +136,7 @@ Minimum="0" Maximum="100000" Increment="0.5" - FormatString="{}{0:0.00}" + FormatString="F02" Value="{Binding Operation.CalcMillimetersToPixels.Millimeters}" /> <TextBlock @@ -237,23 +237,24 @@ <Grid RowDefinitions="Auto,10,Auto"> <Border Background="White" - BorderBrush="Black" BorderThickness="1" Padding="10"> - <TextBlock VerticalAlignment="Center" - TextWrapping="Wrap"> - <TextBlock.Text> + BorderBrush="Black" BorderThickness="1"> + <TextBox VerticalAlignment="Center" + Classes="TransparentReadOnly" + Padding="10"> + <TextBox.Text> <MultiBinding StringFormat="\{0\}

Formula: \{1\}"> <Binding Path="Operation.CalcLightOffDelay.Description"/> <Binding Path="Operation.CalcLightOffDelay.Formula"/> </MultiBinding> - </TextBlock.Text> - </TextBlock> + </TextBox.Text> + </TextBox> </Border> <Grid Grid.Row="2" RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto" - ColumnDefinitions="Auto,10,100,5,Auto,30,Auto,10,100,5,Auto" + ColumnDefinitions="Auto,10,150,5,Auto,30,Auto,10,150,5,Auto" > <!-- Normal --> @@ -270,7 +271,7 @@ Minimum="0" Maximum="1000" Increment="1.0" - FormatString="{}{0:0.00}" + FormatString="F02" Value="{Binding Operation.CalcLightOffDelay.LiftHeight}"/> <TextBlock Grid.Row="0" @@ -291,7 +292,7 @@ Minimum="0" Maximum="1000" Increment="1.0" - FormatString="{}{0:0.00}" + FormatString="F02" Value="{Binding Operation.CalcLightOffDelay.LiftSpeed}"/> <TextBlock Grid.Row="2" @@ -312,7 +313,7 @@ Minimum="0" Maximum="1000" Increment="1.0" - FormatString="{}{0:0.00}" + FormatString="F02" Value="{Binding Operation.CalcLightOffDelay.RetractSpeed}"/> <TextBlock Grid.Row="4" @@ -333,7 +334,7 @@ Minimum="0" Maximum="1000" Increment="0.5" - FormatString="{}{0:0.00}" + FormatString="F02" Value="{Binding Operation.CalcLightOffDelay.WaitTime}"/> <TextBlock Grid.Row="6" @@ -412,7 +413,7 @@ Minimum="0" Maximum="1000" Increment="1.0" - FormatString="{}{0:0.00}" + FormatString="F02" Value="{Binding Operation.CalcLightOffDelay.BottomLiftHeight}"/> <TextBlock Grid.Row="0" @@ -433,7 +434,7 @@ Minimum="0" Maximum="1000" Increment="1.0" - FormatString="{}{0:0.00}" + FormatString="F02" Value="{Binding Operation.CalcLightOffDelay.BottomLiftSpeed}"/> <TextBlock Grid.Row="2" @@ -454,7 +455,7 @@ Minimum="0" Maximum="1000" Increment="0.5" - FormatString="{}{0:0.00}" + FormatString="F02" Value="{Binding Operation.CalcLightOffDelay.BottomWaitTime}"/> <TextBlock Grid.Row="6" @@ -512,22 +513,22 @@ <Grid RowDefinitions="Auto,10,Auto,30,Auto"> <Border Background="White" - BorderBrush="Black" BorderThickness="1" Padding="10"> - <TextBlock VerticalAlignment="Center" TextWrapping="Wrap"> - <TextBlock.Text> + BorderBrush="Black" BorderThickness="1"> + <TextBox VerticalAlignment="Center" Classes="TransparentReadOnly" Padding="10"> + <TextBox.Text> <MultiBinding StringFormat="\{0\}

Formula: \{1\}"> <Binding Path="Operation.CalcOptimalModelTilt.Description"/> <Binding Path="Operation.CalcOptimalModelTilt.Formula"/> </MultiBinding> - </TextBlock.Text> - </TextBlock> + </TextBox.Text> + </TextBox> </Border> <Grid Grid.Row="2" RowDefinitions="Auto,10,Auto" - ColumnDefinitions="Auto,10,100,5,Auto,30,Auto,10,100,5,Auto" + ColumnDefinitions="Auto,10,150,5,Auto,30,Auto,10,150,5,Auto" > <!-- Resolution --> @@ -586,7 +587,7 @@ Minimum="0" Maximum="100000" Increment="0.01" - FormatString="{}{0:0.00}" + FormatString="F02" Value="{Binding Operation.CalcOptimalModelTilt.DisplayWidth}" /> <TextBlock @@ -608,7 +609,7 @@ Minimum="0" Maximum="100000" Increment="0.01" - FormatString="{}{0:0.00}" + FormatString="F02" Value="{Binding Operation.CalcOptimalModelTilt.DisplayHeight}" /> <TextBlock @@ -622,7 +623,7 @@ <Grid Grid.Row="4" RowDefinitions="Auto,10,Auto,10,Auto" - ColumnDefinitions="Auto,10,100,5,Auto" + ColumnDefinitions="Auto,10,150,5,Auto" > <TextBlock Grid.Row="0" @@ -637,7 +638,7 @@ Minimum="0.01" Maximum="0.20" Increment="0.01" - FormatString="{}{0:0.00}" + FormatString="F02" ClipValueToMinMax="True" Value="{Binding Operation.CalcOptimalModelTilt.LayerHeight}" /> diff --git a/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml.cs index 893dcd5..f5fff6f 100644 --- a/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml.cs @@ -21,16 +21,7 @@ namespace UVtools.WPF.Controls.Tools public ToolCalculatorControl() { InitializeComponent(); - BaseOperation = new OperationCalculator - { - CalcMillimetersToPixels = new OperationCalculator.MillimetersToPixels(SlicerFile.Resolution, SlicerFile.Display), - CalcLightOffDelay = new OperationCalculator.LightOffDelayC( - (decimal)SlicerFile.LiftHeight, (decimal)SlicerFile.BottomLiftHeight, - (decimal)SlicerFile.LiftSpeed, (decimal)SlicerFile.BottomLiftSpeed, - (decimal)SlicerFile.RetractSpeed, (decimal)SlicerFile.RetractSpeed), - CalcOptimalModelTilt = new OperationCalculator.OptimalModelTilt(SlicerFile.Resolution, SlicerFile.Display, (decimal) SlicerFile.LayerHeight) - }; - + BaseOperation = new OperationCalculator(SlicerFile); Operation.CalcLightOffDelay.PropertyChanged += (sender, e) => { if (e.PropertyName != nameof(Operation.CalcLightOffDelay.LightOffDelay) && diff --git a/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml b/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml index 744358a..26797c0 100644 --- a/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml @@ -4,8 +4,8 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="UVtools.WPF.Controls.Tools.ToolChangeResolutionControl"> - <StackPanel Orientation="Vertical" Spacing="15"> - <TextBlock Text="{Binding Operation.OldResolution, StringFormat=Current resolution (X/Y): \{0\}}"/> + <StackPanel Orientation="Vertical" Spacing="10"> + <TextBlock Text="{Binding Operation.SlicerFile.Resolution, StringFormat=Current resolution (X/Y): \{0\}}"/> <TextBlock Text="{Binding Operation.VolumeBondsSize, StringFormat=Object volume (X/Y): \{0\}}"/> <StackPanel Orientation="Horizontal" Spacing="10"> @@ -14,8 +14,8 @@ MinWidth="100" Minimum="1" Maximum="50000" - Value="{Binding Operation.NewResolutionX}" - /> + Width="150" + Value="{Binding Operation.NewResolutionX}"/> <TextBlock VerticalAlignment="Center" Text="x"/> @@ -23,6 +23,7 @@ MinWidth="100" Minimum="1" Maximum="50000" + Width="150" Value="{Binding Operation.NewResolutionY}" /> diff --git a/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml.cs index 2762a3c..059a13d 100644 --- a/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml.cs @@ -34,7 +34,7 @@ namespace UVtools.WPF.Controls.Tools public ToolChangeResolutionControl() { InitializeComponent(); - BaseOperation = new OperationChangeResolution(App.SlicerFile.Resolution, App.SlicerFile.LayerManager.BoundingRectangle); + BaseOperation = new OperationChangeResolution(SlicerFile); } private void InitializeComponent() diff --git a/UVtools.WPF/Controls/Tools/ToolControl.axaml b/UVtools.WPF/Controls/Tools/ToolControl.axaml index dafec5a..9fca886 100644 --- a/UVtools.WPF/Controls/Tools/ToolControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolControl.axaml @@ -3,6 +3,5 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="UVtools.WPF.Controls.Tools.ToolControl" - > + x:Class="UVtools.WPF.Controls.Tools.ToolControl"> </UserControl> diff --git a/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs index 8ec6d5b..bf66f21 100644 --- a/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; using Avalonia.Markup.Xaml; +using UVtools.Core.FileFormats; using UVtools.Core.Objects; using UVtools.Core.Operations; using UVtools.WPF.Extensions; @@ -22,6 +23,8 @@ namespace UVtools.WPF.Controls.Tools } } + public FileFormat SlicerFile => App.SlicerFile; + public ToolWindow ParentWindow { get; set; } = null; public bool CanRun { get; set; } = true; diff --git a/UVtools.WPF/Controls/Tools/ToolDynamicLayerHeightControl.axaml b/UVtools.WPF/Controls/Tools/ToolDynamicLayerHeightControl.axaml new file mode 100644 index 0000000..2ff77bf --- /dev/null +++ b/UVtools.WPF/Controls/Tools/ToolDynamicLayerHeightControl.axaml @@ -0,0 +1,199 @@ +<UserControl xmlns="https://github.com/avaloniaui" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" + x:Class="UVtools.WPF.Controls.Tools.ToolDynamicLayerHeightControl"> + + <StackPanel Spacing="10"> + + <Grid RowDefinitions="Auto,0,Auto,10,Auto,10,Auto,10,Auto" + ColumnDefinitions="Auto,10,150,5,Auto,20,Auto,10,150,5,Auto"> + + <!-- + <TextBlock + Grid.Row="0" Grid.Column="0" + VerticalAlignment="Center" + ToolTip.Tip="The printer display width. Required to calculate the XY pixel resolution." + Text="Display width:"/> + <NumericUpDown Grid.Row="0" Grid.Column="2" + ClipValueToMinMax="True" + Increment="0.1" + Minimum="0" + Maximum="10000" + FormatString="F02" + Value="{Binding Operation.DisplayWidth}"/> + <TextBlock Grid.Row="0" Grid.Column="4" + VerticalAlignment="Center" + Text="mm"/> + + <TextBlock Grid.Row="0" Grid.Column="6" + VerticalAlignment="Center" + ToolTip.Tip="The printer display height. Required to calculate the XY pixel resolution." + Text="Display height:"/> + <NumericUpDown Grid.Row="0" Grid.Column="8" + ClipValueToMinMax="True" + Increment="0.1" + Minimum="0" + Maximum="10000" + FormatString="F02" + Value="{Binding Operation.DisplayHeight}"/> + <TextBlock Grid.Row="0" Grid.Column="10" + VerticalAlignment="Center" + Text="mm"/> + !--> + + <TextBlock Grid.Row="2" Grid.Column="0" + VerticalAlignment="Center" + ToolTip.Tip="Never use less than this minimum layer height, layers will always stack at least to this height." + Text="Minimum layer height:"/> + + <NumericUpDown Grid.Row="2" Grid.Column="2" + VerticalAlignment="Center" + HorizontalAlignment="Stretch" + Minimum="{Binding SlicerFile.LayerHeight}" + Maximum="{Binding Operation.MaximumLayerHeight}" + Increment="0.01" + FormatString="F2" + Value="{Binding Operation.MinimumLayerHeight}"/> + <TextBlock Grid.Row="2" Grid.Column="4" + VerticalAlignment="Center" + Text="mm"/> + + <TextBlock Grid.Row="2" Grid.Column="6" + VerticalAlignment="Center" + ToolTip.Tip="Allow to stack layers up to a maximum of this height" + Text="Maximum layer height:"/> + + <NumericUpDown Grid.Row="2" Grid.Column="8" + Name="MaximumLayerHeight" + VerticalAlignment="Center" + HorizontalAlignment="Stretch" + Minimum="{Binding MinimumLayerHeight}" + Maximum="{Binding MaximumLayerHeight}" + Increment="0.01" + FormatString="F2" + Value="{Binding Operation.MaximumLayerHeight}"/> + <TextBlock Grid.Row="2" Grid.Column="10" + VerticalAlignment="Center" + Text="mm"/> + + <TextBlock Grid.Row="4" Grid.Column="0" + VerticalAlignment="Center" + ToolTip.Tip="This operation needs to run sequentially, to boost speed up, a group of layers will be cached (decoded and transformed in parallel to latter use). +
The more layers you can cache the faster it will run but more RAM is used. +
As a rule of thumb, never use less layers than CPU core count x 2 and allow some free margin or SWAP will be used and slow down the operation. +
When allocate more layers than the required, that memory will not be used and kept free." + Text="Cache RAM:"/> + + <NumericUpDown Grid.Row="4" Grid.Column="2" + VerticalAlignment="Center" + HorizontalAlignment="Stretch" + Minimum="0.5" + Maximum="128" + Increment="0.5" + FormatString="F2" + Value="{Binding Operation.CacheRAMSize}"/> + + <TextBlock Grid.Row="4" Grid.Column="4" + VerticalAlignment="Center" + Text="GB"/> + + <TextBlock Grid.Row="4" Grid.Column="6" + HorizontalAlignment="Right" + VerticalAlignment="Center" + Text="Cache layers:"/> + + <TextBlock Grid.Row="4" Grid.Column="8" + VerticalAlignment="Center" + Text="{Binding Operation.CacheObjectCount, StringFormat=±\{0\}}"/> + + + <TextBlock Grid.Row="6" Grid.Column="0" + VerticalAlignment="Center" + ToolTip.Tip="Linear: Current exposure + number of stacked layers * step.
 +Multiplier: Current exposure * layer height * number of stacked layers * step.
 +Manual: User defined exposure per layer height" + Text="Exposure set type:"/> + + <ComboBox Grid.Row="6" Grid.Column="2" + VerticalAlignment="Center" + HorizontalAlignment="Stretch" + Items="{Binding Operation.ExposureSetTypeItems}" + SelectedItem="{Binding Operation.ExposureSetType}"/> + + <TextBlock Grid.Row="8" Grid.Column="0" + IsVisible="{Binding !Operation.IsExposureSetTypeManual}" + VerticalAlignment="Center" + ToolTip.Tip="Bottom exposure increment per layer height" + Text="Bottom exposure step:"/> + + <NumericUpDown Grid.Row="8" Grid.Column="2" + IsVisible="{Binding !Operation.IsExposureSetTypeManual}" + VerticalAlignment="Center" + HorizontalAlignment="Stretch" + Minimum="0.01" + Maximum="100" + Increment="0.01" + FormatString="F2" + Value="{Binding Operation.BottomExposureStep}"/> + <TextBlock Grid.Row="8" Grid.Column="4" + IsVisible="{Binding !Operation.IsExposureSetTypeManual}" + VerticalAlignment="Center" + Text="s"/> + + <TextBlock Grid.Row="8" Grid.Column="6" + IsVisible="{Binding !Operation.IsExposureSetTypeManual}" + HorizontalAlignment="Right" + VerticalAlignment="Center" + ToolTip.Tip="Exposure increment per layer height" + Text="Exposure step:"/> + + <NumericUpDown Grid.Row="8" Grid.Column="8" + IsVisible="{Binding !Operation.IsExposureSetTypeManual}" + VerticalAlignment="Center" + HorizontalAlignment="Stretch" + Minimum="0.01" + Maximum="100" + Increment="0.01" + FormatString="F2" + Value="{Binding Operation.ExposureStep}"/> + + <TextBlock Grid.Row="8" Grid.Column="10" + IsVisible="{Binding !Operation.IsExposureSetTypeManual}" + VerticalAlignment="Center" + Text="s"/> + + </Grid> + + <TextBlock Text="Exposure Table:" + HorizontalAlignment="Center" + FontWeight="Bold" /> + + <DataGrid + Name="ExposureTable" + CanUserReorderColumns="True" + CanUserResizeColumns="True" + CanUserSortColumns="True" + GridLinesVisibility="Horizontal" + IsReadOnly="False" + ClipboardCopyMode="IncludeHeader" + MinHeight="200" + Items="{Binding Operation.ExposureTable}"> + <DataGrid.Columns> + <DataGridTextColumn Header="Layer height (mm)" + Binding="{Binding LayerHeight}" + IsReadOnly="True" + Width="Auto" /> + <DataGridTextColumn Header="Bottom exposure (s)" + Binding="{Binding BottomExposure}" + Width="Auto" /> + <DataGridTextColumn Header="Exposure (s)" + Binding="{Binding Exposure}" + Width="Auto" /> + </DataGrid.Columns> + + </DataGrid> + + </StackPanel> +</UserControl> diff --git a/UVtools.WPF/Controls/Tools/ToolDynamicLayerHeightControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolDynamicLayerHeightControl.axaml.cs new file mode 100644 index 0000000..abe252b --- /dev/null +++ b/UVtools.WPF/Controls/Tools/ToolDynamicLayerHeightControl.axaml.cs @@ -0,0 +1,86 @@ +using System; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using UVtools.Core.FileFormats; +using UVtools.Core.Operations; +using UVtools.WPF.Extensions; +using UVtools.WPF.Windows; + +namespace UVtools.WPF.Controls.Tools +{ + public class ToolDynamicLayerHeightControl : ToolControl + { + public OperationDynamicLayerHeight Operation => BaseOperation as OperationDynamicLayerHeight; + + public double LayerHeight => SlicerFile.LayerHeight; + public double MinimumLayerHeight => Math.Round(SlicerFile.LayerHeight*2, 2); + public double MaximumLayerHeight => FileFormat.MaximumLayerHeight; + + private DataGrid ExposureTable; + + public ToolDynamicLayerHeightControl() + { + BaseOperation = new OperationDynamicLayerHeight(SlicerFile); + + if (SlicerFile.LayerHeight * 2 > FileFormat.MaximumLayerHeight) + { + App.MainWindow.MessageBoxError($"This file already uses the maximum layer height possible ({SlicerFile.LayerHeight}mm).\n" + + $"Layers can not be stacked, please re-slice your file with the lowest layer height of 0.01mm.", + $"{BaseOperation.Title} unable to run"); + CanRun = false; + return; + } + + for (uint layerIndex = 1; layerIndex < SlicerFile.LayerCount; layerIndex++) + { + if ((decimal)Math.Round(SlicerFile[layerIndex].PositionZ - SlicerFile[layerIndex - 1].PositionZ, 2) == + (decimal)SlicerFile.LayerHeight) continue; + App.MainWindow.MessageBoxError($"This file contain layer(s) with modified positions, starting at layer {layerIndex}.\n" + + $"This tool requires sequential layers with equal height.\n" + + $"If you run this tool before, you cant re-run.", + $"{BaseOperation.Title} unable to run"); + CanRun = false; + return; + } + + if (!SlicerFile.HavePrintParameterPerLayerModifier(FileFormat.PrintParameterModifier.ExposureSeconds)) + { + App.MainWindow.MessageBoxWaring($"Your printer seems to not support this tool, still you are allowed to run it for analyze, packing layers or simulation.\n" + + $"Do not print this file after run this tool on a not compatible printer, it will result in malformed model and height violation.\n" + + $"Run this at your own risk!", + BaseOperation.Title).ConfigureAwait(false); + } + + + InitializeComponent(); + + ExposureTable = this.FindControl<DataGrid>("ExposureTable"); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + + public override void Callback(ToolWindow.Callbacks callback) + { + if (SlicerFile is null) return; + switch (callback) + { + case ToolWindow.Callbacks.Init: + case ToolWindow.Callbacks.ProfileLoaded: + /*Operation.PropertyChanged += (sender, e) => + { + if (e.PropertyName.Equals(nameof(Operation.CacheObjectCount))) + { + RaisePropertyChanged(nameof(CacheRAMUsed)); + return; + } + };*/ + Operation.RebuildAutoExposureTable(); + break; + } + } + } +} diff --git a/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml b/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml index e485b25..cc7cbe5 100644 --- a/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml @@ -5,20 +5,47 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="UVtools.WPF.Controls.Tools.ToolEditParametersControl"> - <StackPanel Spacing="15"> + <StackPanel Spacing="10"> <CheckBox IsChecked="{Binding Operation.PerLayerOverride}" IsVisible="{Binding SupportPerLayerSettings}" Content="Change settings per a layer range"/> + <StackPanel Orientation="Horizontal" Spacing="5" IsVisible="{Binding Operation.PerLayerOverride}"> + <TextBlock + VerticalAlignment="Center" + Text="Sequentially set each"/> + + <NumericUpDown + VerticalAlignment="Center" + Minimum="1" + Maximum="100000" + Increment="1" + Value="{Binding Operation.SetNumberOfLayer}"/> + + <TextBlock + VerticalAlignment="Center" + Text="layers, and skip the next"/> + + <NumericUpDown + VerticalAlignment="Center" + Minimum="0" + Maximum="100000" + Increment="1" + Value="{Binding Operation.SkipNumberOfLayer}"/> + + <TextBlock + VerticalAlignment="Center" + Text="layers"/> + </StackPanel> + <Grid Name="grid" RowDefinitions="Auto" - ColumnDefinitions="Auto,Auto,Auto,Auto,*" + ColumnDefinitions="Auto,Auto,*,Auto,Auto" VerticalAlignment="Center" - ShowGridLines="True" - > + ShowGridLines="True"> <TextBlock Grid.Column="0" diff --git a/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs index d0ac7e1..d51bca0 100644 --- a/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs @@ -60,16 +60,18 @@ namespace UVtools.WPF.Controls.Tools { //DecimalPlaces = modifier.DecimalPlates, VerticalAlignment = VerticalAlignment.Center, + HorizontalAlignment = HorizontalAlignment.Stretch, Minimum = (double) modifier.Minimum, Maximum = (double) modifier.Maximum, Increment = modifier.DecimalPlates == 0 ? 1 : 0.5, Value = (double)modifier.NewValue, Tag = this, - Width = 100, + //Width = 100, + ClipValueToMinMax = true }; if (modifier.DecimalPlates > 0) { - NewValue.FormatString = "{0:0.00}"; + NewValue.FormatString = "F02"; } Unit = new TextBlock @@ -87,7 +89,8 @@ namespace UVtools.WPF.Controls.Tools VerticalAlignment = VerticalAlignment.Center, Tag = this, Padding = new Thickness(5), - Content = new Image {Source = App.GetBitmapFromAsset("/Assets/Icons/undo-16x16.png")} + Content = new Image {Source = App.GetBitmapFromAsset("/Assets/Icons/undo-16x16.png")}, + HorizontalAlignment = HorizontalAlignment.Stretch }; ResetButton.Click += ResetButtonOnClick; NewValue.ValueChanged += NewValueOnValueChanged; @@ -110,8 +113,7 @@ namespace UVtools.WPF.Controls.Tools { InitializeComponent(); - App.SlicerFile.RefreshPrintParametersModifiersValues(); - BaseOperation = new OperationEditParameters(App.SlicerFile.PrintParameterModifiers); + BaseOperation = new OperationEditParameters(SlicerFile); if (Operation.Modifiers is null || Operation.Modifiers.Length == 0) { diff --git a/UVtools.WPF/Controls/Tools/ToolFlipControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolFlipControl.axaml.cs index 7899b4d..44ea089 100644 --- a/UVtools.WPF/Controls/Tools/ToolFlipControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolFlipControl.axaml.cs @@ -10,7 +10,7 @@ namespace UVtools.WPF.Controls.Tools public ToolFlipControl() { InitializeComponent(); - BaseOperation = new OperationFlip(); + BaseOperation = new OperationFlip(SlicerFile); } private void InitializeComponent() diff --git a/UVtools.WPF/Controls/Tools/ToolInfillControl.axaml b/UVtools.WPF/Controls/Tools/ToolInfillControl.axaml index f09dc61..4fa2208 100644 --- a/UVtools.WPF/Controls/Tools/ToolInfillControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolInfillControl.axaml @@ -19,6 +19,7 @@ Grid.Column="2" Items="{Binding Operation.InfillAlgorithmTypes}" SelectedItem="{Binding Operation.InfillType}" + HorizontalAlignment="Stretch" /> <!--Wall thickness--> diff --git a/UVtools.WPF/Controls/Tools/ToolInfillControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolInfillControl.axaml.cs index a710a9e..c8fbb10 100644 --- a/UVtools.WPF/Controls/Tools/ToolInfillControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolInfillControl.axaml.cs @@ -10,7 +10,7 @@ namespace UVtools.WPF.Controls.Tools public ToolInfillControl() { InitializeComponent(); - BaseOperation = new OperationInfill(); + BaseOperation = new OperationInfill(SlicerFile); } private void InitializeComponent() diff --git a/UVtools.WPF/Controls/Tools/ToolLayerCloneControl.axaml b/UVtools.WPF/Controls/Tools/ToolLayerCloneControl.axaml index d877261..98c3084 100644 --- a/UVtools.WPF/Controls/Tools/ToolLayerCloneControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolLayerCloneControl.axaml @@ -11,7 +11,7 @@ Text="Clones:"/> <NumericUpDown Minimum="1" - Width="80" + Width="150" Value="{Binding Operation.Clones}" /> <TextBlock diff --git a/UVtools.WPF/Controls/Tools/ToolLayerCloneControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerCloneControl.axaml.cs index 644d023..6bb905c 100644 --- a/UVtools.WPF/Controls/Tools/ToolLayerCloneControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolLayerCloneControl.axaml.cs @@ -31,7 +31,7 @@ namespace UVtools.WPF.Controls.Tools public ToolLayerCloneControl() { InitializeComponent(); - BaseOperation = new OperationLayerClone(); + BaseOperation = new OperationLayerClone(SlicerFile); Operation.PropertyChanged += (sender, args) => { RaisePropertyChanged(nameof(InfoLayersStr)); diff --git a/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml b/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml index fa74937..9e40511 100644 --- a/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml @@ -7,7 +7,7 @@ <StackPanel Spacing="10"> <Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto" - ColumnDefinitions="Auto,10,100,5,Auto,20,Auto"> + ColumnDefinitions="Auto,10,150,5,Auto,20,Auto"> <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" @@ -23,6 +23,7 @@ <ComboBox Grid.Row="0" Grid.Column="2" VerticalAlignment="Center" + HorizontalAlignment="Stretch" SelectedItem="{Binding Operation.ImportType}" Items="{Binding Operation.ImportTypesItems}"/> diff --git a/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs index db58958..0fc4755 100644 --- a/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs @@ -90,9 +90,7 @@ namespace UVtools.WPF.Controls.Tools public ToolLayerImportControl() { InitializeComponent(); - BaseOperation = new OperationLayerImport(App.SlicerFile.Resolution); - Operation.Files.CollectionChanged += (sender, args) => RefreshGUI(); - Operation.PropertyChanged += (sender, args) => RefreshGUI(); + BaseOperation = new OperationLayerImport(SlicerFile); FilesListBox = this.Find<ListBox>("FilesListBox"); FilesListBox.DoubleTapped += (sender, args) => { @@ -152,7 +150,10 @@ namespace UVtools.WPF.Controls.Tools switch (callback) { case ToolWindow.Callbacks.Init: + case ToolWindow.Callbacks.ProfileLoaded: RefreshGUI(); + Operation.Files.CollectionChanged += (sender, args) => RefreshGUI(); + Operation.PropertyChanged += (sender, args) => RefreshGUI(); break; } } diff --git a/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml b/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml index 21c0e98..3db4d3c 100644 --- a/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml @@ -4,13 +4,13 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="UVtools.WPF.Controls.Tools.ToolLayerReHeightControl"> - <StackPanel Orientation="Vertical" Spacing="15"> + <StackPanel Orientation="Vertical" Spacing="10"> <TextBlock VerticalAlignment="Center" Text="{Binding CurrentLayers}"/> <StackPanel Orientation="Horizontal" Spacing="10"> <TextBlock VerticalAlignment="Center" Text="Modifier:"/> <ComboBox - MinWidth="250" + MinWidth="300" SelectedItem="{Binding Operation.Item}" Items="{Binding Presets}" /> diff --git a/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs index 09d3ebb..4b6e093 100644 --- a/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs @@ -8,7 +8,7 @@ namespace UVtools.WPF.Controls.Tools { public OperationLayerReHeight Operation => BaseOperation as OperationLayerReHeight; - public OperationLayerReHeight.OperationLayerReHeightItem[] Presets => OperationLayerReHeight.GetItems( + public OperationLayerReHeight.OperationLayerReHeightItem[] Presets => App.SlicerFile is null ? null : OperationLayerReHeight.GetItems( App.SlicerFile.LayerCount, (decimal)App.SlicerFile.LayerHeight); @@ -17,9 +17,9 @@ namespace UVtools.WPF.Controls.Tools public ToolLayerReHeightControl() { InitializeComponent(); - BaseOperation = new OperationLayerReHeight(); - - if (Presets.Length == 0) + BaseOperation = new OperationLayerReHeight(SlicerFile); + var presets = Presets; + if (presets is null || presets.Length == 0) { App.MainWindow.MessageBoxInfo("No valid configuration to be able to re-height.\n" + "As workaround clone first or last layer and try re run this tool.", "Not possible to re-height"); @@ -27,7 +27,7 @@ namespace UVtools.WPF.Controls.Tools } else { - Operation.Item = Presets[0]; + Operation.Item = presets[0]; } } diff --git a/UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml.cs index f4be3b7..6e56c20 100644 --- a/UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml.cs @@ -31,7 +31,7 @@ namespace UVtools.WPF.Controls.Tools public ToolLayerRemoveControl() { InitializeComponent(); - BaseOperation = new OperationLayerRemove(); + BaseOperation = new OperationLayerRemove(SlicerFile); Operation.PropertyChanged += (sender, args) => { RaisePropertyChanged(nameof(InfoLayersStr)); diff --git a/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml b/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml index da77c3c..2773294 100644 --- a/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml @@ -5,7 +5,7 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800" x:Class="UVtools.WPF.Controls.Tools.ToolMaskControl"> <StackPanel Spacing="10"> - <StackPanel Orientation="Horizontal" Spacing="15"> + <StackPanel Orientation="Horizontal" Spacing="10"> <Button Padding="10" Content="Import grayscale mask image from file" @@ -43,7 +43,6 @@ Grid.Column="2" Minimum="0" Maximum="255" - Width="70" Value="{Binding GenMinimumBrightness}" /> <TextBlock @@ -63,7 +62,6 @@ Grid.Column="8" Minimum="0" Maximum="255" - Width="70" Value="{Binding GenMaximumBrightness}" /> @@ -76,7 +74,6 @@ Grid.Column="2" Minimum="0" Maximum="10000" - Width="70" Value="{Binding GenDiameter}" /> <Button @@ -85,6 +82,7 @@ Grid.ColumnSpan="5" Content="Generate" Padding="5" + VerticalAlignment="Stretch" Command="{Binding GenerateMask}" /> diff --git a/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs index bad26b4..4369133 100644 --- a/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs @@ -69,7 +69,7 @@ namespace UVtools.WPF.Controls.Tools public ToolMaskControl() { InitializeComponent(); - BaseOperation = new OperationMask(); + BaseOperation = new OperationMask(SlicerFile); } private void InitializeComponent() diff --git a/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml b/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml index f9297f2..5611261 100644 --- a/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml @@ -3,7 +3,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:uc="clr-namespace:UVtools.WPF.Controls" - mc:Ignorable="d" d:DesignWidth="450" d:DesignHeight="400" + mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="400" x:Class="UVtools.WPF.Controls.Tools.ToolMorphControl"> <StackPanel> @@ -23,7 +23,7 @@ <NumericUpDown Minimum="1" - Width="70" + Width="140" Value="{Binding Operation.IterationsStart}" /> @@ -34,7 +34,7 @@ <NumericUpDown Minimum="1" - Width="70" + Width="140" Value="{Binding Operation.IterationsEnd}" IsEnabled="{Binding Operation.Chamfer}" /> @@ -55,6 +55,7 @@ <ComboBox Grid.Row="2" Grid.Column="2" + HorizontalAlignment="Stretch" SelectedIndex="{Binding MorphSelectedIndex}" Items="{Binding Operation.MorphOperations}" /> @@ -62,14 +63,12 @@ </Grid> <Border - Margin="0,10,0,0" - > + Margin="0,10,0,0" > <Expander> <Expander.Header> <TextBlock Text="Kernel - Advanced options (Click to expand)" FontWeight="Bold" - Cursor="Hand" - /> + Cursor="Hand"/> </Expander.Header> <uc:KernelControl Name="KernelCtrl" diff --git a/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs index bd5c176..9d17f05 100644 --- a/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs @@ -28,7 +28,7 @@ namespace UVtools.WPF.Controls.Tools public ToolMorphControl() { InitializeComponent(); - BaseOperation = new OperationMorph(); + BaseOperation = new OperationMorph(SlicerFile); _kernelCtrl = this.Find<KernelControl>("KernelCtrl"); } diff --git a/UVtools.WPF/Controls/Tools/ToolMoveControl.axaml b/UVtools.WPF/Controls/Tools/ToolMoveControl.axaml index e9e3953..a216fbc 100644 --- a/UVtools.WPF/Controls/Tools/ToolMoveControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolMoveControl.axaml @@ -27,155 +27,160 @@ </StackPanel> - <StackPanel Grid.Row="0" Grid.Column="2" - VerticalAlignment="Center" - Orientation="Horizontal" - Spacing="5"> - <TextBlock - VerticalAlignment="Center" - FontWeight="Bold" - Text="Left"/> - <NumericUpDown + <Grid Grid.Row="0" Grid.Column="2" + RowDefinitions="Auto,Auto,Auto" + ColumnDefinitions="Auto,Auto,Auto" VerticalAlignment="Center" - ButtonSpinnerLocation="Left" - Width="80" - Value="{Binding Operation.MarginLeft}" - /> + HorizontalAlignment="Center"> - <StackPanel> - <TextBlock - VerticalAlignment="Center" - HorizontalAlignment="Center" - FontWeight="Bold" - Margin="0,0,0,5" - Text="Top"/> - <NumericUpDown - VerticalAlignment="Center" - Width="80" - Value="{Binding Operation.MarginTop}" - /> + <StackPanel Grid.Row="1" Grid.Column="0" + VerticalAlignment="Center" + Orientation="Horizontal" + Spacing="5"> + <TextBlock + VerticalAlignment="Center" + FontWeight="Bold" + Text="Left"/> + <NumericUpDown + VerticalAlignment="Center" + ButtonSpinnerLocation="Left" + Width="150" + Minimum="-100000" + Maximum="100000" + Value="{Binding Operation.MarginLeft}"/> + </StackPanel> - <Grid RowDefinitions="Auto,Auto,Auto" - ColumnDefinitions="Auto,Auto,Auto" - Margin="10" - > + <StackPanel Grid.Row="0" Grid.Column="1"> + <TextBlock + VerticalAlignment="Center" + HorizontalAlignment="Center" + FontWeight="Bold" + Margin="0,0,0,5" + Text="Top"/> + <NumericUpDown + VerticalAlignment="Center" + Width="150" + Minimum="-100000" + Maximum="100000" + Value="{Binding Operation.MarginTop}"/> + </StackPanel> - <RadioButton - Grid.Row="0" Grid.Column="0" - ToolTip.Tip="Top Left" - VerticalAlignment="Center" - HorizontalAlignment="Center" - GroupName="Anchor" - Command="{Binding Operation.SetAnchor}" - CommandParameter="0" - /> - <RadioButton - Grid.Row="0" Grid.Column="1" - ToolTip.Tip="Top Center" - VerticalAlignment="Center" - HorizontalAlignment="Center" - GroupName="Anchor" - Command="{Binding Operation.SetAnchor}" - CommandParameter="1" - /> - <RadioButton - Grid.Row="0" Grid.Column="2" - ToolTip.Tip="Top Right" - VerticalAlignment="Center" - HorizontalAlignment="Center" - GroupName="Anchor" - Command="{Binding Operation.SetAnchor}" - CommandParameter="2" - /> - - <RadioButton - Grid.Row="1" Grid.Column="0" - ToolTip.Tip="Middle Left" - VerticalAlignment="Center" - HorizontalAlignment="Center" - GroupName="Anchor" - Command="{Binding Operation.SetAnchor}" - CommandParameter="3" - /> - <RadioButton - Grid.Row="1" Grid.Column="1" - ToolTip.Tip="Middle Center" - VerticalAlignment="Center" - HorizontalAlignment="Center" - Margin="5" - IsChecked="{Binding IsMiddleCenterChecked}" - GroupName="Anchor" - Command="{Binding Operation.SetAnchor}" - CommandParameter="4" - /> - <RadioButton - Grid.Row="1" Grid.Column="2" - ToolTip.Tip="Middle Right" - VerticalAlignment="Center" - HorizontalAlignment="Center" - GroupName="Anchor" - Command="{Binding Operation.SetAnchor}" - CommandParameter="5" - /> - - <RadioButton - Grid.Row="2" Grid.Column="0" - ToolTip.Tip="Bottom Left" - VerticalAlignment="Center" - HorizontalAlignment="Center" - GroupName="Anchor" - Command="{Binding Operation.SetAnchor}" - CommandParameter="6" - /> - <RadioButton - Grid.Row="2" Grid.Column="1" - ToolTip.Tip="Bottom Center" - VerticalAlignment="Center" - HorizontalAlignment="Center" - GroupName="Anchor" - Command="{Binding Operation.SetAnchor}" - CommandParameter="7" - /> - <RadioButton - Grid.Row="2" Grid.Column="2" - ToolTip.Tip="Bottom Right" - VerticalAlignment="Center" - HorizontalAlignment="Center" - GroupName="Anchor" - Command="{Binding Operation.SetAnchor}" - CommandParameter="8" - /> - - - </Grid> + <StackPanel Grid.Row="1" Grid.Column="2" + Orientation="Horizontal" + VerticalAlignment="Center" + Spacing="5"> + <NumericUpDown + VerticalAlignment="Center" + Width="150" + Minimum="-100000" + Maximum="100000" + Value="{Binding Operation.MarginRight}"/> + <TextBlock + VerticalAlignment="Center" + FontWeight="Bold" + Text="Right"/> + </StackPanel> - <NumericUpDown - VerticalAlignment="Center" - Width="80" - Value="{Binding Operation.MarginBottom}" - /> - <TextBlock - VerticalAlignment="Center" - HorizontalAlignment="Center" - FontWeight="Bold" - Margin="0,5,0,0" - Text="Bottom"/> - - </StackPanel> + <StackPanel Grid.Row="2" Grid.Column="1"> + <NumericUpDown + VerticalAlignment="Center" + Width="150" + Minimum="-100000" + Maximum="100000" + Value="{Binding Operation.MarginBottom}"/> + <TextBlock + VerticalAlignment="Center" + HorizontalAlignment="Center" + FontWeight="Bold" + Margin="0,5,0,0" + Text="Bottom"/> + </StackPanel> - <NumericUpDown - VerticalAlignment="Center" - Width="80" - Value="{Binding Operation.MarginRight}" - /> - <TextBlock - VerticalAlignment="Center" - HorizontalAlignment="Center" - FontWeight="Bold" - Text="Right" - /> - - </StackPanel> - + <Grid Grid.Row="1" Grid.Column="1" + RowDefinitions="30,30,30" + ColumnDefinitions="30,30,30" + Width="90" + Height="90" + Margin="10"> + + <RadioButton + Grid.Row="0" Grid.Column="0" + ToolTip.Tip="Top Left" + VerticalAlignment="Center" + HorizontalAlignment="Center" + GroupName="Anchor" + Command="{Binding Operation.SetAnchor}" + CommandParameter="0"/> + <RadioButton + Grid.Row="0" Grid.Column="1" + ToolTip.Tip="Top Center" + VerticalAlignment="Center" + HorizontalAlignment="Center" + GroupName="Anchor" + Command="{Binding Operation.SetAnchor}" + CommandParameter="1"/> + <RadioButton + Grid.Row="0" Grid.Column="2" + ToolTip.Tip="Top Right" + VerticalAlignment="Center" + HorizontalAlignment="Center" + GroupName="Anchor" + Command="{Binding Operation.SetAnchor}" + CommandParameter="2"/> + + <RadioButton + Grid.Row="1" Grid.Column="0" + ToolTip.Tip="Middle Left" + VerticalAlignment="Center" + HorizontalAlignment="Center" + GroupName="Anchor" + Command="{Binding Operation.SetAnchor}" + CommandParameter="3"/> + <RadioButton + Grid.Row="1" Grid.Column="1" + ToolTip.Tip="Middle Center" + VerticalAlignment="Center" + HorizontalAlignment="Center" + IsChecked="{Binding IsMiddleCenterChecked}" + GroupName="Anchor" + Command="{Binding Operation.SetAnchor}" + CommandParameter="4"/> + <RadioButton + Grid.Row="1" Grid.Column="2" + ToolTip.Tip="Middle Right" + VerticalAlignment="Center" + HorizontalAlignment="Center" + GroupName="Anchor" + Command="{Binding Operation.SetAnchor}" + CommandParameter="5"/> + + <RadioButton + Grid.Row="2" Grid.Column="0" + ToolTip.Tip="Bottom Left" + VerticalAlignment="Center" + HorizontalAlignment="Center" + GroupName="Anchor" + Command="{Binding Operation.SetAnchor}" + CommandParameter="6"/> + <RadioButton + Grid.Row="2" Grid.Column="1" + ToolTip.Tip="Bottom Center" + VerticalAlignment="Center" + HorizontalAlignment="Center" + GroupName="Anchor" + Command="{Binding Operation.SetAnchor}" + CommandParameter="7"/> + <RadioButton + Grid.Row="2" Grid.Column="2" + ToolTip.Tip="Bottom Right" + VerticalAlignment="Center" + HorizontalAlignment="Center" + GroupName="Anchor" + Command="{Binding Operation.SetAnchor}" + CommandParameter="8"/> + + </Grid> + + </Grid> </Grid> </UserControl> diff --git a/UVtools.WPF/Controls/Tools/ToolMoveControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolMoveControl.axaml.cs index a18fc5f..5e01d2b 100644 --- a/UVtools.WPF/Controls/Tools/ToolMoveControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolMoveControl.axaml.cs @@ -19,8 +19,7 @@ namespace UVtools.WPF.Controls.Tools { InitializeComponent(); var roi = App.MainWindow.ROI; - BaseOperation = new OperationMove(roi.IsEmpty ? App.SlicerFile.LayerManager.BoundingRectangle : roi, (uint)App.MainWindow.LayerCache.Image.Width, - (uint)App.MainWindow.LayerCache.Image.Height); + BaseOperation = new OperationMove(SlicerFile, roi); Operation.PropertyChanged += (sender, e) => { diff --git a/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml b/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml index 43c4446..f357225 100644 --- a/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml @@ -9,19 +9,17 @@ <Grid Margin="0,0,0,15" RowDefinitions="Auto,10,Auto" - ColumnDefinitions="Auto,10,Auto,20,Auto,10,Auto,5,Auto,10,Auto" + ColumnDefinitions="Auto,10,150,20,Auto,10,150,5,Auto,10,Auto" > <!-- Cols --> <TextBlock Text="Columns:" VerticalAlignment="Center" HorizontalAlignment="Right" - ToolTip.Tip="Number of copies in X direction." - /> + ToolTip.Tip="Number of copies in X direction."/> <NumericUpDown Grid.Column="2" Grid.Row="0" - Width="80" Minimum="1" Maximum="{Binding Operation.MaxCols}" Value="{Binding Operation.Cols}" @@ -37,7 +35,6 @@ <NumericUpDown Grid.Column="6" Grid.Row="0" - Width="80" Minimum="1" Maximum="65535" Value="{Binding Operation.ColSpacing}" @@ -68,7 +65,6 @@ <NumericUpDown Grid.Column="2" Grid.Row="2" - Width="80" Minimum="1" Maximum="{Binding Operation.MaxRows}" Value="{Binding Operation.Rows}" @@ -84,7 +80,6 @@ <NumericUpDown Grid.Column="6" Grid.Row="2" - Width="80" Minimum="1" Maximum="65535" Value="{Binding Operation.RowSpacing}" diff --git a/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml.cs index cb29ad4..319ec32 100644 --- a/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml.cs @@ -21,7 +21,7 @@ namespace UVtools.WPF.Controls.Tools { InitializeComponent(); var roi = App.MainWindow.ROI; - BaseOperation = new OperationPattern(roi.IsEmpty ? App.SlicerFile.LayerManager.BoundingRectangle : roi, App.SlicerFile.Resolution); + BaseOperation = new OperationPattern(SlicerFile, roi); if (Operation.MaxRows < 2 && Operation.MaxCols < 2) { diff --git a/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml b/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml index 88924e3..8ec27a5 100644 --- a/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml @@ -2,7 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" d:DesignWidth="950" d:DesignHeight="500" + mc:Ignorable="d" d:DesignWidth="950" d:DesignHeight="600" x:Class="UVtools.WPF.Controls.Tools.ToolPixelDimmingControl"> <StackPanel Spacing="10"> <StackPanel Spacing="10" Orientation="Horizontal"> @@ -46,27 +46,26 @@ <CheckBox Margin="20,0,0,0" Content="Dim only walls" - IsChecked="{Binding Operation.WallsOnly}" - /> - - <TextBlock - Margin="20,0,0,0" - VerticalAlignment="Center" - Text="Alternate the pattern every:" - /> - <NumericUpDown - Minimum="1" - Maximum="{Binding ushort.MaxValue}" - Width="80" - Value="{Binding Operation.AlternatePatternPerLayers}" - /> - <TextBlock - VerticalAlignment="Center" - Text="layers" - /> - - + IsChecked="{Binding Operation.WallsOnly}"/> + </StackPanel> + + <StackPanel Orientation="Horizontal" Spacing="10"> + <TextBlock + VerticalAlignment="Center" + Text="Alternate the pattern every:" + /> + <NumericUpDown + Minimum="1" + Maximum="{Binding ushort.MaxValue}" + Width="150" + Value="{Binding Operation.AlternatePatternPerLayers}" + /> + <TextBlock + VerticalAlignment="Center" + Text="layers" + /> + </StackPanel> <Grid RowDefinitions="200,10,Auto" @@ -105,7 +104,6 @@ <NumericUpDown Minimum="0" Maximum="254" - Width="80" Value="{Binding Operation.Brightness}"/> <TextBlock @@ -211,36 +209,33 @@ TextWrapping="Wrap" Text="Warning: This function can generate a large number of resin traps. (Use with caution)"/> + <Grid RowDefinitions="Auto,10,Auto" + ColumnDefinitions="Auto,10,150,5,Auto"> + <TextBlock + VerticalAlignment="Center" + Text="Thickness:"/> + <NumericUpDown + Grid.Row="0" Grid.Column="2" + Minimum="5" + Maximum="10000" + Value="{Binding Operation.InfillGenThickness}"/> + <TextBlock Grid.Row="0" Grid.Column="4" + VerticalAlignment="Center" + Text="px"/> - <StackPanel Orientation="Horizontal" Spacing="10"> - <TextBlock - VerticalAlignment="Center" - Text="Thickness:"/> - <NumericUpDown - Minimum="5" - Maximum="10000" - Width="80" - Value="{Binding Operation.InfillGenThickness}" - /> - <TextBlock - VerticalAlignment="Center" - Text="px"/> - - <TextBlock - Margin="20,0,0,0" - VerticalAlignment="Center" - Text="Spacing:"/> - <NumericUpDown - Minimum="5" - Maximum="10000" - Width="80" - Value="{Binding Operation.InfillGenSpacing}" - /> - <TextBlock - VerticalAlignment="Center" - Text="px"/> - </StackPanel> + <TextBlock Grid.Row="2" Grid.Column="0" + VerticalAlignment="Center" + Text="Spacing:"/> + <NumericUpDown Grid.Row="2" Grid.Column="2" + Minimum="5" + Maximum="10000" + Value="{Binding Operation.InfillGenSpacing}"/> + <TextBlock Grid.Row="2" Grid.Column="4" + VerticalAlignment="Center" + Text="px"/> + </Grid> + <StackPanel Orientation="Horizontal" Spacing="10"> <Button Padding="10" diff --git a/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml.cs index 3ec06b9..4d09bb6 100644 --- a/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml.cs @@ -15,7 +15,7 @@ namespace UVtools.WPF.Controls.Tools public ToolPixelDimmingControl() { InitializeComponent(); - BaseOperation = new OperationPixelDimming(); + BaseOperation = new OperationPixelDimming(SlicerFile); Operation.GeneratePixelDimming("Chessboard"); } diff --git a/UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml b/UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml index 02b0ad7..7f1e337 100644 --- a/UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml @@ -7,14 +7,14 @@ <Grid - ColumnDefinitions="Auto,10,Auto,5,Auto" + ColumnDefinitions="Auto,10,150,5,Auto" RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto" > <TextBlock Text="Relief type:" VerticalAlignment="Center"/> <ComboBox Grid.Row="0" Grid.Column="2" - Width="100" + HorizontalAlignment="Stretch" SelectedItem="{Binding Operation.ReliefType}" Items="{Binding Operation.RaftReliefItems}"/> @@ -25,7 +25,6 @@ Text="Ignore first:" VerticalAlignment="Center"/> <NumericUpDown Grid.Row="2" Grid.Column="2" - Width="100" Minimum="0" Maximum="255" Value="{Binding Operation.IgnoreFirstLayers}"/> @@ -40,7 +39,6 @@ Text="Pixel brightness:" VerticalAlignment="Center"/> <NumericUpDown Grid.Row="4" Grid.Column="2" - Width="100" Minimum="0" Maximum="255" IsVisible="{Binding !Operation.IsDecimate}" @@ -65,7 +63,6 @@ Text="Dilate supports by:" VerticalAlignment="Center"/> <NumericUpDown Grid.Row="6" Grid.Column="2" - Width="100" Minimum="0" Maximum="255" Value="{Binding Operation.DilateIterations}"/> @@ -81,7 +78,6 @@ /> <NumericUpDown Grid.Row="8" Grid.Column="2" - Width="100" Minimum="1" Maximum="255" Value="{Binding Operation.WallMargin}" @@ -98,7 +94,6 @@ /> <NumericUpDown Grid.Row="10" Grid.Column="2" - Width="100" Minimum="10" Maximum="255" Value="{Binding Operation.HoleDiameter}" @@ -115,7 +110,6 @@ /> <NumericUpDown Grid.Row="12" Grid.Column="2" - Width="100" Minimum="10" Maximum="255" Value="{Binding Operation.HoleSpacing}" diff --git a/UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml.cs index aa333eb..58606b9 100644 --- a/UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml.cs @@ -10,7 +10,7 @@ namespace UVtools.WPF.Controls.Tools public ToolRaftReliefControl() { this.InitializeComponent(); - BaseOperation = new OperationRaftRelief(); + BaseOperation = new OperationRaftRelief(SlicerFile); } private void InitializeComponent() diff --git a/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml b/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml index cce0f0e..92bd9ef 100644 --- a/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml @@ -4,46 +4,40 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="UVtools.WPF.Controls.Tools.ToolRedrawModelControl"> - <StackPanel Spacing="10"> - <Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto" - ColumnDefinitions="Auto,10,100,5,300,5,Auto"> + <Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto" + ColumnDefinitions="Auto,10,Auto"> <TextBlock Grid.Row="0" Grid.Column="0" ToolTip.Tip="Select the sliced file without supports and raft. (Model B)" VerticalAlignment="Center" Text="Body file:"/> - <TextBox Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="3" - ToolTip.Tip="Select the sliced file without supports and raft. (Model B)" - IsReadOnly="True" - Watermark="Select the sliced file without supports and raft. (Model B)" - Text="{Binding Operation.FilePath}"/> - <Button Grid.Row="0" Grid.Column="6" - ToolTip.Tip="Select the sliced file without supports and raft. (Model B)" - Command="{Binding ImportFile}"> - <Image Source="/Assets/Icons/file-import-16x16.png"/> - </Button> + <StackPanel Grid.Row="0" Grid.Column="2" Orientation="Horizontal" Spacing="1"> + <TextBox + ToolTip.Tip="Select the sliced file without supports and raft. (Model B)" + IsReadOnly="True" + Watermark="Select the sliced file without supports and raft. (Model B)" + HorizontalAlignment="Stretch" + Width="505" + Text="{Binding Operation.FilePath}"/> + <Button + VerticalAlignment="Stretch" + ToolTip.Tip="Select the sliced file without supports and raft. (Model B)" + Command="{Binding ImportFile}"> + <Image Source="/Assets/Icons/file-import-16x16.png"/> + </Button> + </StackPanel> + <TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Text="Redraw:"/> - <ComboBox Grid.Row="2" Grid.Column="2" - SelectedItem="{Binding Operation.RedrawType}" - Items="{Binding Operation.RedrawTypesItems}"/> - <TextBlock Grid.Row="4" Grid.Column="0" - VerticalAlignment="Center" - Text="Brightness:"/> - <NumericUpDown Grid.Row="4" Grid.Column="2" - ClipValueToMinMax="True" - Minimum="0" - Maximum="255" - Increment="1" - Value="{Binding Operation.Brightness}"/> - <TextBlock Grid.Row="4" Grid.Column="4" - VerticalAlignment="Center" - Text="{Binding Operation.BrightnessPercent, StringFormat=\{0:0.00\}%}"/> + <StackPanel Grid.Row="2" Grid.Column="2" Orientation="Horizontal" Spacing="10"> + <ComboBox + SelectedItem="{Binding Operation.RedrawType}" + Items="{Binding Operation.RedrawTypesItems}" + Width="120" + /> - <StackPanel Grid.Row="2" Grid.Column="4" Grid.ColumnSpan="3" - Orientation="Horizontal" Spacing="10"> <CheckBox VerticalAlignment="Center" IsVisible="{Binding !Operation.RedrawType}" @@ -58,9 +52,21 @@ ToolTip.Tip="If enabled, all supports pixels with no physical contact with the model will be ignored and maintain the same original brightness." Content="Ignore contact-less pixels"/> </StackPanel> - - </Grid> + <TextBlock Grid.Row="4" Grid.Column="0" + VerticalAlignment="Center" + Text="Brightness:"/> - </StackPanel> + <StackPanel Grid.Row="4" Grid.Column="2" Orientation="Horizontal" Spacing="5"> + <NumericUpDown + Minimum="0" + Maximum="255" + Increment="1" + Value="{Binding Operation.Brightness}"/> + <TextBlock + VerticalAlignment="Center" + Text="{Binding Operation.BrightnessPercent, StringFormat=\{0:0.00\}%}"/> + </StackPanel> + + </Grid> </UserControl> diff --git a/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml.cs index 83934ff..ad5cd81 100644 --- a/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml.cs @@ -3,6 +3,7 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; using UVtools.Core.FileFormats; using UVtools.Core.Operations; +using UVtools.WPF.Windows; namespace UVtools.WPF.Controls.Tools { @@ -12,7 +13,7 @@ namespace UVtools.WPF.Controls.Tools public ToolRedrawModelControl() { InitializeComponent(); - BaseOperation = new OperationRedrawModel(); + BaseOperation = new OperationRedrawModel(SlicerFile); } private void InitializeComponent() @@ -20,6 +21,24 @@ namespace UVtools.WPF.Controls.Tools AvaloniaXamlLoader.Load(this); } + public override void Callback(ToolWindow.Callbacks callback) + { + switch (callback) + { + case ToolWindow.Callbacks.Init: + case ToolWindow.Callbacks.ProfileLoaded: + ParentWindow.ButtonOkEnabled = !string.IsNullOrWhiteSpace(Operation.FilePath); + Operation.PropertyChanged += (sender, e) => + { + if (e.PropertyName == nameof(Operation.FilePath)) + { + ParentWindow.ButtonOkEnabled = !string.IsNullOrWhiteSpace(Operation.FilePath); + } + }; + break; + } + } + public async void ImportFile() { var filters = Helpers.ToAvaloniaFileFilter(FileFormat.AllFileFiltersAvalonia); diff --git a/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml b/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml index 5a71eb5..d20808e 100644 --- a/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml @@ -5,51 +5,35 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="UVtools.WPF.Controls.Tools.ToolRepairLayersControl"> - <StackPanel Orientation="Vertical" Spacing="15"> + <StackPanel Orientation="Vertical" Spacing="10"> <StackPanel Orientation="Horizontal" Spacing="20"> <!-- Repair islands --> <CheckBox IsChecked="{Binding Operation.RepairIslands}" + ToolTip.Tip="If enabled, repair will first attempt to eliminate islands smaller than the pixel area removal threshold, and then runs the “gap closure” technique." Content="Repair islands" - > - <ToolTip.Tip> - <StackPanel> - <TextBlock FontWeight="Bold" Text="Repair islands:"/> - <TextBlock Text="If enabled, repair will first attempt to eliminate islands smaller than the pixel area removal threshold, and then runs the “gap closure” technique"/> - </StackPanel> - </ToolTip.Tip> - </CheckBox> + /> + <!-- Repair resin traps --> <CheckBox IsChecked="{Binding Operation.RepairResinTraps}" + ToolTip.Tip="If enabled, repair will fill black pixels found within a resin trap with white pixels. +
Hollow areas will become solid." Content="Repair resin traps" - > - <ToolTip.Tip> - <StackPanel> - <TextBlock FontWeight="Bold" Text="Repair resin traps:"/> - <TextBlock Text="If enabled, repair will fill black pixels found within a resin trap with white pixels. -
Hollow areas will become solid."/> - </StackPanel> - </ToolTip.Tip> - </CheckBox> + /> + <!-- Remove empty layers --> <CheckBox IsChecked="{Binding Operation.RemoveEmptyLayers}" + ToolTip.Tip="If enabled, repair will remove all layers with no white pixels. +
The model will be recalculated to ensure the correct Z height is maintained." Content="Remove empty layers" - > - <ToolTip.Tip> - <StackPanel> - <TextBlock FontWeight="Bold" Text="Remove empty layers:"/> - <TextBlock Text="If enabled, repair will remove all layers with no white pixels. -
The model will be recalculated to ensure the correct Z height is maintained."/> - </StackPanel> - </ToolTip.Tip> - </CheckBox> - </StackPanel> - - <Grid ColumnDefinitions="Auto,10,Auto,5,Auto" + /> + </StackPanel> + + <Grid ColumnDefinitions="Auto,10,150,5,Auto" RowDefinitions="Auto,10,Auto"> <!-- Remove islands equal or smaller than --> @@ -57,15 +41,9 @@ Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" - Text="Remove islands equal or smaller than:"> - <ToolTip.Tip> - <StackPanel> - <TextBlock FontWeight="Bold" Text="Remove islands equal or smaller than:"/> - <TextBlock Text="The pixel area theshold above which islands will not be removed by this repair. - 
Islands remaining after repair will require supports to be added manually."/> - </StackPanel> - </ToolTip.Tip> - </TextBlock> + ToolTip.Tip="The pixel area threshold above which islands will not be removed by this repair. + 
Islands remaining after repair will require supports to be added manually." + Text="Remove islands equal or smaller than:"/> <NumericUpDown Grid.Row="0" @@ -73,17 +51,12 @@ Increment="1" Minimum="0" Maximum="65535" - Width="70" + ClipValueToMinMax="True" + ToolTip.Tip="The pixel area threshold above which islands will not be removed by this repair. +
Islands remaining after repair will require supports to be added manually." Value="{Binding Operation.RemoveIslandsBelowEqualPixelCount}" - > - <ToolTip.Tip> - <StackPanel> - <TextBlock FontWeight="Bold" Text="Remove islands equal or smaller than:"/> - <TextBlock Text="The pixel area theshold above which islands will not be removed by this repair. -
Islands remaining after repair will require supports to be added manually."/> - </StackPanel> - </ToolTip.Tip> - </NumericUpDown> + /> + <TextBlock Grid.Row="0" Grid.Column="4" @@ -96,18 +69,12 @@ Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" - Text="Recursively remove islands for up to:"> - <ToolTip.Tip> - <StackPanel> - <TextBlock FontWeight="Bold" Text="Recursively remove islands for up to:"/> - <TextBlock Text="If the removal of an island in the current layer results in a new island being introduce in the layer above, the island in the layer above will also be automatically removed. + ToolTip.Tip="If the removal of an island in the current layer results in a new island being introduce in the layer above, the island in the layer above will also be automatically removed. 
This process will repeat for up to the number of layers specified. Set to 0 to repeat until there are no more valid islands to remove. 
 
NOTE: Use with caution as this can remove large portions of your model if proper supports have not been added beforehand. -
 Using this function with high values can be extremely slow depending on resolution and issue count."/> - </StackPanel> - </ToolTip.Tip> - </TextBlock> +
Using this function with high values can be extremely slow depending on resolution and issue count." + Text="Recursively remove islands for up to:"/> <NumericUpDown Grid.Row="2" @@ -115,20 +82,16 @@ Increment="1" Minimum="0" Maximum="65535" - Width="70" - Value="{Binding Operation.RemoveIslandsRecursiveIterations}" - > - <ToolTip.Tip> - <StackPanel> - <TextBlock FontWeight="Bold" Text="Recursively remove islands for up to:"/> - <TextBlock Text="If the removal of an island in the current layer results in a new island being introduce in the layer above, the island in the layer above will also be automatically removed. + ClipValueToMinMax="True" + ToolTip.Tip="If the removal of an island in the current layer results in a new island being introduce in the layer above, the island in the layer above will also be automatically removed. 
This process will repeat for up to the number of layers specified. Set to 0 to repeat until there are no more valid islands to remove. 
 
NOTE: Use with caution as this can remove large portions of your model if proper supports have not been added beforehand. -
 Using this function with high values can be extremely slow depending on resolution and issue count."/> - </StackPanel> - </ToolTip.Tip> - </NumericUpDown> +
Using this function with high values can be extremely slow depending on resolution and issue count." + Value="{Binding Operation.RemoveIslandsRecursiveIterations}" + /> + + <TextBlock Grid.Row="2" Grid.Column="4" @@ -140,24 +103,20 @@ <Grid IsVisible="{Binding ParentWindow.IsCheckBox1Checked}" - RowDefinitions="Auto,10,Auto" ColumnDefinitions="Auto,10,70"> + RowDefinitions="Auto,10,Auto" + ColumnDefinitions="Auto,10,155"> <!-- Gap closing iterations --> <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" - Text="Gap closing iterations:"> - <ToolTip.Tip> - <StackPanel> - <TextBlock FontWeight="Bold" Text="Gap closing iterations:"/> - <TextBlock Text="Attempt to repair islands by attaching them to other nearby islands in safe increments. + ToolTip.Tip="Attempt to repair islands by attaching them to other nearby islands in safe increments. 
If enabled, gap closing is attempted before island removal is attempted. 
Set iterations to 0 to disable. -
WARNING: Using high iteration values can destroy your model. Low values recomended."/> - </StackPanel> - </ToolTip.Tip> - </TextBlock> +
WARNING: Using high iteration values can destroy your model. Low values recommended." + Text="Gap closing iterations:"/> + <NumericUpDown Grid.Row="0" @@ -165,55 +124,41 @@ Increment="1" Minimum="0" Maximum="255" - Value="{Binding Operation.GapClosingIterations}" - > - <ToolTip.Tip> - <StackPanel> - <TextBlock FontWeight="Bold" Text="Gap closing iterations:"/> - <TextBlock Text="Attempt to repair islands by attaching them to other nearby islands in safe increments. + ClipValueToMinMax="True" + ToolTip.Tip="Attempt to repair islands by attaching them to other nearby islands in safe increments. 
If enabled, gap closing is attempted before island removal is attempted. 
Set iterations to 0 to disable. -
WARNING: Using high iteration values can destroy your model. Low values recomended."/> - </StackPanel> - </ToolTip.Tip> - </NumericUpDown> +
WARNING: Using high iteration values can destroy your model. Low values recommended." + Value="{Binding Operation.GapClosingIterations}" + /> + <!-- Noise removal iterations: --> <TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" - Text="Noise removal iterations:"> - <ToolTip.Tip> - <StackPanel> - <TextBlock FontWeight="Bold" Text="Noise removal iterations:"/> - <TextBlock Text="Remove individual or small clusters of pixels in iterations. + ToolTip.Tip="Remove individual or small clusters of pixels in iterations. 
This settings can remove noise from a layer, but will also remove fine details. -
Set iterations to 0 to diasble. -
WARNING: Even at low settings this operation has the potential to introduce new islands, as it may remove supporting material from previous layers."/> - </StackPanel> - </ToolTip.Tip> - </TextBlock> +
Set iterations to 0 to disable. +
WARNING: Even at low settings this operation has the potential to introduce new islands, as it may remove supporting material from previous layers." + Text="Noise removal iterations:"/> + <NumericUpDown Grid.Row="2" Grid.Column="2" - ToolTip.Tip="Remove individual or small clusters of pixels in iterations. This settings can remove noise from a layer, but will also remove fine details. Set iterations to 0 to diasble. WARNING: Even at low settings this operation has the potential to introduce new islands, as it may remove supporting material from previous layers." Increment="1" Minimum="0" Maximum="255" - Value="{Binding Operation.NoiseRemovalIterations}" - > - <ToolTip.Tip> - <StackPanel> - <TextBlock FontWeight="Bold" Text="Noise removal iterations:"/> - <TextBlock Text="Remove individual or small clusters of pixels in iterations. + ClipValueToMinMax="True" + ToolTip.Tip="Remove individual or small clusters of pixels in iterations. 
This settings can remove noise from a layer, but will also remove fine details. -
Set iterations to 0 to diasble. -
WARNING: Even at low settings this operation has the potential to introduce new islands, as it may remove supporting material from previous layers."/> - </StackPanel> - </ToolTip.Tip> - </NumericUpDown> +
Set iterations to 0 to disable. +
WARNING: Even at low settings this operation has the potential to introduce new islands, as it may remove supporting material from previous layers." + Value="{Binding Operation.NoiseRemovalIterations}" + /> + </Grid> </StackPanel> diff --git a/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs index 0c317d8..14ed006 100644 --- a/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs @@ -11,7 +11,7 @@ namespace UVtools.WPF.Controls.Tools public ToolRepairLayersControl() { InitializeComponent(); - BaseOperation = new OperationRepairLayers + BaseOperation = new OperationRepairLayers(SlicerFile) { RepairIslands = UserSettings.Instance.LayerRepair.RepairIslands, RepairResinTraps = UserSettings.Instance.LayerRepair.RepairResinTraps, diff --git a/UVtools.WPF/Controls/Tools/ToolResizeControl.axaml b/UVtools.WPF/Controls/Tools/ToolResizeControl.axaml index 300ecc8..ff8571c 100644 --- a/UVtools.WPF/Controls/Tools/ToolResizeControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolResizeControl.axaml @@ -2,31 +2,27 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="200" + mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="200" x:Class="UVtools.WPF.Controls.Tools.ToolResizeControl"> <StackPanel Orientation="Vertical" Spacing="10"> <StackPanel Orientation="Horizontal" Spacing="10"> <TextBlock VerticalAlignment="Center" Text="X:"/> - <NumericUpDown - MinWidth="100" - MaxWidth="150" + <NumericUpDown + Width="150" Minimum="1" Maximum="10000" Increment="0.1" FormatString="{}{0:0.00} %" - Value="{Binding Operation.X}" - /> + Value="{Binding Operation.X}"/> <TextBlock Margin="10,0,0,0" VerticalAlignment="Center" Text="Y:" - IsEnabled="{Binding !#ConstrainXY.IsChecked}" - /> + IsEnabled="{Binding !#ConstrainXY.IsChecked}"/> <NumericUpDown - MinWidth="100" - MaxWidth="150" + Width="150" Minimum="1" Maximum="10000" Increment="0.1" diff --git a/UVtools.WPF/Controls/Tools/ToolResizeControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolResizeControl.axaml.cs index c88b965..c55ac81 100644 --- a/UVtools.WPF/Controls/Tools/ToolResizeControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolResizeControl.axaml.cs @@ -10,7 +10,7 @@ namespace UVtools.WPF.Controls.Tools public ToolResizeControl() { InitializeComponent(); - BaseOperation = new OperationResize(); + BaseOperation = new OperationResize(SlicerFile); } private void InitializeComponent() diff --git a/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml b/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml index 293c620..4181605 100644 --- a/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml @@ -7,12 +7,11 @@ <StackPanel Orientation="Horizontal" Spacing="10"> <TextBlock VerticalAlignment="Center" Text="Rotation angle:"/> <NumericUpDown - MinWidth="100" - MaxWidth="150" + Width="150" Minimum="-359.99" Maximum="359.99" Increment="1.0" - FormatString="{}{0:0.00}" + FormatString="F02" Value="{Binding Operation.AngleDegrees}" /> <TextBlock VerticalAlignment="Center" Text="degrees"/> diff --git a/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml.cs index c09b075..0fb6e9f 100644 --- a/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml.cs @@ -10,7 +10,7 @@ namespace UVtools.WPF.Controls.Tools public ToolRotateControl() { InitializeComponent(); - BaseOperation = new OperationRotate(); + BaseOperation = new OperationRotate(SlicerFile); } private void InitializeComponent() diff --git a/UVtools.WPF/Controls/Tools/ToolSolidifyControl.axaml b/UVtools.WPF/Controls/Tools/ToolSolidifyControl.axaml index fe7cc24..bad072e 100644 --- a/UVtools.WPF/Controls/Tools/ToolSolidifyControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolSolidifyControl.axaml @@ -12,7 +12,7 @@ <ComboBox Items="{Binding Operation.AreaCheckTypeItems}" SelectedItem="{Binding Operation.AreaCheckType}" - Width="70" + Width="100" VerticalAlignment="Center" /> <TextBlock @@ -20,7 +20,7 @@ VerticalAlignment="Center" /> <NumericUpDown - Width="100" + Width="200" Minimum="1" Maximum="4294967295" Value="{Binding Operation.MinimumArea}" diff --git a/UVtools.WPF/Controls/Tools/ToolSolidifyControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolSolidifyControl.axaml.cs index e851ee9..7a138de 100644 --- a/UVtools.WPF/Controls/Tools/ToolSolidifyControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolSolidifyControl.axaml.cs @@ -9,7 +9,7 @@ namespace UVtools.WPF.Controls.Tools public ToolSolidifyControl() { InitializeComponent(); - BaseOperation = new OperationSolidify(); + BaseOperation = new OperationSolidify(SlicerFile); } private void InitializeComponent() diff --git a/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml b/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml index 5e42a42..d97d1bf 100644 --- a/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml @@ -31,7 +31,7 @@ <NumericUpDown Minimum="0" Maximum="255" - MinWidth="80" + Width="150" IsEnabled="{Binding IsThresholdEnabled}" Value="{Binding Operation.Threshold}" /> @@ -45,13 +45,13 @@ <NumericUpDown Minimum="0" Maximum="255" - MinWidth="80" + Width="150" IsEnabled="{Binding IsMaximumEnabled}" Value="{Binding Operation.Maximum}" /> <ComboBox - MinWidth="120" + MinWidth="150" IsEnabled="{Binding IsTypeEnabled}" Items="{Binding Operation.ThresholdTypes}" SelectedItem="{Binding Operation.Type}"/> diff --git a/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml.cs index 648bbbf..3be8386 100644 --- a/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml.cs @@ -74,7 +74,7 @@ namespace UVtools.WPF.Controls.Tools public ToolThresholdControl() { InitializeComponent(); - BaseOperation = new OperationThreshold(); + BaseOperation = new OperationThreshold(SlicerFile); } private void InitializeComponent() diff --git a/UVtools.WPF/Controls/WindowEx.cs b/UVtools.WPF/Controls/WindowEx.cs index 5f870f2..4c28b9a 100644 --- a/UVtools.WPF/Controls/WindowEx.cs +++ b/UVtools.WPF/Controls/WindowEx.cs @@ -6,15 +6,18 @@ * of this license document, but changing it is not allowed. */ +using System; using System.Collections.Generic; using System.ComponentModel; using System.Runtime.CompilerServices; -using System.Threading.Tasks; +using Avalonia; using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Styling; namespace UVtools.WPF.Controls { - public class WindowEx : Window, INotifyPropertyChanged + public class WindowEx : Window, INotifyPropertyChanged, IStyleable { #region BindableBase /// <summary> @@ -58,6 +61,8 @@ namespace UVtools.WPF.Controls } #endregion + Type IStyleable.StyleKey => typeof(Window); + public DialogResults DialogResult { get; set; } = DialogResults.Unknown; public enum DialogResults { @@ -66,6 +71,14 @@ namespace UVtools.WPF.Controls Cancel } + public WindowEx() + { +#if DEBUG + this.AttachDevTools(new KeyGesture(Key.F12, KeyModifiers.Control)); +#endif + //TransparencyLevelHint = WindowTransparencyLevel.AcrylicBlur; + } + public void CloseWithResult() { Close(DialogResult); diff --git a/UVtools.WPF/Extensions/WindowExtensions.cs b/UVtools.WPF/Extensions/WindowExtensions.cs index 22a871f..954297b 100644 --- a/UVtools.WPF/Extensions/WindowExtensions.cs +++ b/UVtools.WPF/Extensions/WindowExtensions.cs @@ -11,7 +11,6 @@ using System.Threading.Tasks; using Avalonia; using Avalonia.Controls; using Avalonia.Threading; -using Avalonia.VisualTree; using MessageBox.Avalonia.DTO; using MessageBox.Avalonia.Enums; @@ -36,7 +35,6 @@ namespace UVtools.WPF.Extensions MaxWidth = window.GetScreenWorkingArea().Width - UserSettings.Instance.General.WindowsHorizontalMargin, ShowInCenter = true }); - return await messageBoxStandardWindow.ShowDialog(window); } @@ -49,6 +47,9 @@ namespace UVtools.WPF.Extensions public static async Task<ButtonResult> MessageBoxQuestion(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.YesNo, Style style = Style.None) => await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Question", buttons, Icon.Setting, WindowStartupLocation.CenterOwner, style); + public static async Task<ButtonResult> MessageBoxWaring(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.Ok, Style style = Style.None) + => await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Question", buttons, Icon.Warning, WindowStartupLocation.CenterOwner, style); + public static void ShowDialogSync(this Window window, Window parent = null) { diff --git a/UVtools.WPF/MainWindow.Information.cs b/UVtools.WPF/MainWindow.Information.cs index 1de8ac1..9b0bb1a 100644 --- a/UVtools.WPF/MainWindow.Information.cs +++ b/UVtools.WPF/MainWindow.Information.cs @@ -335,13 +335,14 @@ namespace UVtools.WPF var layer = LayerCache.Layer; CurrentLayerProperties.Clear(); CurrentLayerProperties.Add(new StringTag(nameof(layer.Index), $"{layer.Index}")); + CurrentLayerProperties.Add(new StringTag(nameof(layer.LayerHeight), $"{layer.LayerHeight:F2}mm")); //CurrentLayerProperties.Add(new KeyValuePair<string, string>(nameof(layer.Filename), layer.Filename)); - CurrentLayerProperties.Add(new StringTag(nameof(layer.PositionZ), $"{layer.PositionZ.ToString(CultureInfo.InvariantCulture)}mm")); + CurrentLayerProperties.Add(new StringTag(nameof(layer.PositionZ), $"{layer.PositionZ:F2}mm")); CurrentLayerProperties.Add(new StringTag(nameof(layer.IsBottomLayer), layer.IsBottomLayer.ToString())); CurrentLayerProperties.Add(new StringTag(nameof(layer.IsModified), layer.IsModified.ToString())); //CurrentLayerProperties.Add(new StringTag(nameof(layer.BoundingRectangle), layer.BoundingRectangle.ToString())); //CurrentLayerProperties.Add(new StringTag(nameof(layer.NonZeroPixelCount), layer.NonZeroPixelCount.ToString())); - CurrentLayerProperties.Add(new StringTag(nameof(layer.ExposureTime), $"{layer.ExposureTime.ToString(CultureInfo.InvariantCulture)}s")); + CurrentLayerProperties.Add(new StringTag(nameof(layer.ExposureTime), $"{layer.ExposureTime:F2}s")); if (ReferenceEquals(SlicerFile.PrintParameterPerLayerModifiers, null)) return; diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml index f2dcee6..85de78b 100644 --- a/UVtools.WPF/MainWindow.axaml +++ b/UVtools.WPF/MainWindow.axaml @@ -1,8 +1,7 @@ -<Window xmlns="https://github.com/avaloniaui" +<uc:WindowEx xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:manager="clr-namespace:Avalonia.ThemeManager;assembly=Avalonia.ThemeManager" xmlns:uc="clr-namespace:UVtools.WPF.Controls" mc:Ignorable="d" d:DesignWidth="1024" d:DesignHeight="600" x:Class="UVtools.WPF.MainWindow" @@ -684,7 +683,7 @@ Command="{Binding OnClickRepairIssues}"> <StackPanel Orientation="Horizontal" Spacing="5"> <Image Source="/Assets/Icons/wrench-16x16.png" /> - <TextBlock VerticalAlignment="Center" Text="Repair"/> + <!--<TextBlock VerticalAlignment="Center" Text="Repair"/>!--> </StackPanel> </Button> @@ -761,6 +760,7 @@ <TabItem Name="TabPixelEditor" ToolTip.Tip="Pixel editor" + IsVisible="{Binding IsPixelEditorActive}" IsEnabled="{Binding IsPixelEditorActive}"> <TabItem.Header> <StackPanel VerticalAlignment="Center" Orientation="Horizontal"> @@ -770,13 +770,12 @@ </TabItem.Header> <Grid RowDefinitions="Auto,20,Auto,*"> - <TabControl SelectedIndex="{Binding SelectedPixelOperationTabIndex}"> + <TabControl + Padding="10,0" + SelectedIndex="{Binding SelectedPixelOperationTabIndex}"> <!-- Drawing !--> - <TabItem - ToolTip.Tip="Drawing" - > - <TabItem.Header - > + <TabItem ToolTip.Tip="Drawing"> + <TabItem.Header> <StackPanel VerticalAlignment="Center" Orientation="Horizontal"> <Image Source="/Assets/Icons/pencil-alt-24x24.png" Width="24"/> <TextBlock @@ -786,16 +785,15 @@ </StackPanel> </TabItem.Header> - <StackPanel Spacing="10" Margin="5"> + <StackPanel Spacing="10"> <Border Background="LightGray" BorderThickness="1" BorderBrush="Black"> <TextBlock Padding="10" Text="Shift+Left click to add white pixels 
Shift+Right click to remove pixels"/> </Border> <Grid - - RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,Auto" - ColumnDefinitions="Auto,10,Auto,10,Auto,5,Auto"> + RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto" + ColumnDefinitions="Auto,10,130,5,40"> <TextBlock Grid.Row="0" @@ -805,10 +803,10 @@ <ComboBox Grid.Row="0" Grid.Column="2" - Grid.ColumnSpan="5" + Grid.ColumnSpan="3" + Width="180" Items="{Binding DrawingPixelDrawing.LineTypes}" - SelectedItem="{Binding DrawingPixelDrawing.LineType}" - /> + SelectedItem="{Binding DrawingPixelDrawing.LineType}"/> <TextBlock Grid.Row="2" @@ -818,10 +816,10 @@ <ComboBox Grid.Row="2" Grid.Column="2" - Grid.ColumnSpan="5" + Grid.ColumnSpan="3" + Width="180" Items="{Binding DrawingPixelDrawing.BrushShapeTypes}" - SelectedItem="{Binding DrawingPixelDrawing.BrushShape}" - /> + SelectedItem="{Binding DrawingPixelDrawing.BrushShape}"/> <TextBlock Grid.Row="4" @@ -831,13 +829,14 @@ <NumericUpDown Grid.Row="4" Grid.Column="2" - Grid.ColumnSpan="3" Minimum="1" Maximum="255" + ClipValueToMinMax="True" + HorizontalAlignment="Stretch" Value="{Binding DrawingPixelDrawing.BrushSize}"/> <TextBlock Grid.Row="4" - Grid.Column="6" + Grid.Column="4" VerticalAlignment="Center" Text="px" /> @@ -849,14 +848,14 @@ <NumericUpDown Grid.Row="6" Grid.Column="2" - Grid.ColumnSpan="3" Minimum="-1" Maximum="255" - Value="{Binding DrawingPixelDrawing.Thickness}" - /> + ClipValueToMinMax="True" + HorizontalAlignment="Stretch" + Value="{Binding DrawingPixelDrawing.Thickness}"/> <TextBlock Grid.Row="6" - Grid.Column="6" + Grid.Column="4" VerticalAlignment="Center" Text="px" /> @@ -868,16 +867,16 @@ <NumericUpDown Grid.Row="8" Grid.Column="2" - Grid.ColumnSpan="3" Minimum="0" Maximum="255" ClipValueToMinMax="True" + HorizontalAlignment="Stretch" Value="{Binding DrawingPixelDrawing.RemovePixelBrightness}"/> <TextBlock Grid.Row="8" - Grid.Column="6" + Grid.Column="4" VerticalAlignment="Center" - Text="{Binding DrawingPixelDrawing.RemovePixelBrightnessPercent, StringFormat=\{0:0.00\}%}" /> + Text="{Binding DrawingPixelDrawing.RemovePixelBrightnessPercent, StringFormat=\{0:0\}%}" /> <TextBlock Grid.Row="10" @@ -887,49 +886,45 @@ <NumericUpDown Grid.Row="10" Grid.Column="2" - Grid.ColumnSpan="3" Minimum="1" Maximum="255" ClipValueToMinMax="True" + HorizontalAlignment="Stretch" Value="{Binding DrawingPixelDrawing.PixelBrightness}"/> <TextBlock Grid.Row="10" - Grid.Column="6" + Grid.Column="4" VerticalAlignment="Center" - Text="{Binding DrawingPixelDrawing.PixelBrightnessPercent, StringFormat=\{0:0.00\}%}" /> + Text="{Binding DrawingPixelDrawing.PixelBrightnessPercent, StringFormat=\{0:0\}%}" /> <TextBlock Grid.Row="12" Grid.Column="0" VerticalAlignment="Center" - Text="Layers depth:" /> + Text="Layers depth below:" /> <NumericUpDown Grid.Row="12" Grid.Column="2" + Grid.ColumnSpan="3" Minimum="0" - Width="80" - Value="{Binding DrawingPixelDrawing.LayersBelow}" - /> - <TextBlock - Grid.Row="13" - Grid.Column="2" - VerticalAlignment="Center" - HorizontalAlignment="Center" - Text="Below" /> + ClipValueToMinMax="True" + HorizontalAlignment="Stretch" + Value="{Binding DrawingPixelDrawing.LayersBelow}"/> + + <TextBlock + Grid.Row="14" + Grid.Column="0" + VerticalAlignment="Center" + Text="Layers depth above:" /> <NumericUpDown - Grid.Row="12" - Grid.Column="4" + Grid.Row="14" + Grid.Column="2" + Grid.ColumnSpan="3" Minimum="0" - Width="80" - Value="{Binding DrawingPixelDrawing.LayersAbove}" - /> - <TextBlock - Grid.Row="13" - Grid.Column="4" - VerticalAlignment="Center" - HorizontalAlignment="Center" - Text="Above" /> + ClipValueToMinMax="True" + HorizontalAlignment="Stretch" + Value="{Binding DrawingPixelDrawing.LayersAbove}"/> </Grid> @@ -938,9 +933,7 @@ </TabItem> <!-- Text !--> - <TabItem - ToolTip.Tip="Text" - > + <TabItem ToolTip.Tip="Text"> <TabItem.Header> <StackPanel VerticalAlignment="Center" Orientation="Horizontal"> <Image Source="/Assets/Icons/font-24x24.png" Width="24"/> @@ -951,15 +944,15 @@ </StackPanel> </TabItem.Header> - <StackPanel Spacing="10" Margin="5"> + <StackPanel Spacing="10"> <Border Background="LightGray" BorderThickness="1" BorderBrush="Black"> <TextBlock Padding="10" Text="Shift+Left click to add text 
Shift+Right click to remove text"/> </Border> <Grid - RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,Auto" - ColumnDefinitions="Auto,10,Auto,10,Auto,5,Auto"> + RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto" + ColumnDefinitions="Auto,10,130,5,40"> <TextBlock Grid.Row="0" @@ -970,6 +963,7 @@ Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="3" + Width="180" Items="{Binding DrawingPixelText.LineTypes}" SelectedItem="{Binding DrawingPixelText.LineType}"/> @@ -982,6 +976,7 @@ Grid.Row="2" Grid.Column="2" Grid.ColumnSpan="3" + Width="180" Items="{Binding DrawingPixelText.FontFaces}" SelectedItem="{Binding DrawingPixelText.Font}"/> @@ -997,6 +992,7 @@ Minimum="0.1" Maximum="255" Increment="0.1" + ClipValueToMinMax="True" Value="{Binding DrawingPixelText.FontScale}"/> @@ -1011,6 +1007,7 @@ Grid.ColumnSpan="3" Minimum="1" Maximum="255" + ClipValueToMinMax="True" Value="{Binding DrawingPixelText.Thickness}"/> <TextBlock @@ -1039,16 +1036,15 @@ <NumericUpDown Grid.Row="12" Grid.Column="2" - Grid.ColumnSpan="3" Minimum="0" Maximum="255" ClipValueToMinMax="True" Value="{Binding DrawingPixelText.RemovePixelBrightness}"/> <TextBlock Grid.Row="12" - Grid.Column="6" + Grid.Column="4" VerticalAlignment="Center" - Text="{Binding DrawingPixelText.RemovePixelBrightnessPercent, StringFormat=\{0:0.00\}%}" /> + Text="{Binding DrawingPixelText.RemovePixelBrightnessPercent, StringFormat=\{0:0\}%}" /> <TextBlock Grid.Row="14" @@ -1058,50 +1054,43 @@ <NumericUpDown Grid.Row="14" Grid.Column="2" - Grid.ColumnSpan="3" Minimum="1" Maximum="255" ClipValueToMinMax="True" Value="{Binding DrawingPixelText.PixelBrightness}"/> <TextBlock Grid.Row="14" - Grid.Column="6" + Grid.Column="4" VerticalAlignment="Center" - Text="{Binding DrawingPixelText.PixelBrightnessPercent, StringFormat=\{0:0.00\}%}" /> + Text="{Binding DrawingPixelText.PixelBrightnessPercent, StringFormat=\{0:0\}%}" /> <TextBlock Grid.Row="16" Grid.Column="0" VerticalAlignment="Center" - Text="Layers depth:" /> + Text="Layers depth below:" /> <NumericUpDown Grid.Row="16" Grid.Column="2" + Grid.ColumnSpan="3" Minimum="0" - Width="80" - Value="{Binding DrawingPixelText.LayersBelow}" - /> - <TextBlock - Grid.Row="17" - Grid.Column="2" - VerticalAlignment="Center" - HorizontalAlignment="Center" - Text="Below" /> + ClipValueToMinMax="True" + Value="{Binding DrawingPixelText.LayersBelow}"/> + + <TextBlock + Grid.Row="18" + Grid.Column="0" + VerticalAlignment="Center" + Text="Layers depth above:" /> <NumericUpDown - Grid.Row="16" - Grid.Column="4" + Grid.Row="18" + Grid.Column="2" + Grid.ColumnSpan="3" Minimum="0" - Width="80" - Value="{Binding DrawingPixelText.LayersAbove}" - /> - <TextBlock - Grid.Row="17" - Grid.Column="4" - VerticalAlignment="Center" - HorizontalAlignment="Center" - Text="Above" /> + ClipValueToMinMax="True" + Value="{Binding DrawingPixelText.LayersAbove}"/> </Grid> @@ -1110,9 +1099,7 @@ </TabItem> <!-- Eraser !--> - <TabItem - ToolTip.Tip="Eraser" - > + <TabItem ToolTip.Tip="Eraser"> <TabItem.Header> <StackPanel VerticalAlignment="Center" Orientation="Horizontal"> <Image Source="/Assets/Icons/eraser-24x24.png" Width="24"/> @@ -1123,14 +1110,15 @@ </StackPanel> </TabItem.Header> - <StackPanel Spacing="10" Margin="5"> + <StackPanel Spacing="10"> <Border Background="LightGray" BorderThickness="1" BorderBrush="Black"> - <TextBlock Padding="10" Text="Shift+click over a white pixel to remove whole linked area. (Fill with black)"/> + <TextBlock Padding="10" Text="Shift+click over a white pixel to remove the linked area. +
(Fill linked area with black)"/> </Border> <Grid - RowDefinitions="Auto,10,Auto,Auto" - ColumnDefinitions="Auto,10,Auto,10,Auto,5,Auto"> + RowDefinitions="Auto,10,Auto,10,Auto" + ColumnDefinitions="Auto,10,*,10,40"> <TextBlock Grid.Row="0" @@ -1140,49 +1128,42 @@ <NumericUpDown Grid.Row="0" Grid.Column="2" - Grid.ColumnSpan="3" Minimum="0" Maximum="255" ClipValueToMinMax="True" Value="{Binding DrawingPixelEraser.PixelBrightness}"/> <TextBlock Grid.Row="0" - Grid.Column="6" + Grid.Column="4" VerticalAlignment="Center" - Text="{Binding DrawingPixelEraser.PixelBrightnessPercent, StringFormat=\{0:0.00\}%}" /> + Text="{Binding DrawingPixelEraser.PixelBrightnessPercent, StringFormat=\{0:0\}%}" /> - <TextBlock + <TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" - Text="Layers depth:" /> + Text="Layers depth below:" /> <NumericUpDown Grid.Row="2" Grid.Column="2" + Grid.ColumnSpan="3" Minimum="0" - Width="80" - Value="{Binding DrawingPixelEraser.LayersBelow}" - /> - <TextBlock - Grid.Row="3" - Grid.Column="2" - VerticalAlignment="Center" - HorizontalAlignment="Center" - Text="Below" /> + ClipValueToMinMax="True" + Value="{Binding DrawingPixelEraser.LayersBelow}"/> + <TextBlock + Grid.Row="4" + Grid.Column="0" + VerticalAlignment="Center" + Text="Layers depth above:" /> <NumericUpDown - Grid.Row="2" - Grid.Column="4" + Grid.Row="4" + Grid.Column="2" + Grid.ColumnSpan="3" Minimum="0" - Width="80" - Value="{Binding DrawingPixelEraser.LayersAbove}" - /> - <TextBlock - Grid.Row="3" - Grid.Column="4" - VerticalAlignment="Center" - HorizontalAlignment="Center" - Text="Above" /> + ClipValueToMinMax="True" + Value="{Binding DrawingPixelEraser.LayersAbove}"/> + </Grid> @@ -1204,14 +1185,14 @@ </StackPanel> </TabItem.Header> - <StackPanel Spacing="10" Margin="5"> + <StackPanel Spacing="10" > <Border Background="LightGray" BorderThickness="1" BorderBrush="Black"> - <TextBlock Padding="10" Text="Shift+click under a island to add a primitive support. + <TextBlock Padding="10" Text="Shift+click under a island to add primitive support. 
Note: this operation can't be previewed."/> </Border> <Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto" - ColumnDefinitions="Auto,10,100,5,Auto"> + ColumnDefinitions="Auto,10,*,5,35"> <TextBlock Grid.Row="0" Grid.Column="0" @@ -1279,7 +1260,7 @@ Grid.Row="6" Grid.Column="4" VerticalAlignment="Center" - Text="{Binding DrawingPixelSupport.PixelBrightnessPercent, StringFormat=\{0:0.00\}%}" /> + Text="{Binding DrawingPixelSupport.PixelBrightnessPercent, StringFormat=\{0:0\}%}" /> </Grid> </StackPanel> @@ -1300,14 +1281,14 @@ </StackPanel> </TabItem.Header> - <StackPanel Spacing="10" Margin="5"> + <StackPanel Spacing="10"> <Border Background="LightGray" BorderThickness="1" BorderBrush="Black"> <TextBlock Padding="10" Text="Shift+click to add a vertical drain hole. 
Note: this operation can't be previewed."/> </Border> <Grid RowDefinitions="Auto" - ColumnDefinitions="Auto,10,100,5,Auto"> + ColumnDefinitions="Auto,10,*,5,Auto"> <TextBlock Grid.Row="0" Grid.Column="0" @@ -1565,7 +1546,7 @@ <Grid IsEnabled="{Binding IsFileLoaded}" DockPanel.Dock="Right" - ColumnDefinitions="130" + ColumnDefinitions="150" RowDefinitions="Auto,Auto,*,Auto,Auto,Auto,Auto" Margin="5"> <TextBlock Text="{Binding MaximumLayerString}" @@ -1579,6 +1560,7 @@ ToolTip.Tip="Navigate to up layer [Ctrl + Up]" HotKey="Ctrl + Up" Interval="100" + HorizontalAlignment="Stretch" IsEnabled="{Binding CanGoUp}" Command="{Binding GoNextLayer}"> <Image Width="16" Height="16" Source="/Assets/Icons/arrow-up-16x16.png"/> @@ -1586,13 +1568,12 @@ <Grid Name="LayerNavigationSliderGrid" - Grid.Row="2" ColumnDefinitions="*,20,Auto"> + Grid.Row="2" ColumnDefinitions="*,20,40"> <Panel Grid.Column="0" Name="LayerNavigationTooltipPanel" Margin="{Binding LayerNavigationTooltipMargin}" - HorizontalAlignment="Left" - > + HorizontalAlignment="Stretch"> <Border Name="Layer.Navigation.Tooltip.Border" VerticalAlignment="Top" @@ -1600,7 +1581,7 @@ BorderThickness="5" CornerRadius="5" > - <TextBlock Padding="5" Text="{Binding ActualLayerTooltip}"/> + <TextBlock Padding="2" Text="{Binding ActualLayerTooltip}"/> </Border> </Panel> @@ -1636,6 +1617,7 @@ ToolTip.Tip="Navigate to down layer [Ctrl + Down]" HotKey="Ctrl + Down" Interval="100" + HorizontalAlignment="Stretch" IsEnabled="{Binding CanGoDown}" Command="{Binding GoPreviousLayer}" > @@ -1705,6 +1687,7 @@ <ToggleButton IsChecked="{Binding ShowLayerImageRotated}" HotKey="Ctrl + R" + VerticalAlignment="Stretch" ToolTip.Tip="Auto rotate layer preview image at 90º (This can slow down the layer preview) [CTRL+R]" > <StackPanel Orientation="Horizontal"> @@ -1716,6 +1699,7 @@ <ToggleButton IsChecked="{Binding ShowLayerImageDifference}" ToolTip.Tip="Show layer differences where darker pixels were also present on previous layer and the white pixels the difference between previous and current layer." + VerticalAlignment="Stretch" Margin="1,0,0,0" > <StackPanel Orientation="Horizontal"> @@ -1727,6 +1711,7 @@ <ToggleButton IsChecked="{Binding ShowLayerImageIssues}" ToolTip.Tip="Highlight Issues on current layer. Valid only if Issues are calculated." + VerticalAlignment="Stretch" Margin="1,0,0,0" > <StackPanel Orientation="Horizontal"> @@ -1738,6 +1723,7 @@ <ToggleButton IsChecked="{Binding ShowLayerImageCrosshairs}" ToolTip.Tip="Show crosshairs for selected issues on the current layer." + VerticalAlignment="Stretch" Margin="1,0,0,0" > <StackPanel Orientation="Horizontal"> @@ -1750,6 +1736,7 @@ ToolTip.Tip="Click to access the various outlines." Command="{Binding OpenContextMenu}" CommandParameter="LayerPreviewOutline" + VerticalAlignment="Stretch" Margin="1,0,0,0" > <Button.ContextMenu> @@ -1778,6 +1765,7 @@ <ToggleButton HorizontalAlignment="Right" IsChecked="{Binding IsPixelEditorActive}" ToolTip.Tip="Edit layer image: Draw pixels, add supports and/or drain holes." + VerticalAlignment="Stretch" Margin="1,0,0,0" > <StackPanel Orientation="Horizontal"> @@ -1792,6 +1780,7 @@ <WrapPanel HorizontalAlignment="Right" Grid.Row="0" Grid.Column="1" Orientation="Horizontal"> <Button Name="LayerActionsButton" Command="{Binding OpenContextMenu}" + VerticalAlignment="Stretch" CommandParameter="LayerActions"> <StackPanel Orientation="Horizontal"> <Image Source="/Assets/Icons/layers-alt-16x16.png"/> @@ -1806,6 +1795,7 @@ Command="{Binding ShowLayer}" HotKey="F5" ToolTip.Tip="Refresh current layer [F5]" + VerticalAlignment="Stretch" Margin="1,0,0,0"> <StackPanel Orientation="Horizontal"> <Image Source="/Assets/Icons/refresh-16x16.png"/> @@ -1815,6 +1805,7 @@ <Button Command="{Binding SaveCurrentLayerImage}" ToolTip.Tip="Save layer image to a file" + VerticalAlignment="Stretch" Margin="1,0,0,0"> <StackPanel Orientation="Horizontal"> <Image Source="/Assets/Icons/save-16x16.png"/> @@ -1942,4 +1933,4 @@ </DockPanel> -</Window> +</uc:WindowEx> diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs index f2b4647..84bfe9c 100644 --- a/UVtools.WPF/MainWindow.axaml.cs +++ b/UVtools.WPF/MainWindow.axaml.cs @@ -21,6 +21,7 @@ using System.Globalization; using System.IO; using System.Linq; using System.Threading.Tasks; +using Avalonia.Controls.Primitives; using UVtools.Core; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; @@ -51,11 +52,11 @@ namespace UVtools.WPF #region Controls - public ProgressWindow ProgressWindow = new ProgressWindow(); + public ProgressWindow ProgressWindow = new (); public static MenuItem[] MenuTools { get; } = { - new MenuItem + new() { Tag = new OperationEditParameters(), Icon = new Avalonia.Controls.Image @@ -63,7 +64,7 @@ namespace UVtools.WPF Source = new Bitmap(App.GetAsset("/Assets/Icons/wrench-16x16.png")) } }, - new MenuItem + new() { Tag = new OperationRepairLayers(), Icon = new Avalonia.Controls.Image @@ -71,7 +72,7 @@ namespace UVtools.WPF Source = new Bitmap(App.GetAsset("/Assets/Icons/toolbox-16x16.png")) } }, - new MenuItem + new() { Tag = new OperationMove(), Icon = new Avalonia.Controls.Image @@ -79,7 +80,7 @@ namespace UVtools.WPF Source = new Bitmap(App.GetAsset("/Assets/Icons/move-16x16.png")) } }, - new MenuItem + new() { Tag = new OperationResize(), Icon = new Avalonia.Controls.Image @@ -87,7 +88,7 @@ namespace UVtools.WPF Source = new Bitmap(App.GetAsset("/Assets/Icons/crop-16x16.png")) } }, - new MenuItem + new() { Tag = new OperationFlip(), Icon = new Avalonia.Controls.Image @@ -95,7 +96,7 @@ namespace UVtools.WPF Source = new Bitmap(App.GetAsset("/Assets/Icons/flip-16x16.png")) } }, - new MenuItem + new() { Tag = new OperationRotate(), Icon = new Avalonia.Controls.Image @@ -103,7 +104,7 @@ namespace UVtools.WPF Source = new Bitmap(App.GetAsset("/Assets/Icons/sync-16x16.png")) } }, - new MenuItem + new() { Tag = new OperationSolidify(), Icon = new Avalonia.Controls.Image @@ -111,7 +112,7 @@ namespace UVtools.WPF Source = new Bitmap(App.GetAsset("/Assets/Icons/square-solid-16x16.png")) } }, - new MenuItem + new() { Tag = new OperationMorph(), Icon = new Avalonia.Controls.Image @@ -119,7 +120,7 @@ namespace UVtools.WPF Source = new Bitmap(App.GetAsset("/Assets/Icons/geometry-16x16.png")) } }, - new MenuItem + new() { Tag = new OperationRaftRelief(), Icon = new Avalonia.Controls.Image @@ -127,15 +128,15 @@ namespace UVtools.WPF Source = new Bitmap(App.GetAsset("/Assets/Icons/cookie-16x16.png")) } }, - new MenuItem + new() { Tag = new OperationRedrawModel(), Icon = new Avalonia.Controls.Image { - Source = new Bitmap(App.GetAsset("/Assets/Icons/sun-16x16.png")) + Source = new Bitmap(App.GetAsset("/Assets/Icons/code-branch-16x16.png")) } }, - new MenuItem + new() { Tag = new OperationThreshold(), Icon = new Avalonia.Controls.Image @@ -143,7 +144,7 @@ namespace UVtools.WPF Source = new Bitmap(App.GetAsset("/Assets/Icons/th-16x16.png")) } }, - new MenuItem + new() { Tag = new OperationArithmetic(), Icon = new Avalonia.Controls.Image @@ -151,7 +152,7 @@ namespace UVtools.WPF Source = new Bitmap(App.GetAsset("/Assets/Icons/square-root-16x16.png")) } }, - new MenuItem + new() { Tag = new OperationMask(), Icon = new Avalonia.Controls.Image @@ -159,7 +160,7 @@ namespace UVtools.WPF Source = new Bitmap(App.GetAsset("/Assets/Icons/mask-16x16.png")) } }, - new MenuItem + new() { Tag = new OperationPixelDimming(), Icon = new Avalonia.Controls.Image @@ -167,7 +168,7 @@ namespace UVtools.WPF Source = new Bitmap(App.GetAsset("/Assets/Icons/pixel-16x16.png")) } }, - new MenuItem + new() { Tag = new OperationInfill(), Icon = new Avalonia.Controls.Image @@ -175,7 +176,7 @@ namespace UVtools.WPF Source = new Bitmap(App.GetAsset("/Assets/Icons/stroopwafel-16x16.png")) } }, - new MenuItem + new() { Tag = new OperationBlur(), Icon = new Avalonia.Controls.Image @@ -183,7 +184,7 @@ namespace UVtools.WPF Source = new Bitmap(App.GetAsset("/Assets/Icons/blur-16x16.png")) } }, - new MenuItem + new() { Tag = new OperationPattern(), Icon = new Avalonia.Controls.Image @@ -191,7 +192,15 @@ namespace UVtools.WPF Source = new Bitmap(App.GetAsset("/Assets/Icons/pattern-16x16.png")) } }, - new MenuItem + new() + { + Tag = new OperationDynamicLayerHeight(), + Icon = new Avalonia.Controls.Image + { + Source = new Bitmap(App.GetAsset("/Assets/Icons/dynamic-layers-16x16.png")) + } + }, + new() { Tag = new OperationLayerReHeight(), Icon = new Avalonia.Controls.Image @@ -199,7 +208,7 @@ namespace UVtools.WPF Source = new Bitmap(App.GetAsset("/Assets/Icons/ladder-16x16.png")) } }, - new MenuItem + new() { Tag = new OperationChangeResolution(), Icon = new Avalonia.Controls.Image @@ -207,7 +216,7 @@ namespace UVtools.WPF Source = new Bitmap(App.GetAsset("/Assets/Icons/resize-16x16.png")) } }, - new MenuItem + new() { Tag = new OperationCalculator(), Icon = new Avalonia.Controls.Image @@ -221,6 +230,14 @@ namespace UVtools.WPF { new() { + Tag = new OperationCalibrateExposureFinder(), + Icon = new Avalonia.Controls.Image + { + Source = new Bitmap(App.GetAsset("/Assets/Icons/sun-16x16.png")) + } + }, + new() + { Tag = new OperationCalibrateElephantFoot(), Icon = new Avalonia.Controls.Image { @@ -358,7 +375,7 @@ namespace UVtools.WPF public bool IsFileLoaded { - get => !(SlicerFile is null); + get => SlicerFile is not null; set => RaisePropertyChanged(); } @@ -419,9 +436,6 @@ namespace UVtools.WPF public MainWindow() { InitializeComponent(); -#if DEBUG - //this.AttachDevTools(); -#endif App.ThemeSelector?.EnableThemes(this); InitInformation(); @@ -489,7 +503,6 @@ namespace UVtools.WPF WindowState = WindowState.Maximized; } - AddLog($"{About.Software} start", Program.ProgramStartupTime.Elapsed.TotalSeconds); if (Settings.General.CheckForUpdatesOnStartup) @@ -965,7 +978,8 @@ namespace UVtools.WPF if (convertToFormat is not null) { var directory = Path.GetDirectoryName(sl1File.FileFullPath); - var filename = PathExtensions.GetFileNameStripAllExtensions(sl1File.FileFullPath); + var extensions = FileFormat.AllFileExtensionsString.OrderByDescending(s => s.Length).ToList(); + var filename = PathExtensions.GetFileNameStripExtensions(sl1File.FileFullPath, extensions); FileFormat convertedFile = null; IsGUIEnabled = false; @@ -1036,7 +1050,9 @@ namespace UVtools.WPF CanSave = true; if (Settings.Automations.SaveFileAfterModifications) { + var saveCount = _savesCount; await SaveFile(null, true); + _savesCount = saveCount; } } @@ -1087,7 +1103,7 @@ namespace UVtools.WPF UpdateTitle(); - if (!(mat is null) && Settings.LayerPreview.AutoRotateLayerBestView) + if (mat is not null && Settings.LayerPreview.AutoRotateLayerBestView) { _showLayerImageRotated = mat.Height > mat.Width; } @@ -1105,7 +1121,6 @@ namespace UVtools.WPF { OnClickDetectIssues(); } - } private async void ShowProgressWindow(string title) @@ -1395,7 +1410,7 @@ namespace UVtools.WPF switch (baseOperation) { case OperationEditParameters operation: - operation.Execute(SlicerFile); + operation.Execute(); RefreshProperties(); RefreshCurrentLayerData(); ResetDataContext(); @@ -1433,7 +1448,7 @@ namespace UVtools.WPF try { - return baseOperation.Execute(SlicerFile, ProgressWindow.RestartProgress(baseOperation.CanCancel)); + return baseOperation.Execute(ProgressWindow.RestartProgress(baseOperation.CanCancel)); } catch (OperationCanceledException) { @@ -1470,6 +1485,17 @@ namespace UVtools.WPF } } + if (baseOperation.Tag is not null) + { + var message = baseOperation.Tag.ToString(); + if (!string.IsNullOrWhiteSpace(message)) + { + //message += $"\nExecution time: "; + + await this.MessageBoxInfo(message, $"{baseOperation.Title} report ({LastStopWatch.Elapsed.Hours}h{LastStopWatch.Elapsed.Minutes}m{LastStopWatch.Elapsed.Seconds}s)"); + } + } + return result; } diff --git a/UVtools.WPF/Program.cs b/UVtools.WPF/Program.cs index efd7797..f8f9b20 100644 --- a/UVtools.WPF/Program.cs +++ b/UVtools.WPF/Program.cs @@ -1,7 +1,6 @@ using System; using System.Diagnostics; using Avalonia; -using Avalonia.Threading; using UVtools.WPF.Extensions; namespace UVtools.WPF diff --git a/UVtools.WPF/Structures/OperationProfiles.cs b/UVtools.WPF/Structures/OperationProfiles.cs index 1afadd9..5796fe8 100644 --- a/UVtools.WPF/Structures/OperationProfiles.cs +++ b/UVtools.WPF/Structures/OperationProfiles.cs @@ -1,11 +1,16 @@ -using System; -using System.Collections; +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Xml.Serialization; -using DynamicData; using UVtools.Core.Operations; namespace UVtools.WPF.Structures @@ -27,6 +32,7 @@ namespace UVtools.WPF.Structures [XmlElement(typeof(OperationFlip))] //[XmlElement(typeof(OperationLayerClone))] //[XmlElement(typeof(OperationLayerImport))] + [XmlElement(typeof(OperationDynamicLayerHeight))] //[XmlElement(typeof(OperationLayerReHeight))] //[XmlElement(typeof(OperationLayerRemove))] //[XmlElement(typeof(OperationMask))] @@ -46,7 +52,7 @@ namespace UVtools.WPF.Structures [XmlElement(typeof(OperationCalibrateTolerance))] [XmlElement(typeof(OperationCalibrateGrayscale))] [XmlElement(typeof(OperationCalibrateStressTower))] - public List<Operation> Operations { get; internal set; } = new List<Operation>(); + public List<Operation> Operations { get; internal set; } = new(); [XmlIgnore] public static List<Operation> Profiles diff --git a/UVtools.WPF/Structures/PEProfileFolder.cs b/UVtools.WPF/Structures/PEProfileFolder.cs index c09745b..5e26514 100644 --- a/UVtools.WPF/Structures/PEProfileFolder.cs +++ b/UVtools.WPF/Structures/PEProfileFolder.cs @@ -120,6 +120,10 @@ namespace UVtools.WPF.Structures Items[i].IsEnabled = false; } } + else + { + Updates++; + } } } } diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj index 3db6b4e..45fa8f8 100644 --- a/UVtools.WPF/UVtools.WPF.csproj +++ b/UVtools.WPF/UVtools.WPF.csproj @@ -12,7 +12,7 @@ <PackageLicenseFile>LICENSE</PackageLicenseFile> <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> <RepositoryType>Git</RepositoryType> - <Version>2.3.2</Version> + <Version>2.4.0</Version> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> @@ -24,33 +24,21 @@ <NoWarn>1701;1702;</NoWarn> </PropertyGroup> <ItemGroup> - <PackageReference Include="Avalonia" Version="0.10.0-rc1" /> + <PackageReference Include="Avalonia" Version="0.10.0" /> <PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2020091801" /> - <PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.0-rc1" /> - <PackageReference Include="Avalonia.Desktop" Version="0.10.0-rc1" /> - <PackageReference Include="Avalonia.ThemeManager" Version="0.10.0-rc1" /> - <PackageReference Include="Emgu.CV.runtime.windows" Version="4.4.0.4099" /> - <PackageReference Include="MessageBox.Avalonia" Version="0.10.7-rc1" /> - <PackageReference Include="ThemeEditor.Controls.ColorPicker" Version="0.10.0-rc1" /> + <PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.0" /> + <PackageReference Include="Avalonia.Desktop" Version="0.10.0" /> + <PackageReference Include="Avalonia.Diagnostics" Version="0.10.0" /> + <PackageReference Include="Avalonia.ThemeManager" Version="0.10.0" /> + <PackageReference Include="Emgu.CV.runtime.windows" Version="4.5.1.4349" /> + <PackageReference Include="MessageBox.Avalonia" Version="1.0.5" /> + <PackageReference Include="ThemeEditor.Controls.ColorPicker" Version="0.10.0" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\UVtools.Core\UVtools.Core.csproj" /> </ItemGroup> <ItemGroup> - <AvaloniaResource Include="Assets\Icons\*" /> - </ItemGroup> - <ItemGroup> - <None Remove="Assets\Icons\clipboard-32x32.png" /> - <None Remove="Assets\Icons\eye-slash-16x16.png" /> - <None Remove="Assets\Icons\layers-alt-32x32.png" /> - <None Remove="Assets\Icons\redo-16x16.png" /> - <None Remove="Assets\Icons\stroopwafel-16x16.png" /> - <None Remove="Assets\Icons\UVtools.ico" /> - </ItemGroup> - <ItemGroup> - <None Update="libcvextern.so"> - <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> - </None> + <None Include="..\PrusaSlicer\**" CopyToOutputDirectory="PreserveNewest" LinkBase="Assets\PrusaSlicer" /> <None Update="Assets\Themes\UVtools.themes"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> @@ -79,47 +67,12 @@ </None> </ItemGroup> <ItemGroup> - <Compile Update="Controls\AdvancedImageBox.axaml.cs"> - <DependentUpon>AdvancedImageBox.axaml</DependentUpon> - </Compile> - <Compile Update="Controls\Calibrators\CalibrateExternalTestsControl.axaml.cs"> - <DependentUpon>CalibrateExternalTestsControl.axaml</DependentUpon> - </Compile> - <Compile Update="Controls\Calibrators\CalibrateStressTowerControl.axaml.cs"> - <DependentUpon>CalibrateStressTowerControl.axaml</DependentUpon> - </Compile> - <Compile Update="Controls\Tools\ToolLayerRemoveControl.axaml.cs"> - <DependentUpon>ToolLayerRemoveControl.axaml</DependentUpon> - </Compile> - <Compile Update="Controls\Tools\ToolRedrawModelControl.axaml.cs"> - <DependentUpon>ToolRedrawModelControl.axaml</DependentUpon> - </Compile> - <Compile Update="Controls\Tools\ToolResizeControl.axaml.cs"> - <DependentUpon>ToolResizeControl.axaml</DependentUpon> - </Compile> - <Compile Update="MainWindow.Clipboard.cs"> - <DependentUpon>%(Filename)</DependentUpon> - </Compile> - <Compile Update="MainWindow.Information.cs"> - <DependentUpon>%(Filename)</DependentUpon> - </Compile> - <Compile Update="MainWindow.GCode.cs"> - <DependentUpon>%(Filename)</DependentUpon> - </Compile> - <Compile Update="MainWindow.LayerPreview.cs"> - <DependentUpon>%(Filename)</DependentUpon> - </Compile> - <Compile Update="MainWindow.Log.cs"> + <Compile Update="**\*.xaml.cs"> <DependentUpon>%(Filename)</DependentUpon> </Compile> - <Compile Update="MainWindow.Issues.cs"> - <DependentUpon>%(Filename)</DependentUpon> - </Compile> - <Compile Update="MainWindow.PixelEditor.cs"> - <DependentUpon>%(Filename)</DependentUpon> - </Compile> - </ItemGroup> - <ItemGroup> - <None Include="..\PrusaSlicer\**" CopyToOutputDirectory="PreserveNewest" LinkBase="Assets\PrusaSlicer" /> + <AvaloniaResource Include="**\*.xaml"> + <SubType>Designer</SubType> + </AvaloniaResource> + <AvaloniaResource Include="Assets\Icons\*" /> </ItemGroup> </Project> diff --git a/UVtools.WPF/Windows/AboutWindow.axaml b/UVtools.WPF/Windows/AboutWindow.axaml index 474155d..fe270f7 100644 --- a/UVtools.WPF/Windows/AboutWindow.axaml +++ b/UVtools.WPF/Windows/AboutWindow.axaml @@ -1,7 +1,8 @@ -<Window xmlns="https://github.com/avaloniaui" +<controls:WindowEx xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:controls="clr-namespace:UVtools.WPF.Controls" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="UVtools.WPF.Windows.AboutWindow" WindowStartupLocation="CenterOwner" @@ -104,4 +105,4 @@ </Border> </StackPanel> -</Window> +</controls:WindowEx> diff --git a/UVtools.WPF/Windows/BenchmarkWindow.axaml b/UVtools.WPF/Windows/BenchmarkWindow.axaml index 57b86ac..8af0308 100644 --- a/UVtools.WPF/Windows/BenchmarkWindow.axaml +++ b/UVtools.WPF/Windows/BenchmarkWindow.axaml @@ -1,7 +1,8 @@ -<Window xmlns="https://github.com/avaloniaui" +<controls:WindowEx xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:controls="clr-namespace:UVtools.WPF.Controls" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="UVtools.WPF.Windows.BenchmarkWindow" Title="Benchmark" @@ -111,6 +112,7 @@ <ProgressBar IsIndeterminate="{Binding IsRunning}" IsEnabled="{Binding IsRunning}" + VerticalAlignment="Stretch" /> <Button @@ -125,4 +127,4 @@ </Grid> </Grid> -</Window> +</controls:WindowEx> diff --git a/UVtools.WPF/Windows/ColorPickerWindow.axaml b/UVtools.WPF/Windows/ColorPickerWindow.axaml index cbbc563..47164ce 100644 --- a/UVtools.WPF/Windows/ColorPickerWindow.axaml +++ b/UVtools.WPF/Windows/ColorPickerWindow.axaml @@ -1,8 +1,9 @@ -<Window xmlns="https://github.com/avaloniaui" +<controls:WindowEx xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:cp="clr-namespace:ThemeEditor.Controls.ColorPicker;assembly=ThemeEditor.Controls.ColorPicker" + xmlns:controls="clr-namespace:UVtools.WPF.Controls" mc:Ignorable="d" d:DesignWidth="420" d:DesignHeight="420" WindowStartupLocation="CenterOwner" Width="420" @@ -38,4 +39,4 @@ </StackPanel> -</Window> +</controls:WindowEx> diff --git a/UVtools.WPF/Windows/ProgressWindow.axaml b/UVtools.WPF/Windows/ProgressWindow.axaml index c513795..6426d5d 100644 --- a/UVtools.WPF/Windows/ProgressWindow.axaml +++ b/UVtools.WPF/Windows/ProgressWindow.axaml @@ -1,14 +1,15 @@ -<Window xmlns="https://github.com/avaloniaui" +<controls:WindowEx xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:controls="clr-namespace:UVtools.WPF.Controls" mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="400" x:Class="UVtools.WPF.Windows.ProgressWindow" Title="ProgressWindow" CanResize="False" ShowInTaskbar="False" WindowStartupLocation="CenterOwner" - MinWidth="400" + MinWidth="450" SizeToContent="WidthAndHeight" SystemDecorations="BorderOnly" Icon="/Assets/Icons/UVtools.ico" @@ -34,22 +35,26 @@ <Grid Grid.Row="3" - RowDefinitions="30" ColumnDefinitions="*,Auto"> + RowDefinitions="30" ColumnDefinitions="*,100"> <ProgressBar Grid.Column="0" Minimum="0" Maximum="100" + VerticalAlignment="Stretch" IsIndeterminate="{Binding Progress.IsIndeterminate}" Value="{Binding Progress.ProgressPercent}" ShowProgressText="True"/> <Button IsEnabled="{Binding CanCancel}" Command="{Binding OnClickCancel}" Grid.Column="1" - Padding="30,0" IsCancel="True" + VerticalAlignment="Stretch" + HorizontalAlignment="Stretch" + VerticalContentAlignment="Center" + HorizontalContentAlignment="Center" Content="Cancel" /> </Grid> </Grid> </Border> -</Window> +</controls:WindowEx> diff --git a/UVtools.WPF/Windows/ProgressWindow.axaml.cs b/UVtools.WPF/Windows/ProgressWindow.axaml.cs index 79611e8..cbfa574 100644 --- a/UVtools.WPF/Windows/ProgressWindow.axaml.cs +++ b/UVtools.WPF/Windows/ProgressWindow.axaml.cs @@ -21,11 +21,13 @@ namespace UVtools.WPF.Windows public class ProgressWindow : WindowEx, IDisposable { public Stopwatch StopWatch => Progress.StopWatch; - public OperationProgress Progress { get; } = new OperationProgress(); + public OperationProgress Progress { get; } = new (); - private LogItem _logItem = new LogItem(); + private LogItem _logItem = new (); - private Timer _timer = new Timer(100) { AutoReset = true }; + private Timer _timer = new (200) { AutoReset = true }; + + private long _lastTotalSeconds = 0; public bool CanCancel { @@ -45,7 +47,16 @@ namespace UVtools.WPF.Windows _timer.Elapsed += (sender, args) => { - Progress.TriggerRefresh(); + var elapsedSeconds = StopWatch.ElapsedMilliseconds / 1000; + if (_lastTotalSeconds == elapsedSeconds) return; + /*Debug.WriteLine(StopWatch.ElapsedMilliseconds); + Debug.WriteLine(elapsedSeconds); + Debug.WriteLine(_lastTotalSeconds);*/ + _lastTotalSeconds = elapsedSeconds; + + + Dispatcher.UIThread.InvokeAsync(() => Progress.TriggerRefresh(), DispatcherPriority.Render); + }; DataContext = this; @@ -88,6 +99,7 @@ namespace UVtools.WPF.Windows { CanCancel = canCancel; Progress.StopWatch.Restart(); + _lastTotalSeconds = 0; if (!_timer.Enabled) { diff --git a/UVtools.WPF/Windows/PrusaSlicerManager.axaml b/UVtools.WPF/Windows/PrusaSlicerManager.axaml index c6e77f8..e9d6b1c 100644 --- a/UVtools.WPF/Windows/PrusaSlicerManager.axaml +++ b/UVtools.WPF/Windows/PrusaSlicerManager.axaml @@ -1,7 +1,8 @@ -<Window xmlns="https://github.com/avaloniaui" +<controls:WindowEx xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:controls="clr-namespace:UVtools.WPF.Controls" mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="700" x:Class="UVtools.WPF.Windows.PrusaSlicerManager" Title="Install profiles into PrusaSlicer" @@ -16,21 +17,18 @@ RowDefinitions="Auto" ColumnDefinitions="Auto,*"> - <Border - Margin="5,5,0,5" - BorderBrush="LightBlue" - BorderThickness="4" - > + <Border Classes="GroupBox" + Margin="5,5,0,5"> <StackPanel Orientation="Vertical"> - <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Legend"/> - <StackPanel Margin="15" Orientation="Vertical" Spacing="15"> + <TextBlock Classes="GroupBoxHeader" Text="Legend"/> + <StackPanel Margin="15" Orientation="Vertical" Spacing="10"> <StackPanel Orientation="Horizontal" Spacing="5"> <Border Width="18" Height="18" BorderBrush="Black" BorderThickness="2" - Background="Green"/> + Background="LightGray"/> <TextBlock VerticalAlignment="Center" @@ -71,13 +69,12 @@ <Border Grid.Column="1" + Classes="GroupBox" Margin="5" - BorderBrush="LightBlue" - BorderThickness="4" > <StackPanel Orientation="Vertical"> - <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Information"/> + <TextBlock Classes="GroupBoxHeader" Text="Information"/> <TextBlock TextWrapping="Wrap" Margin="15" @@ -90,10 +87,7 @@ </Grid> <Border DockPanel.Dock="Bottom" - Background="LightGray" - Height="80" - Padding="5,20,0,5" - > + Classes="FooterActions"> <Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto"> <Button @@ -143,18 +137,27 @@ <Border Grid.Column="0" + Classes="GroupBox" Margin="5" - BorderBrush="LightBlue" - BorderThickness="4" > <Grid RowDefinitions="Auto,Auto,*"> - <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Print profiles"/> + <TextBlock Classes="GroupBoxHeader" Text="Print profiles"/> + <TextBlock HorizontalAlignment="Right" + Padding="10"> + <TextBlock.Text> + <MultiBinding StringFormat="\{0\} Update(s) | {1} Installed | {2} Profiles"> + <Binding Path="Profiles[0].Updates"/> + <Binding Path="Profiles[0].Installed"/> + <Binding Path="Profiles[0].Items.Count"/> + </MultiBinding> + </TextBlock.Text> + </TextBlock> <StackPanel Grid.Row="1" - Orientation="Horizontal" Spacing="5"> + Orientation="Horizontal" Spacing="1"> <Button Padding="10" IsEnabled="{Binding Profiles[0].Updates}" Command="{Binding Profiles[0].SelectNone}" @@ -177,39 +180,6 @@ </StackPanel> - <StackPanel - Grid.Row="1" - HorizontalAlignment="Right" - Orientation="Horizontal" - Spacing="5"> - - <TextBlock - VerticalAlignment="Center" - Text="{Binding Profiles[0].Updates, - StringFormat=\{0\} Update(s)}"/> - - <TextBlock - VerticalAlignment="Center" - Text="|"/> - - <TextBlock - VerticalAlignment="Center" - Text="{Binding Profiles[0].Installed, - StringFormat=\{0\} Installed}"/> - - <TextBlock - VerticalAlignment="Center" - Text="|"/> - - <TextBlock - Margin="0,0,5,0" - VerticalAlignment="Center" - Text="{Binding Profiles[0].Items.Count, - StringFormat=\{0\} Profiles}"/> - - - </StackPanel> - <ListBox Grid.Row="2" SelectionMode="Toggle" @@ -220,18 +190,27 @@ <Border Grid.Column="1" - Margin="5" - BorderBrush="LightBlue" - BorderThickness="4" + Classes="GroupBox" + Margin="0,5,5,5" > <Grid RowDefinitions="Auto,Auto,*"> - <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Printer profiles"/> + <TextBlock Classes="GroupBoxHeader" Text="Printer profiles"/> + <TextBlock HorizontalAlignment="Right" + Padding="10"> + <TextBlock.Text> + <MultiBinding StringFormat="\{0\} Update(s) | {1} Installed | {2} Profiles"> + <Binding Path="Profiles[1].Updates"/> + <Binding Path="Profiles[1].Installed"/> + <Binding Path="Profiles[1].Items.Count"/> + </MultiBinding> + </TextBlock.Text> + </TextBlock> <StackPanel Grid.Row="1" - Orientation="Horizontal" Spacing="5"> + Orientation="Horizontal" Spacing="1"> <Button Padding="10" IsEnabled="{Binding Profiles[1].Updates}" Command="{Binding Profiles[1].SelectNone}" @@ -254,39 +233,6 @@ </StackPanel> - <StackPanel - Grid.Row="1" - HorizontalAlignment="Right" - Orientation="Horizontal" - Spacing="5"> - - <TextBlock - VerticalAlignment="Center" - Text="{Binding Profiles[1].Updates, - StringFormat=\{0\} Update(s)}"/> - - <TextBlock - VerticalAlignment="Center" - Text="|"/> - - <TextBlock - VerticalAlignment="Center" - Text="{Binding Profiles[1].Installed, - StringFormat=\{0\} Installed}"/> - - <TextBlock - VerticalAlignment="Center" - Text="|"/> - - <TextBlock - Margin="0,0,5,0" - VerticalAlignment="Center" - Text="{Binding Profiles[1].Items.Count, - StringFormat=\{0\} Profiles}"/> - - - </StackPanel> - <ListBox Grid.Row="2" SelectionMode="Toggle" @@ -296,4 +242,4 @@ </Border> </Grid> </DockPanel> -</Window> +</controls:WindowEx> diff --git a/UVtools.WPF/Windows/SettingsWindow.axaml b/UVtools.WPF/Windows/SettingsWindow.axaml index db86adf..63be73b 100644 --- a/UVtools.WPF/Windows/SettingsWindow.axaml +++ b/UVtools.WPF/Windows/SettingsWindow.axaml @@ -1,13 +1,13 @@ -<Window xmlns="https://github.com/avaloniaui" +<controls:WindowEx xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:cp="clr-namespace:ThemeEditor.Controls.ColorPicker;assembly=ThemeEditor.Controls.ColorPicker" + xmlns:controls="clr-namespace:UVtools.WPF.Controls" mc:Ignorable="d" d:DesignWidth="700" d:DesignHeight="450" x:Class="UVtools.WPF.Windows.SettingsWindow" WindowStartupLocation="CenterOwner" MinWidth="600" - Width="700" + Width="740" SizeToContent="Height" CanResize="False" Title="UVtools - Settings" @@ -18,13 +18,12 @@ <ScrollViewer MaxHeight="{Binding ScrollViewerMaxHeight}"> <StackPanel Orientation="Vertical" Spacing="5"> <Border - Margin="5" - BorderBrush="LightBlue" - BorderThickness="4"> + Classes="GroupBox" + Margin="5"> <StackPanel Orientation="Vertical"> <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Startup"/> - <StackPanel Margin="15" Orientation="Vertical" Spacing="15"> + <StackPanel Margin="10" Orientation="Vertical" Spacing="10"> <CheckBox IsChecked="{Binding Settings.General.StartMaximized}" Content="Start maximized"/> <CheckBox IsChecked="{Binding Settings.General.CheckForUpdatesOnStartup}" Content="Check for updates on startup"/> <CheckBox IsChecked="{Binding Settings.General.LoadDemoFileOnStartup}" Content="Loads a demo file on startup if no file was specified"/> @@ -33,19 +32,17 @@ </StackPanel> </Border> - <Border - Margin="5" - BorderBrush="LightBlue" - BorderThickness="4" - > + <Border + Classes="GroupBox" + Margin="5"> <StackPanel Orientation="Vertical"> <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Windows / dialogs"/> - <StackPanel Margin="15" Orientation="Vertical" Spacing="15"> + <StackPanel Margin="10" Orientation="Vertical" Spacing="10"> <CheckBox IsChecked="{Binding Settings.General.WindowsTakeIntoAccountScreenScaling}" Content="Take into account the screen scale factor to limit the dialogs windows maximum size"/> <Grid RowDefinitions="Auto,10,Auto" - ColumnDefinitions="Auto,10,100,5,Auto"> + ColumnDefinitions="Auto,10,150,5,Auto"> <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" @@ -89,14 +86,12 @@ </Border> <Border - Margin="5" - BorderBrush="LightBlue" - BorderThickness="4" - > + Classes="GroupBox" + Margin="5"> <StackPanel Orientation="Vertical"> <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="File dialog"/> - <Grid Margin="15" RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto" ColumnDefinitions="Auto,10,*,Auto,Auto"> + <Grid Margin="10" RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto" ColumnDefinitions="Auto,10,*,Auto,Auto"> <TextBlock VerticalAlignment="Center" Grid.Row="0" Grid.Column="0" @@ -104,8 +99,8 @@ <ComboBox Items="{Binding FileOpenDialogFilters}" SelectedIndex="{Binding Settings.General.DefaultOpenFileExtensionIndex}" HorizontalAlignment="Left" - Width="435" - MaxDropDownHeight="300" + Width="473" + MaxDropDownHeight="350" Grid.ColumnSpan="3" Grid.Row="0" Grid.Column="2"/> <TextBlock VerticalAlignment="Center" @@ -116,11 +111,15 @@ Text="{Binding Settings.General.DefaultDirectoryOpenFile}" Grid.Row="2" Grid.Column="2" IsReadOnly="True"/> <Button Grid.Row="2" Grid.Column="3" + VerticalAlignment="Stretch" + HorizontalAlignment="Stretch" Command="{Binding GeneralOpenFolderField}" CommandParameter="DefaultDirectoryOpenFile"> <Image Source="/Assets/Icons/open-16x16.png"/> </Button> <Button Grid.Row="2" Grid.Column="4" + VerticalAlignment="Stretch" + HorizontalAlignment="Stretch" Command="{Binding GeneralClearField}" CommandParameter="DefaultDirectoryOpenFile"> <Image Source="/Assets/Icons/delete-16x16.png"/> @@ -134,11 +133,15 @@ Text="{Binding Settings.General.DefaultDirectorySaveFile}" Grid.Row="4" Grid.Column="2" IsReadOnly="True"/> <Button Grid.Row="4" Grid.Column="3" + VerticalAlignment="Stretch" + HorizontalAlignment="Stretch" Command="{Binding GeneralOpenFolderField}" CommandParameter="DefaultDirectorySaveFile"> <Image Source="/Assets/Icons/open-16x16.png"/> </Button> <Button Grid.Row="4" Grid.Column="4" + VerticalAlignment="Stretch" + HorizontalAlignment="Stretch" Command="{Binding GeneralClearField}" CommandParameter="DefaultDirectorySaveFile"> <Image Source="/Assets/Icons/delete-16x16.png"/> @@ -152,11 +155,15 @@ Text="{Binding Settings.General.DefaultDirectoryExtractFile}" Grid.Row="6" Grid.Column="2" IsReadOnly="True"/> <Button Grid.Row="6" Grid.Column="3" + VerticalAlignment="Stretch" + HorizontalAlignment="Stretch" Command="{Binding GeneralOpenFolderField}" CommandParameter="DefaultDirectoryExtractFile"> <Image Source="/Assets/Icons/open-16x16.png"/> </Button> <Button Grid.Row="6" Grid.Column="4" + VerticalAlignment="Stretch" + HorizontalAlignment="Stretch" Command="{Binding GeneralClearField}" CommandParameter="DefaultDirectoryExtractFile"> <Image Source="/Assets/Icons/delete-16x16.png"/> @@ -170,6 +177,8 @@ Text="{Binding Settings.General.DefaultDirectoryConvertFile}" Grid.Row="8" Grid.Column="2" IsReadOnly="True"/> <Button Grid.Row="8" Grid.Column="3" + VerticalAlignment="Stretch" + HorizontalAlignment="Stretch" Command="{Binding GeneralOpenFolderField}" CommandParameter="DefaultDirectoryConvertFile"> <Image Source="/Assets/Icons/open-16x16.png"/> @@ -177,14 +186,16 @@ <Button Command="{Binding GeneralClearField}" CommandParameter="DefaultDirectoryConvertFile" + VerticalAlignment="Stretch" + HorizontalAlignment="Stretch" Grid.Row="8" Grid.Column="4"> <Image Source="/Assets/Icons/delete-16x16.png"/> </Button> </Grid> - <CheckBox IsChecked="{Binding Settings.General.PromptOverwriteFileSave}" Margin="15,0" Content="On file 'Save' prompt for file overwrite for the first time" /> + <CheckBox IsChecked="{Binding Settings.General.PromptOverwriteFileSave}" Margin="10,0" Content="On file 'Save' prompt for file overwrite for the first time" /> - <Grid Margin="15" ColumnDefinitions="Auto,*,Auto,*"> + <Grid Margin="10" ColumnDefinitions="Auto,*,Auto,*"> <TextBlock VerticalAlignment="Center" Grid.Column="0" Text="File 'Save as' prefix:"/> <TextBox Text="{Binding Settings.General.FileSaveNamePrefix}" Grid.Column="1" Margin="10,0,10,0"/> @@ -205,14 +216,13 @@ <ScrollViewer MaxHeight="{Binding ScrollViewerMaxHeight}"> <StackPanel Orientation="Vertical" Spacing="5"> <Border - Margin="5" - BorderBrush="LightBlue" - BorderThickness="4"> + Classes="GroupBox" + Margin="5"> <StackPanel Orientation="Vertical"> <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Layer colors"/> - <Grid Margin="15" RowDefinitions="Auto,10,Auto,10,Auto,10,Auto" ColumnDefinitions="Auto,Auto,70,Auto,*"> + <Grid Margin="10" RowDefinitions="Auto,10,Auto,10,Auto,10,Auto" ColumnDefinitions="Auto,Auto,Auto,Auto,*"> <!--Tooltip overlay--> <TextBlock Grid.Row="0" Grid.Column="0" @@ -343,7 +353,7 @@ </Grid> - <Grid Margin="15,0,15,15" RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto" ColumnDefinitions="Auto,Auto,*,Auto,Auto"> + <Grid Margin="10,0,10,10" RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto" ColumnDefinitions="Auto,Auto,*,Auto,Auto"> <!--Previous layer difference & Island--> <TextBlock Grid.Row="0" Grid.Column="0" @@ -515,13 +525,12 @@ <!--Zoom--> <Border - Margin="5" - BorderBrush="LightBlue" - BorderThickness="4"> + Classes="GroupBox" + Margin="5"> <StackPanel Orientation="Vertical"> <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Zoom"/> - <Grid Margin="15" RowDefinitions="Auto" ColumnDefinitions="Auto,200,*,70"> + <Grid Margin="10" RowDefinitions="Auto" ColumnDefinitions="Auto,250,*,100"> <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Text="Zoom out to fit:"/> @@ -530,7 +539,7 @@ Margin="10,0,0,0" VerticalAlignment="Center" SelectedIndex="{Binding Settings.LayerPreview.ZoomToFitPrintVolumeBounds}" - > + HorizontalAlignment="Stretch"> <ComboBox.Items> <ComboBoxItem Content="Print Volume Boundary" /> <ComboBoxItem Content="Layer Boundary" /> @@ -549,12 +558,13 @@ VerticalAlignment="Center" Items="{Binding ZoomRanges}" SelectedIndex="{Binding Settings.LayerPreview.ZoomLockLevelIndex}" + HorizontalAlignment="Stretch" /> </Grid> <CheckBox - Margin="15,0,15,15" + Margin="10,0,10,10" Content="Auto zoom & center issues on selection" IsChecked="{Binding Settings.LayerPreview.ZoomIssues}" /> @@ -564,13 +574,12 @@ <!--Crosshairs--> <Border - Margin="5" - BorderBrush="LightBlue" - BorderThickness="4"> + Classes="GroupBox" + Margin="5"> <StackPanel Orientation="Vertical"> <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Crosshairs"/> - <Grid Margin="15" RowDefinitions="Auto,10,Auto" ColumnDefinitions="Auto,100,*,Auto,Auto"> + <Grid Margin="10" RowDefinitions="Auto,10,Auto" ColumnDefinitions="Auto,150,*,Auto,Auto"> <CheckBox Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" VerticalAlignment="Center" Content="Show crosshairs for selected issues only" @@ -634,16 +643,15 @@ </Border> <Border - Margin="5" - BorderBrush="LightBlue" - BorderThickness="4"> + Classes="GroupBox" + Margin="5"> <StackPanel Orientation="Vertical"> <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Miscellaneous"/> <StackPanel Orientation="Horizontal"> <CheckBox - Margin="15" + Margin="10,10" Content="Auto rotate on load" IsChecked="{Binding Settings.LayerPreview.AutoRotateLayerBestView}" /> @@ -656,7 +664,7 @@ </StackPanel> <CheckBox - Margin="15,0,15,15" + Margin="10,0,10,10" Content="Show square grid as background " IsChecked="{Binding Settings.LayerPreview.ShowBackgroudGrid}" /> @@ -672,24 +680,23 @@ <ScrollViewer MaxHeight="{Binding ScrollViewerMaxHeight}"> <StackPanel Orientation="Vertical"> <Border - Margin="5" - BorderBrush="LightBlue" - BorderThickness="4"> + Classes="GroupBox" + Margin="5"> <StackPanel Orientation="Vertical"> <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Common"/> - <CheckBox Margin="15" Content="Compute issues on file load" + <CheckBox Margin="10,10" Content="Compute issues on file load" IsChecked="{Binding Settings.Issues.ComputeIssuesOnLoad}" /> - <CheckBox Margin="15,0,15,0" Content="Auto compute issues when the Issues tab is opened for the first time" + <CheckBox Margin="10,0,10,0" Content="Auto compute issues when the Issues tab is opened for the first time" IsChecked="{Binding Settings.Issues.ComputeIssuesOnClickTab}" /> - <StackPanel Orientation="Horizontal" Margin="15" Spacing="15"> - <TextBlock Text="Compute:"/> + <StackPanel Orientation="Horizontal" Margin="10" Spacing="10"> + <TextBlock Text="Compute:" VerticalAlignment="Center"/> <CheckBox Content="Islands" IsChecked="{Binding Settings.Issues.ComputeIslands}"/> <CheckBox Content="Overhangs" @@ -706,14 +713,13 @@ </Border> <Border - Margin="5" - BorderBrush="LightBlue" - BorderThickness="4"> + Classes="GroupBox" + Margin="5"> <StackPanel Orientation="Vertical"> <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Islands"/> - <CheckBox Margin="15" + <CheckBox Margin="10,10" Content="Enhance island detection with an combined overhang check" IsChecked="{Binding Settings.Issues.IslandEnhancedDetection}" ToolTip.Tip="Combines the island and overhang detections for a better and more realistic detection, also to discard false-positives. (Slower) @@ -721,7 +727,7 @@ 
Note: Overhangs settings will be used to configure the detection. Enabling Overhangs is not required for this procedure to work." /> - <CheckBox Margin="15,0,15,15" + <CheckBox Margin="10,0,10,10" Content="Allow diagonal bonds during island detection" IsChecked="{Binding Settings.Issues.IslandAllowDiagonalBonds}" ToolTip.Tip="If enabled, components touching by even a single diagonal bond will be considered a single component for the purposes of island detection. @@ -729,7 +735,7 @@ /> - <StackPanel Orientation="Horizontal" Margin="15,0,15,15" Spacing="10"> + <StackPanel Orientation="Horizontal" Margin="10,0,10,10" Spacing="10"> <NumericUpDown Width="60" ClipValueToMinMax="True" Minimum="0" @@ -745,7 +751,7 @@ /> </StackPanel> - <StackPanel Orientation="Horizontal" Margin="15,0,15,15" Spacing="10"> + <StackPanel Orientation="Horizontal" Margin="10,0,10,10" Spacing="10"> <NumericUpDown Width="60" ClipValueToMinMax="True" Minimum="1" @@ -759,7 +765,7 @@ /> </StackPanel> - <StackPanel Orientation="Horizontal" Margin="15,0,15,15" Spacing="10"> + <StackPanel Orientation="Horizontal" Margin="10,0,10,10" Spacing="10"> <NumericUpDown Width="60" ClipValueToMinMax="True" Minimum="1" @@ -773,7 +779,7 @@ /> </StackPanel> - <StackPanel Orientation="Horizontal" Margin="15,0,15,15" Spacing="10"> + <StackPanel Orientation="Horizontal" Margin="10,0,10,10" Spacing="10"> <NumericUpDown Width="60" ClipValueToMinMax="True" Minimum="0.05" @@ -790,7 +796,7 @@ </StackPanel> - <StackPanel Orientation="Horizontal" Margin="15,0,15,15" Spacing="10"> + <StackPanel Orientation="Horizontal" Margin="10,0,10,10" Spacing="10"> <NumericUpDown Width="60" ClipValueToMinMax="True" Minimum="50" @@ -805,7 +811,7 @@ </StackPanel> <!-- - <StackPanel Orientation="Horizontal" Margin="15,0,15,15" Spacing="10"> + <StackPanel Orientation="Horizontal" Margin="10,0,10,10" Spacing="10"> <NumericUpDown Width="60" ClipValueToMinMax="True" Minimum="1" @@ -821,7 +827,7 @@ </StackPanel> - <StackPanel Orientation="Horizontal" Margin="15,0,15,15" Spacing="10"> + <StackPanel Orientation="Horizontal" Margin="10,0,10,10" Spacing="10"> <NumericUpDown Width="60" ClipValueToMinMax="True" Minimum="50" @@ -839,22 +845,21 @@ </Border> <Border - Margin="5" - BorderBrush="LightBlue" - BorderThickness="4"> + Classes="GroupBox" + Margin="5"> <StackPanel Orientation="Vertical"> <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Overhangs"/> <CheckBox - Margin="15" + Margin="10,10" Content="Include islands when detecting overhangs" ToolTip.Tip="If enabled, overhangs will be computed everywhere, including on islands. An overhang on an island will always be detected and reported. (Faster) 
If disabled, islands are disregarded when computing overhangs. An overhang that is on an island will never be detected or reported. (Slower) 
When island detection is disabled and overhang detection is enabled, overhangs will always be computed everywhere." IsChecked="{Binding Settings.Issues.OverhangIndependentFromIslands}"/> - <StackPanel Orientation="Horizontal" Margin="15,0,15,15" Spacing="10"> + <StackPanel Orientation="Horizontal" Margin="10,0,10,10" Spacing="10"> <NumericUpDown Width="60" ClipValueToMinMax="True" Minimum="2" @@ -872,14 +877,13 @@ </Border> <Border - Margin="5" - BorderBrush="LightBlue" - BorderThickness="4"> + Classes="GroupBox" + Margin="5"> <StackPanel Orientation="Vertical"> <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Resin traps"/> - <StackPanel Orientation="Horizontal" Margin="15" Spacing="10"> + <StackPanel Orientation="Horizontal" Margin="10" Spacing="10"> <NumericUpDown Width="60" ClipValueToMinMax="True" Minimum="1" @@ -896,7 +900,7 @@ </StackPanel> - <StackPanel Orientation="Horizontal" Margin="15,0,15,15" Spacing="10"> + <StackPanel Orientation="Horizontal" Margin="10,0,10,10" Spacing="10"> <NumericUpDown Width="60" ClipValueToMinMax="True" Minimum="1" @@ -910,7 +914,7 @@ /> </StackPanel> - <StackPanel Orientation="Horizontal" Margin="15,0,15,15" Spacing="10"> + <StackPanel Orientation="Horizontal" Margin="10,0,10,10" Spacing="10"> <NumericUpDown Width="60" ClipValueToMinMax="True" Minimum="1" @@ -924,7 +928,7 @@ /> </StackPanel> - <StackPanel Orientation="Horizontal" Margin="15,0,15,15" Spacing="10"> + <StackPanel Orientation="Horizontal" Margin="10,0,10,10" Spacing="10"> <NumericUpDown Width="60" ClipValueToMinMax="True" Minimum="1" @@ -941,14 +945,13 @@ </Border> <Border - Margin="5" - BorderBrush="LightBlue" - BorderThickness="4"> + Classes="GroupBox" + Margin="5"> <StackPanel Orientation="Vertical"> <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Touching boundary"/> - <StackPanel Orientation="Horizontal" Margin="15" Spacing="10"> + <StackPanel Orientation="Horizontal" Margin="10" Spacing="10"> <NumericUpDown Width="60" ClipValueToMinMax="True" Minimum="1" @@ -967,7 +970,7 @@ <Grid ColumnDefinitions="Auto,Auto,Auto,Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto" - Margin="15,0,15,15" + Margin="10,0,10,10" > <TextBlock VerticalAlignment="Center" @@ -1087,14 +1090,13 @@ <ScrollViewer MaxHeight="{Binding ScrollViewerMaxHeight}"> <StackPanel Orientation="Vertical"> <Border - Margin="5" - BorderBrush="LightBlue" - BorderThickness="4"> + Classes="GroupBox" + Margin="5"> <StackPanel Orientation="Vertical"> <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Colors & highlights"/> - <Grid Margin="15" RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto" ColumnDefinitions="Auto,Auto,Auto"> + <Grid Margin="10" RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto" ColumnDefinitions="Auto,Auto,Auto"> <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Text="Pixel add operation / (Selected):"/> @@ -1198,20 +1200,19 @@ </Border> <Border - Margin="5" - BorderBrush="LightBlue" - BorderThickness="4"> + Classes="GroupBox" + Margin="5"> <StackPanel Orientation="Vertical"> <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Actions"/> <CheckBox IsChecked="{Binding Settings.PixelEditor.PartialUpdateIslandsOnEditing}" - Margin="15" + Margin="10" Content="Refresh issues for modified layers"/> <CheckBox IsChecked="{Binding Settings.PixelEditor.CloseEditorOnApply}" - Margin="15,0,15,15" + Margin="10,0,10,10" Content="Close pixel editor after operations are applied"/> </StackPanel> </Border> @@ -1224,21 +1225,20 @@ <ScrollViewer MaxHeight="{Binding ScrollViewerMaxHeight}"> <StackPanel Orientation="Vertical"> <Border - Margin="5" - BorderBrush="LightBlue" - BorderThickness="4"> + Classes="GroupBox" + Margin="5"> - <StackPanel Orientation="Vertical" Spacing="15"> + <StackPanel Orientation="Vertical" Spacing="10"> <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Default values"/> <CheckBox IsChecked="{Binding Settings.LayerRepair.RepairIslands}" - Margin="15,0" Content="Attempt to repair islands by default"/> + Margin="10,0" Content="Attempt to repair islands by default"/> <CheckBox IsChecked="{Binding Settings.LayerRepair.RepairResinTraps}" - Margin="15,0" Content="Attempt to repair resin traps by default"/> + Margin="10,0" Content="Attempt to repair resin traps by default"/> <CheckBox IsChecked="{Binding Settings.LayerRepair.RemoveEmptyLayers}" - Margin="15,0" Content="Remove empty layers by default"/> + Margin="10,0" Content="Remove empty layers by default"/> - <StackPanel Orientation="Horizontal" Margin="15,0" Spacing="10"> + <StackPanel Orientation="Horizontal" Margin="10,0" Spacing="10"> <NumericUpDown Value="{Binding Settings.LayerRepair.RemoveIslandsBelowEqualPixels}" Width="70" @@ -1248,7 +1248,7 @@ <TextBlock VerticalAlignment="Center" Text="Default maximum pixel area for Island removal (0 = disable)"/> </StackPanel> - <StackPanel Orientation="Horizontal" Margin="15,0" Spacing="10"> + <StackPanel Orientation="Horizontal" Margin="10,0" Spacing="10"> <NumericUpDown Value="{Binding Settings.LayerRepair.RemoveIslandsRecursiveIterations}" Width="70" @@ -1258,7 +1258,7 @@ <TextBlock VerticalAlignment="Center" Text="Default maximum layers for recursive island removal (0 = All)"/> </StackPanel> - <StackPanel Orientation="Horizontal" Margin="15,0" Spacing="10"> + <StackPanel Orientation="Horizontal" Margin="10,0" Spacing="10"> <NumericUpDown Value="{Binding Settings.LayerRepair.ClosingIterations}" Width="70" @@ -1268,7 +1268,7 @@ <TextBlock VerticalAlignment="Center" Text="Default 'Gap Closing' iterations"/> </StackPanel> - <StackPanel Orientation="Horizontal" Margin="15,0,15,15" Spacing="10"> + <StackPanel Orientation="Horizontal" Margin="10,0,10,10" Spacing="10"> <NumericUpDown Value="{Binding Settings.LayerRepair.OpeningIterations}" Width="70" @@ -1289,14 +1289,12 @@ <ScrollViewer MaxHeight="{Binding ScrollViewerMaxHeight}"> <StackPanel Orientation="Vertical" Spacing="5"> <Border - Margin="5" - BorderBrush="LightBlue" - BorderThickness="4" - > + Classes="GroupBox" + Margin="5"> <StackPanel Orientation="Vertical"> <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Common"/> - <StackPanel Margin="15" Orientation="Vertical" Spacing="15"> + <StackPanel Margin="10" Orientation="Vertical" Spacing="10"> <CheckBox IsChecked="{Binding Settings.Automations.SaveFileAfterModifications}" Content="Auto save the file after apply any automation(s)"/> </StackPanel> @@ -1311,7 +1309,7 @@ <StackPanel Orientation="Vertical"> <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="PrusaSlicer SL1 files"/> - <StackPanel Margin="15" Orientation="Vertical" Spacing="15"> + <StackPanel Margin="10" Orientation="Vertical" Spacing="10"> <CheckBox IsChecked="{Binding Settings.Automations.AutoConvertSL1Files}" ToolTip.Tip="Converts SL1 files to the format specified on 'PrusaSlicer - Printer - Notes' on 'FILEFORMAT_XXX' variable. 
A new file with same name but a new extension will be created and overwrite any previous file. @@ -1323,23 +1321,22 @@ </Border> - <Border - Margin="5" - BorderBrush="LightBlue" - BorderThickness="4"> + <Border + Classes="GroupBox" + Margin="5"> <StackPanel Orientation="Vertical"> <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Light-off delay"/> - <CheckBox Margin="15,15,15,0" + <CheckBox Margin="10,10,10,0" Content="Change only light-off delay if value is zero" ToolTip.Tip="If enabled, it will only change light-off delay to the defined values if the original value is zero. 
If disabled, it will always change the light-off delay to the defined values." IsChecked="{Binding Settings.Automations.ChangeOnlyLightOffDelayIfZero}"/> - <Grid Margin="15" + <Grid Margin="10" RowDefinitions="Auto" - ColumnDefinitions="Auto,10,100,5,Auto,30,Auto,10,100,5,Auto"> + ColumnDefinitions="Auto,10,150,5,Auto,30,Auto,10,150,5,Auto"> <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" @@ -1352,7 +1349,7 @@ Minimum="0" Maximum="255" Increment="0.5" - FormatString="{}{0:0.00}" + FormatString="F02" Value="{Binding Settings.Automations.LightOffDelay}"/> <TextBlock Grid.Row="0" Grid.Column="4" VerticalAlignment="Center" @@ -1369,7 +1366,7 @@ Minimum="0" Maximum="255" Increment="0.5" - FormatString="{}{0:0.00}" + FormatString="F02" Value="{Binding Settings.Automations.BottomLightOffDelay}"/> <TextBlock Grid.Row="0" Grid.Column="10" VerticalAlignment="Center" @@ -1389,41 +1386,42 @@ </TabControl> - <Grid ColumnDefinitions="*,*" Background="LightGray"> + <Border Classes="FooterActions"> + <Grid ColumnDefinitions="*,*"> - <StackPanel Grid.Column="0" Orientation="Horizontal" Margin="10"> - <Button Name="Actions.ResetAllSettings" Padding="10" - Command="{Binding OnClickResetAllDefaults}"> - <StackPanel Orientation="Horizontal"> - <Image Source="/Assets/Icons/undo-alt-16x16.png"/> - <TextBlock Margin="10,0,0,0">Reset all settings</TextBlock> - </StackPanel> - </Button> - </StackPanel> + <StackPanel Grid.Column="0" Orientation="Horizontal"> + <Button Name="Actions.ResetAllSettings" Padding="10" + Command="{Binding OnClickResetAllDefaults}"> + <StackPanel Orientation="Horizontal"> + <Image Source="/Assets/Icons/undo-alt-16x16.png"/> + <TextBlock Margin="10,0,0,0">Reset all settings</TextBlock> + </StackPanel> + </Button> + </StackPanel> - <StackPanel Grid.Column="1" - HorizontalAlignment="Right" - VerticalAlignment="Center" - Orientation="Horizontal" - Margin="10" - > - <Button Name="Actions.Save" Margin="0,0,10,0" Padding="10" IsDefault="True" - Command="{Binding OnClickSave}"> - <StackPanel Orientation="Horizontal"> - <Image Source="/Assets/Icons/save-16x16.png"/> - <TextBlock Margin="10,0,0,0">Save</TextBlock> - </StackPanel> - </Button> + <StackPanel Grid.Column="1" + HorizontalAlignment="Right" + VerticalAlignment="Center" + Orientation="Horizontal"> + <Button Name="Actions.Save" Margin="0,0,10,0" Padding="10" IsDefault="True" + Command="{Binding OnClickSave}"> + <StackPanel Orientation="Horizontal"> + <Image Source="/Assets/Icons/save-16x16.png"/> + <TextBlock Margin="10,0,0,0">Save</TextBlock> + </StackPanel> + </Button> - <Button Name="Actions.Cancel" Padding="10" IsCancel="True" - Command="{Binding Close}"> - <StackPanel Orientation="Horizontal"> - <Image Source="/Assets/Icons/exit-16x16.png"/> - <TextBlock Margin="10,0,0,0">Cancel</TextBlock> - </StackPanel> - </Button> - </StackPanel> - </Grid> - </StackPanel> + <Button Name="Actions.Cancel" Padding="10" IsCancel="True" + Command="{Binding Close}"> + <StackPanel Orientation="Horizontal"> + <Image Source="/Assets/Icons/exit-16x16.png"/> + <TextBlock Margin="10,0,0,0">Cancel</TextBlock> + </StackPanel> + </Button> + </StackPanel> + </Grid> + </Border> + + </StackPanel> - </Window> + </controls:WindowEx> diff --git a/UVtools.WPF/Windows/ToolWindow.axaml b/UVtools.WPF/Windows/ToolWindow.axaml index f14f07e..0a50fd0 100644 --- a/UVtools.WPF/Windows/ToolWindow.axaml +++ b/UVtools.WPF/Windows/ToolWindow.axaml @@ -1,7 +1,8 @@ -<Window xmlns="https://github.com/avaloniaui" +<controls:WindowEx xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:controls="clr-namespace:UVtools.WPF.Controls" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="UVtools.WPF.Windows.ToolWindow" CanResize="False" @@ -9,39 +10,30 @@ WindowStartupLocation="CenterOwner" Title="Tool" Icon="/Assets/Icons/UVtools.ico"> - <StackPanel Orientation="Vertical"> + + <StackPanel Orientation="Vertical"> <!-- Description --> <Border Background="WhiteSmoke" Padding="10" - IsVisible="{Binding Description, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" - > - <Panel> - <TextBox + IsVisible="{Binding Description, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"> + <TextBox Classes="TransparentReadOnly" HorizontalAlignment="Left" - TextWrapping="Wrap" MaxWidth="{Binding DescriptionMaxWidth}" FontSize="16" - IsReadOnly="True" - AcceptsReturn="True" - Background="Transparent" - CaretBrush="Transparent" - BorderBrush="Transparent" Watermark="Description:" UseFloatingWatermark="True" Padding="5" Width="Infinity" Text="{Binding Description}"/> - </Panel> </Border> <!-- Layer Range --> <Border Background="WhiteSmoke" + Classes="GroupBox" Margin="5" - BorderBrush="LightBlue" - BorderThickness="4" IsVisible="{Binding LayerRangeVisible}"> <StackPanel Orientation="Vertical"> @@ -65,7 +57,7 @@ <Grid RowDefinitions="Auto,5,Auto" - ColumnDefinitions="Auto,100,Auto,100,150" + ColumnDefinitions="Auto,150,Auto,150,Auto" Margin="15"> <TextBlock Grid.Row="0" @@ -107,6 +99,7 @@ Grid.Column="4" Margin="10,0,0,0" VerticalAlignment="Stretch" + VerticalContentAlignment="Center" Padding="10,0,10,0" Content="Select ⮟" Command="{Binding OpenContextMenu}" @@ -161,15 +154,16 @@ <TextBlock Grid.Row="2" Grid.Column="1" HorizontalAlignment="Center" - Text="{Binding LayerStartMM, StringFormat=(\{0\}mm)}" /> + Text="{Binding LayerStartMM, StringFormat=(\{0:F2\}mm)}" /> <TextBlock Grid.Row="2" Grid.Column="3" HorizontalAlignment="Center" - Text="{Binding LayerEndMM, StringFormat=(\{0\}mm)}" /> + Text="{Binding LayerEndMM, StringFormat=(\{0:F2\}mm)}" /> <TextBlock Grid.Row="2" Grid.Column="4" + Margin="10,0,0,0" HorizontalAlignment="Center" Text="{Binding LayerRangeCountStr}" /> @@ -180,18 +174,14 @@ <!-- ROI --> <Border + Classes="GroupBox" Background="WhiteSmoke" Margin="5" - BorderBrush="LightBlue" - BorderThickness="4" - IsVisible="{Binding IsROIVisible}" - > + IsVisible="{Binding IsROIVisible}"> <StackPanel Orientation="Vertical"> <TextBlock - Padding="10" - Background="LightBlue" - FontWeight="Bold" + Classes="GroupBoxHeader" Text="ROI - Region of interest"/> <TextBlock Margin="15" @@ -214,10 +204,9 @@ <!-- Profiles --> <Border + Classes="GroupBox" Background="WhiteSmoke" Margin="5" - BorderBrush="LightBlue" - BorderThickness="4" IsVisible="{Binding IsProfilesVisible}" > @@ -258,13 +247,14 @@ IsEnabled="{Binding Profiles.Count}" IsVisible="{Binding Profiles.Count}" SelectedItem="{Binding SelectedProfileItem}" + HorizontalAlignment="Stretch" Items="{Binding Profiles}" /> <Button Grid.Row="0" Grid.Column="2" Margin="0,0,0,10" - VerticalAlignment="Center" + VerticalAlignment="Stretch" ToolTip.Tip="Deselect the current profile" IsEnabled="{Binding SelectedProfileItem, Converter={x:Static ObjectConverters.IsNotNull}}" IsVisible="{Binding Profiles.Count}" @@ -276,8 +266,7 @@ Grid.Row="0" Grid.Column="4" Margin="0,0,0,10" - Width="24" - VerticalAlignment="Center" + VerticalAlignment="Stretch" FontWeight="Bold" ToolTip.Tip="Set the selected profile as default to load in with this dialog. 
Shift + click to clear the default profile." @@ -290,7 +279,7 @@ Grid.Row="0" Grid.Column="6" Margin="0,0,0,10" - VerticalAlignment="Center" + VerticalAlignment="Stretch" ToolTip.Tip="Remove the selected profile" IsEnabled="{Binding SelectedProfileItem, Converter={x:Static ObjectConverters.IsNotNull}}" IsVisible="{Binding Profiles.Count}" @@ -309,7 +298,7 @@ <Button Grid.Row="1" Grid.Column="2" - VerticalAlignment="Center" + VerticalAlignment="Stretch" ToolTip.Tip="Add a new profile with the current set values" IsEnabled="{Binding ButtonOkEnabled}" Command="{Binding AddProfile}" @@ -319,37 +308,19 @@ </Grid> - - <!-- - <StackPanel Spacing="20" Margin="15,0,15,15" Orientation="Horizontal"> - <CheckBox - Content="Clear ROI after perform the operation" - IsChecked="{Binding ClearROIAfterOperation}" - /> - <Button - Padding="5" - Content="Clear ROI" - Command="{Binding ClearROI}"/> - </StackPanel> - !--> - </StackPanel> </Border> <!-- Content --> <Border + Classes="GroupBox" IsVisible="{Binding IsContentVisible}" Background="WhiteSmoke" - Margin="5" - BorderBrush="LightBlue" - BorderThickness="4"> + Margin="5"> <StackPanel Orientation="Vertical"> - <TextBlock - Padding="10" - Background="LightBlue" - FontWeight="Bold" + <TextBlock Classes="GroupBoxHeader" Text="{Binding Title}"/> <ScrollViewer @@ -361,7 +332,7 @@ </Border> <!-- Actions --> - <Border Margin="0,10,0,0" Padding="5,20" Background="LightGray"> + <Border Classes="FooterActions"> <Grid ColumnDefinitions="*"> <StackPanel Spacing="10" Orientation="Horizontal"> <Button @@ -419,4 +390,4 @@ </Border> </StackPanel> -</Window> +</controls:WindowEx> diff --git a/UVtools.WPF/Windows/ToolWindow.axaml.cs b/UVtools.WPF/Windows/ToolWindow.axaml.cs index b5586c4..74f42cb 100644 --- a/UVtools.WPF/Windows/ToolWindow.axaml.cs +++ b/UVtools.WPF/Windows/ToolWindow.axaml.cs @@ -1,6 +1,5 @@ using System; using System.Collections.ObjectModel; -using System.Diagnostics; using System.Drawing; using Avalonia; using Avalonia.Controls; @@ -42,7 +41,7 @@ namespace UVtools.WPF.Windows private bool _clearRoiAfterOperation; private bool _isProfilesVisible; - private ObservableCollection<Operation> _profiles = new ObservableCollection<Operation>(); + private ObservableCollection<Operation> _profiles = new(); private Operation _selectedProfileItem; private string _profileText; @@ -155,7 +154,7 @@ namespace UVtools.WPF.Windows get { uint layerCount = (uint) Math.Max(0, (int)LayerIndexEnd - LayerIndexStart + 1); - return $"({layerCount} layers / {(decimal)App.SlicerFile.LayerHeight * layerCount}mm)"; + return $"({layerCount} layers / {Math.Round(App.SlicerFile.LayerHeight * layerCount, 2)}mm)"; } } @@ -291,6 +290,7 @@ namespace UVtools.WPF.Windows if (ToolControl is null) return; var operation = _selectedProfileItem.Clone(); operation.ProfileName = null; + operation.SlicerFile = App.SlicerFile; ToolControl.BaseOperation = operation; SelectLayers(operation.LayerRangeSelection); ToolControl.Callback(Callbacks.ProfileLoaded); @@ -499,7 +499,15 @@ namespace UVtools.WPF.Windows ButtonOkText = toolControl.BaseOperation.ButtonOkText; ButtonOkVisible = ButtonOkEnabled = toolControl.BaseOperation.HaveAction; - SelectLayers(toolControl.BaseOperation.StartLayerRangeSelection); + if (toolControl.BaseOperation.LayerIndexStart == 0 && toolControl.BaseOperation.LayerIndexEnd == 0) + { + SelectLayers(toolControl.BaseOperation.StartLayerRangeSelection); + } + else + { + LayerIndexStart = toolControl.BaseOperation.LayerIndexStart; + LayerIndexEnd = toolControl.BaseOperation.LayerIndexEnd; + } //RaisePropertyChanged(nameof(IsContentVisible)); //RaisePropertyChanged(nameof(IsROIVisible)); @@ -509,17 +517,19 @@ namespace UVtools.WPF.Windows var profiles = OperationProfiles.GetOperations(ToolControl.BaseOperation.GetType()); Profiles.AddRange(profiles); IsProfilesVisible = true; - } - foreach (var operation in Profiles) - { - if (operation.ProfileIsDefault) + foreach (var operation in Profiles) { - SelectedProfileItem = operation; - break; + if (operation.ProfileIsDefault) + { + SelectedProfileItem = operation; + break; + } } } + + // Ensure the description don't stretch window DispatcherTimer.Run(() => { |