diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4d77e43..83ac813 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,12 +1,33 @@
# Changelog
+## 04/05/2021 - v2.9.3
+
+- (Upgrade) AvaloniaUI from 0.10.2 to 0.10.3
+- (Change) PrusaSlicer printers: Set 'Light-Off Delay' and 'Bottom Light-Off Delay' to 0 as default to allow UVtools auto-compute the right value once open the file in
+- **Exposure time finder:**
+ - Optimize layers by merge same position/exposure layers
+ - Add a fence option to the zebra bars
+ - Add a option to pattern the loaded model and generate multiple exposures on that
+ - Allow to generate tests without the holes feature
+ - Prevent from using more chamfers than the base height
+ - Removed two of the largest holes
+ - Hide not so often used 'Multiple brightness' and 'Multiple layer height' by default
+ - Disable Anti-Aliasing by default
+ - Change 'Part margin' from 1.5mm to 2.0mm
+ - Change 'Multiple exposures - Bottom exposure step' default to 0
+ - Fix a error when generating tests with multiple exposures
+ - Fix some typos
+- **XYZ accuracy test:**
+ - Change 'Wall thickess' default from 2.5mm to 3.0mm
+- (Improvement) Allow compute print time without a lift sequence
+
## 30/04/2021 - v2.9.2
- (Upgrade) AvaloniaUI from 0.10 to 0.10.2
- (Remove) Unused assemblies
-- **Issues**
- - Improve the performance when loading big lists of issues into the DataGrid
- - Auto refresh issues on the vertical highlight tracker once cath a modification on the Issues list
+- **Issues:**
+ - Improve the performance when loading big lists of issues into the DataGrid
+ - Auto refresh issues on the vertical highlight tracker once cath a modification on the Issues list
- **Layer preview - Difference:**
- Layer difference will now only check the pixels inside the union of previous, current and next layer bounding rectangle, increasing the performance and speed
- Previous and next layer pixels if both exists was not showing with the configured color and using the next layer color instead
diff --git a/PrusaSlicer/printer/AnyCubic Photon Mono SE.ini b/PrusaSlicer/printer/AnyCubic Photon Mono SE.ini
index 5f7594b..3318815 100644
--- a/PrusaSlicer/printer/AnyCubic Photon Mono SE.ini
+++ b/PrusaSlicer/printer/AnyCubic Photon Mono SE.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_MONO_SE\nFILEFORMAT_PWMS\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0.5\nBottomLightOffDelay_2\nBottomLiftHeight_6\nLiftHeight_6\nBottomLiftSpeed_150\nLiftSpeed_150\nRetractSpeed_150\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_ANYCUBIC\nPRINTER_MODEL_PHOTON_MONO_SE\nFILEFORMAT_PWMS\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_6\nLiftHeight_6\nBottomLiftSpeed_150\nLiftSpeed_150\nRetractSpeed_150\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/AnyCubic Photon Mono X.ini b/PrusaSlicer/printer/AnyCubic Photon Mono X.ini
index 0a9a498..7c1c19c 100644
--- a/PrusaSlicer/printer/AnyCubic Photon Mono X.ini
+++ b/PrusaSlicer/printer/AnyCubic Photon Mono X.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_MONO_X\nFILEFORMAT_PWMX\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0.5\nBottomLightOffDelay_2\nBottomLiftHeight_8\nLiftHeight_8\nBottomLiftSpeed_120\nLiftSpeed_120\nRetractSpeed_120\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_ANYCUBIC\nPRINTER_MODEL_PHOTON_MONO_X\nFILEFORMAT_PWMX\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_8\nLiftHeight_8\nBottomLiftSpeed_120\nLiftSpeed_120\nRetractSpeed_120\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/AnyCubic Photon Mono.ini b/PrusaSlicer/printer/AnyCubic Photon Mono.ini
index a6a56b7..9df8b45 100644
--- a/PrusaSlicer/printer/AnyCubic Photon Mono.ini
+++ b/PrusaSlicer/printer/AnyCubic Photon Mono.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_MONO\nFILEFORMAT_PWMO\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0.5\nBottomLightOffDelay_2\nBottomLiftHeight_6\nLiftHeight_6\nBottomLiftSpeed_150\nLiftSpeed_150\nRetractSpeed_150\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_ANYCUBIC\nPRINTER_MODEL_PHOTON_MONO\nFILEFORMAT_PWMO\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_6\nLiftHeight_6\nBottomLiftSpeed_150\nLiftSpeed_150\nRetractSpeed_150\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/AnyCubic Photon S.ini b/PrusaSlicer/printer/AnyCubic Photon S.ini
index 6af11a5..e5fdb20 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\nLiftSpeed_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_0\nLiftHeight_6\nLiftSpeed_60\nRetractSpeed_150\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/AnyCubic Photon X.ini b/PrusaSlicer/printer/AnyCubic Photon X.ini
index e7954b6..963f5ee 100644
--- a/PrusaSlicer/printer/AnyCubic Photon X.ini
+++ b/PrusaSlicer/printer/AnyCubic Photon X.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_X\nFILEFORMAT_PWX\n\nSTART_CUSTOM_VALUES\nLightOffDelay_1\nBottomLightOffDelay_2\nBottomLiftHeight_8\nLiftHeight_8\nBottomLiftSpeed_150\nLiftSpeed_150\nRetractSpeed_150\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_ANYCUBIC\nPRINTER_MODEL_PHOTON_X\nFILEFORMAT_PWX\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_8\nLiftHeight_8\nBottomLiftSpeed_150\nLiftSpeed_150\nRetractSpeed_150\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/AnyCubic Photon Zero.ini b/PrusaSlicer/printer/AnyCubic Photon Zero.ini
index 7b64510..dde504d 100644
--- a/PrusaSlicer/printer/AnyCubic Photon Zero.ini
+++ b/PrusaSlicer/printer/AnyCubic Photon Zero.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_ZERO\nFILEFORMAT_PW0\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_ZERO\nFILEFORMAT_PW0\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nLiftHeight_6\nLiftingSpeed_60\nRetractSpeed_150\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/AnyCubic Photon.ini b/PrusaSlicer/printer/AnyCubic Photon.ini
index e71b8d3..3a09109 100644
--- a/PrusaSlicer/printer/AnyCubic Photon.ini
+++ b/PrusaSlicer/printer/AnyCubic Photon.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\nFILEFORMAT_PHOTON\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_2\nBottomLiftHeight_5\nLiftHeight_5\nBottomLiftSpeed_65\nLiftSpeed_65\nRetractSpeed_150\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_ANYCUBIC\nPRINTER_MODEL_PHOTON\nFILEFORMAT_PHOTON\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_5\nLiftHeight_5\nBottomLiftSpeed_65\nLiftSpeed_65\nRetractSpeed_150\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/Creality LD-002R.ini b/PrusaSlicer/printer/Creality LD-002R.ini
index 3605fff..d5ea06d 100644
--- a/PrusaSlicer/printer/Creality LD-002R.ini
+++ b/PrusaSlicer/printer/Creality LD-002R.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_CREALITY\nPRINTER_MODEL_LD_002R\nFILEFORMAT_CTB\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_2\nBottomLiftHeight_5\nLiftHeight_5\nBottomLiftSpeed_65\nLiftSpeed_65\nRetractSpeed_150\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_CREALITY\nPRINTER_MODEL_LD_002R\nFILEFORMAT_CTB\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_5\nLiftHeight_5\nBottomLiftSpeed_65\nLiftSpeed_65\nRetractSpeed_150\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/Longer Orange 10.ini b/PrusaSlicer/printer/Longer Orange 10.ini
index cedf3f5..9c20964 100644
--- a/PrusaSlicer/printer/Longer Orange 10.ini
+++ b/PrusaSlicer/printer/Longer Orange 10.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_LONGER\nPRINTER_MODEL_ORANGE10\nFILEFORMAT_LGS\n\nSTART_CUSTOM_VALUES\nLightOffDelay_1\nBottomLightOffDelay_1\nBottomLiftHeight_4\nLiftHeight_2\nBottomLiftSpeed_90\nLiftSpeed_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_LONGER\nPRINTER_MODEL_ORANGE10\nFILEFORMAT_LGS\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_4\nLiftHeight_2\nBottomLiftSpeed_90\nLiftSpeed_150\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/Longer Orange 30.ini b/PrusaSlicer/printer/Longer Orange 30.ini
index b5ee165..7a32e4b 100644
--- a/PrusaSlicer/printer/Longer Orange 30.ini
+++ b/PrusaSlicer/printer/Longer Orange 30.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_LONGER\nPRINTER_MODEL_ORANGE30\nFILEFORMAT_LGS30\n\nSTART_CUSTOM_VALUES\nLightOffDelay_1\nBottomLightOffDelay_1\nBottomLiftHeight_2\nLiftHeight_2\nBottomLiftSpeed_48\nLiftSpeed_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_LONGER\nPRINTER_MODEL_ORANGE30\nFILEFORMAT_LGS30\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_2\nLiftHeight_2\nBottomLiftSpeed_48\nLiftSpeed_150\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/Longer Orange4K.ini b/PrusaSlicer/printer/Longer Orange4K.ini
index 440ae51..b02931f 100644
--- a/PrusaSlicer/printer/Longer Orange4K.ini
+++ b/PrusaSlicer/printer/Longer Orange4K.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_LONGER\nPRINTER_MODEL_ORANGE30\nFILEFORMAT_LGS\n\nSTART_CUSTOM_VALUES\nLightOffDelay_1\nBottomLightOffDelay_1\nBottomLiftHeight_2\nLiftHeight_2\nBottomLiftSpeed_90\nLiftSpeed_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_LONGER\nPRINTER_MODEL_ORANGE30\nFILEFORMAT_LGS\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_2\nLiftHeight_2\nBottomLiftSpeed_90\nLiftSpeed_150\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/Peopoly Phenom XXL.ini b/PrusaSlicer/printer/Peopoly Phenom XXL.ini
index 50a551f..db192e5 100644
--- a/PrusaSlicer/printer/Peopoly Phenom XXL.ini
+++ b/PrusaSlicer/printer/Peopoly Phenom XXL.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_PEOPOLY\nPRINTER_MODEL_PHENOM_XXL\nFILEFORMAT_CTB\n\nSTART_CUSTOM_VALUES\nLightOffDelay_2\nBottomLightOffDelay_8\nBottomLiftHeight_15\nLiftHeight_14\nBottomLiftSpeed_32\nLiftSpeed_35\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_PEOPOLY\nPRINTER_MODEL_PHENOM_XXL\nFILEFORMAT_CTB\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_15\nLiftHeight_14\nBottomLiftSpeed_32\nLiftSpeed_35\nRetractSpeed_100\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/Phrozen Shuffle 16.ini b/PrusaSlicer/printer/Phrozen Shuffle 16.ini
index cc546b5..7417f45 100644
--- a/PrusaSlicer/printer/Phrozen Shuffle 16.ini
+++ b/PrusaSlicer/printer/Phrozen Shuffle 16.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_PHROZEN\nPRINTER_MODEL_SHUFFLE_16\nFILEFORMAT_CTB\n\nSTART_CUSTOM_VALUES\nLightOffDelay_15\nBottomLightOffDelay_15\nBottomLiftHeight_10\nLiftHeight_8\nBottomLiftSpeed_65\nLiftSpeed_65\nRetractSpeed_150\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_PHROZEN\nPRINTER_MODEL_SHUFFLE_16\nFILEFORMAT_CTB\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_10\nLiftHeight_8\nBottomLiftSpeed_65\nLiftSpeed_65\nRetractSpeed_150\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/Phrozen Shuffle 4K.ini b/PrusaSlicer/printer/Phrozen Shuffle 4K.ini
index bef8e0d..91e9e50 100644
--- a/PrusaSlicer/printer/Phrozen Shuffle 4K.ini
+++ b/PrusaSlicer/printer/Phrozen Shuffle 4K.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_PHROZEN\nPRINTER_MODEL_SHUFFLE_4K\nFILEFORMAT_ZIP\n\nSTART_CUSTOM_VALUES\nLightOffDelay_10\nBottomLightOffDelay_10\nBottomLiftHeight_6\nLiftHeight_5\nBottomLiftSpeed_100\nLiftSpeed_100\nRetractSpeed_150\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_PHROZEN\nPRINTER_MODEL_SHUFFLE_4K\nFILEFORMAT_ZIP\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_6\nLiftHeight_5\nBottomLiftSpeed_100\nLiftSpeed_100\nRetractSpeed_150\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/Phrozen Shuffle Lite.ini b/PrusaSlicer/printer/Phrozen Shuffle Lite.ini
index 8915387..abb2cf2 100644
--- a/PrusaSlicer/printer/Phrozen Shuffle Lite.ini
+++ b/PrusaSlicer/printer/Phrozen Shuffle Lite.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_PHROZEN\nPRINTER_MODEL_SHUFFLE_LITE\nFILEFORMAT_ZIP\n\nSTART_CUSTOM_VALUES\nLightOffDelay_7\nBottomLightOffDelay_7\nBottomLiftHeight_6\nLiftHeight_5\nBottomLiftSpeed_100\nLiftSpeed_100\nRetractSpeed_200\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_PHROZEN\nPRINTER_MODEL_SHUFFLE_LITE\nFILEFORMAT_ZIP\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_6\nLiftHeight_5\nBottomLiftSpeed_100\nLiftSpeed_100\nRetractSpeed_200\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/Phrozen Shuffle XL.ini b/PrusaSlicer/printer/Phrozen Shuffle XL.ini
index 8e3bf95..651461d 100644
--- a/PrusaSlicer/printer/Phrozen Shuffle XL.ini
+++ b/PrusaSlicer/printer/Phrozen Shuffle XL.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_PHROZEN\nPRINTER_MODEL_SHUFFLE_XL\nFILEFORMAT_ZIP\n\nSTART_CUSTOM_VALUES\nLightOffDelay_7\nBottomLightOffDelay_7\nBottomLiftHeight_8\nLiftHeight_6\nBottomLiftSpeed_100\nLiftSpeed_100\nRetractSpeed_150\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_PHROZEN\nPRINTER_MODEL_SHUFFLE_XL\nFILEFORMAT_ZIP\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_8\nLiftHeight_6\nBottomLiftSpeed_100\nLiftSpeed_100\nRetractSpeed_150\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/Phrozen Shuffle.ini b/PrusaSlicer/printer/Phrozen Shuffle.ini
index faa1ecb..43e99b7 100644
--- a/PrusaSlicer/printer/Phrozen Shuffle.ini
+++ b/PrusaSlicer/printer/Phrozen Shuffle.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_PHROZEN\nPRINTER_MODEL_SHUFFLE\nFILEFORMAT_ZIP\n\nSTART_CUSTOM_VALUES\nLightOffDelay_7\nBottomLightOffDelay_7\nBottomLiftHeight_6\nLiftHeight_5\nBottomLiftSpeed_100\nLiftSpeed_100\nRetractSpeed_200\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_PHROZEN\nPRINTER_MODEL_SHUFFLE\nFILEFORMAT_ZIP\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_6\nLiftHeight_5\nBottomLiftSpeed_100\nLiftSpeed_100\nRetractSpeed_200\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/Phrozen Sonic Mighty 4K.ini b/PrusaSlicer/printer/Phrozen Sonic Mighty 4K.ini
index dd9fd15..9fc0045 100644
--- a/PrusaSlicer/printer/Phrozen Sonic Mighty 4K.ini
+++ b/PrusaSlicer/printer/Phrozen Sonic Mighty 4K.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_PHROZEN\nPRINTER_MODEL_SONIC_MIGHTY_4K\nFILEFORMAT_CTB\n\nSTART_CUSTOM_VALUES\nLightOffDelay_11\nBottomLightOffDelay_11\nBottomLiftHeight_8\nLiftHeight_6\nBottomLiftSpeed_60\nLiftSpeed_60\nRetractSpeed_150\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_PHROZEN\nPRINTER_MODEL_SONIC_MIGHTY_4K\nFILEFORMAT_CTB\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_8\nLiftHeight_6\nBottomLiftSpeed_60\nLiftSpeed_60\nRetractSpeed_150\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/Phrozen Sonic.ini b/PrusaSlicer/printer/Phrozen Sonic.ini
index 8fe4087..cbfd4e4 100644
--- a/PrusaSlicer/printer/Phrozen Sonic.ini
+++ b/PrusaSlicer/printer/Phrozen Sonic.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_PHROZEN\nPRINTER_MODEL_SONIC\nFILEFORMAT_ZIP\n\nSTART_CUSTOM_VALUES\nLightOffDelay_6\nBottomLightOffDelay_6\nBottomLiftHeight_6\nLiftHeight_5\nBottomLiftSpeed_100\nLiftSpeed_100\nRetractSpeed_200\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_PHROZEN\nPRINTER_MODEL_SONIC\nFILEFORMAT_ZIP\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_6\nLiftHeight_5\nBottomLiftSpeed_100\nLiftSpeed_100\nRetractSpeed_200\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/Phrozen Transform.ini b/PrusaSlicer/printer/Phrozen Transform.ini
index 0674451..5d1dfea 100644
--- a/PrusaSlicer/printer/Phrozen Transform.ini
+++ b/PrusaSlicer/printer/Phrozen Transform.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_PHROZEN\nPRINTER_MODEL_TRANSFORM\nFILEFORMAT_ZIP\n\nSTART_CUSTOM_VALUES\nLightOffDelay_10\nBottomLightOffDelay_10\nBottomLiftHeight_10\nLiftHeight_8\nBottomLiftSpeed_65\nLiftSpeed_65\nRetractSpeed_150\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_PHROZEN\nPRINTER_MODEL_TRANSFORM\nFILEFORMAT_ZIP\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_10\nLiftHeight_8\nBottomLiftSpeed_65\nLiftSpeed_65\nRetractSpeed_150\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/Uniz IBEE.ini b/PrusaSlicer/printer/Uniz IBEE.ini
index d760053..2a7d1ca 100644
--- a/PrusaSlicer/printer/Uniz IBEE.ini
+++ b/PrusaSlicer/printer/Uniz IBEE.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_UNIZ\nPRINTER_MODEL_IBEE\nFILEFORMAT_ZCODE\n\nSTART_CUSTOM_VALUES\nLightOffDelay_2\nBottomLightOffDelay_4\nBottomLiftHeight_15\nLiftHeight_7\nBottomLiftSpeed_20\nLiftSpeed_80\nRetractSpeed_80\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_UNIZ\nPRINTER_MODEL_IBEE\nFILEFORMAT_ZCODE\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_15\nLiftHeight_7\nBottomLiftSpeed_20\nLiftSpeed_80\nRetractSpeed_80\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/Zortrax Inkspire.ini b/PrusaSlicer/printer/Zortrax Inkspire.ini
index 4b574ec..f35d066 100644
--- a/PrusaSlicer/printer/Zortrax Inkspire.ini
+++ b/PrusaSlicer/printer/Zortrax Inkspire.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_ZORTRAX\nPRINTER_MODEL_INKSPIRE\nFILEFORMAT_ZCODEX\n\nSTART_CUSTOM_VALUES\nLightOffDelay_5\nLiftHeight_5\nLiftSpeed_100\nRetractSpeed_100\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_ZORTRAX\nPRINTER_MODEL_INKSPIRE\nFILEFORMAT_ZCODEX\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nLiftHeight_5\nLiftSpeed_100\nRetractSpeed_100\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs
index 9ea852a..2806e43 100644
--- a/UVtools.Core/Extensions/EmguExtensions.cs
+++ b/UVtools.Core/Extensions/EmguExtensions.cs
@@ -220,6 +220,11 @@ namespace UVtools.Core.Extensions
return new byte[mat.GetLength()];
}
+ public static Mat New(this Mat mat)
+ {
+ return new(mat.Rows, mat.Cols, mat.Depth, mat.NumberOfChannels);
+ }
+
///
/// Clone this blanked (All zeros)
///
diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs
index 9b05d2e..ff2f5c3 100644
--- a/UVtools.Core/FileFormats/FileFormat.cs
+++ b/UVtools.Core/FileFormats/FileFormat.cs
@@ -915,11 +915,11 @@ namespace UVtools.Core.FileFormats
}
var lightOffDelay = OperationCalculator.LightOffDelayC.CalculateSeconds(layer.LiftHeight, layer.LiftSpeed, layer.RetractSpeed);
- time += layer.ExposureTime;
- if (lightOffDelay >= layer.LightOffDelay)
+ time += layer.ExposureTime + lightOffDelay;
+ /*if (lightOffDelay >= layer.LightOffDelay)
time += lightOffDelay;
else
- time += layer.LightOffDelay;
+ time += layer.LightOffDelay;*/
}
}
@@ -1043,18 +1043,19 @@ namespace UVtools.Core.FileFormats
{
if (SuppressRebuildProperties) return;
if (
- e.PropertyName == nameof(BottomLayerCount) ||
- e.PropertyName == nameof(BottomExposureTime) ||
- e.PropertyName == nameof(ExposureTime) ||
- e.PropertyName == nameof(BottomLightOffDelay) ||
- e.PropertyName == nameof(LightOffDelay) ||
- e.PropertyName == nameof(BottomLiftHeight) ||
- e.PropertyName == nameof(LiftHeight) ||
- e.PropertyName == nameof(BottomLiftSpeed) ||
- e.PropertyName == nameof(LiftSpeed) ||
- e.PropertyName == nameof(RetractSpeed) ||
- e.PropertyName == nameof(BottomLightPWM) ||
- e.PropertyName == nameof(LightPWM)
+ e.PropertyName
+ is nameof(BottomLayerCount)
+ or nameof(BottomExposureTime)
+ or nameof(ExposureTime)
+ or nameof(BottomLightOffDelay)
+ or nameof(LightOffDelay)
+ or nameof(BottomLiftHeight)
+ or nameof(LiftHeight)
+ or nameof(BottomLiftSpeed)
+ or nameof(LiftSpeed)
+ or nameof(RetractSpeed)
+ or nameof(BottomLightPWM)
+ or nameof(LightPWM)
)
{
LayerManager?.RebuildLayersProperties(false, e.PropertyName);
diff --git a/UVtools.Core/Layer/Layer.cs b/UVtools.Core/Layer/Layer.cs
index a5dc4e9..84df8fb 100644
--- a/UVtools.Core/Layer/Layer.cs
+++ b/UVtools.Core/Layer/Layer.cs
@@ -312,6 +312,8 @@ namespace UVtools.Core
}
}
+ public Mat LayerMatBoundingRectangle => new(LayerMat, BoundingRectangle);
+
///
/// Gets a new Brg image instance
///
diff --git a/UVtools.Core/Layer/LayerManager.cs b/UVtools.Core/Layer/LayerManager.cs
index 84ab961..2abd13e 100644
--- a/UVtools.Core/Layer/LayerManager.cs
+++ b/UVtools.Core/Layer/LayerManager.cs
@@ -14,7 +14,6 @@ using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
-using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Emgu.CV;
using Emgu.CV.CvEnum;
@@ -523,6 +522,7 @@ namespace UVtools.Core
if(zeroLightOffDelay) layer.LightOffDelay = 0;
}
SlicerFile?.RebuildGCode();
+ SlicerFile.PrintTime = SlicerFile.PrintTimeComputed;
}
public Rectangle GetBoundingRectangle(OperationProgress progress = null)
diff --git a/UVtools.Core/Objects/ExposureItem.cs b/UVtools.Core/Objects/ExposureItem.cs
index 8e889f5..6ab12ab 100644
--- a/UVtools.Core/Objects/ExposureItem.cs
+++ b/UVtools.Core/Objects/ExposureItem.cs
@@ -97,5 +97,10 @@ namespace UVtools.Core.Objects
if (exposureComparison != 0) return exposureComparison;
return _brightness.CompareTo(other._brightness);
}
+
+ public ExposureItem Clone()
+ {
+ return MemberwiseClone() as ExposureItem;
+ }
}
}
diff --git a/UVtools.Core/Operations/OperationCalculator.cs b/UVtools.Core/Operations/OperationCalculator.cs
index d4c0384..e683af4 100644
--- a/UVtools.Core/Operations/OperationCalculator.cs
+++ b/UVtools.Core/Operations/OperationCalculator.cs
@@ -287,7 +287,7 @@ namespace UVtools.Core.Operations
}
catch (Exception)
{
- return 0;
+ return extraWaitTime;
}
}
diff --git a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs
index e039b8a..8c886a6 100644
--- a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs
+++ b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs
@@ -7,11 +7,13 @@
*/
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Linq;
using System.Text;
+using System.Threading.Tasks;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
@@ -48,17 +50,19 @@ namespace UVtools.Core.Operations
private byte _chamferLayers = 0;
private byte _erodeBottomIterations = 0;
private decimal _partMargin = 0;
- private bool _enableAntiAliasing = true;
+ private bool _enableAntiAliasing = false;
private bool _mirrorOutput;
private decimal _baseHeight = 1;
private decimal _featuresHeight = 1;
- private decimal _featuresMargin = 1.5m;
+ private decimal _featuresMargin = 2m;
private Measures _unitOfMeasure = Measures.Pixels;
- private string _holeDiametersPx = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13";
- private string _holeDiametersMm = "0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.2, 1.4";
+ private string _holeDiametersPx = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11";
+ private string _holeDiametersMm = "0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.2";
private decimal _barSpacing = 1.5m;
private decimal _barLength = 4;
private sbyte _barVerticalSplitter = 0;
+ private byte _barFenceThickness = 12;
+ private byte _barFenceOffset;
private string _barThicknessesPx = "4, 6, 8, 10, 12, 14, 16, 18, 20";
private string _barThicknessesMm = "0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.2";
private FontFace _textFont = TextMarkingFontFace;
@@ -81,7 +85,7 @@ namespace UVtools.Core.Operations
private bool _multipleExposures;
private ExposureGenTypes _exposureGenType = ExposureGenTypes.Linear;
private bool _exposureGenIgnoreBaseExposure;
- private decimal _exposureGenBottomStep = 0.5m;
+ private decimal _exposureGenBottomStep = 0;
private decimal _exposureGenNormalStep = 0.2m;
private byte _exposureGenTests = 4;
private decimal _exposureGenManualLayerHeight;
@@ -89,6 +93,8 @@ namespace UVtools.Core.Operations
private decimal _exposureGenManualNormal;
private RangeObservableCollection _exposureTable = new();
private CalibrateExposureFinderShapes _holeShape = CalibrateExposureFinderShapes.Square;
+ private bool _patternModel;
+ private bool _patternModelGlueBottomLayers = true;
#endregion
@@ -128,9 +134,14 @@ namespace UVtools.Core.Operations
sb.AppendLine("Display height must be a positive value.");
}
- if (Holes.Length <= 0)
+ if (!_patternModel && Bars.Length <= 0 && Holes.Length <= 0 && string.IsNullOrWhiteSpace(Text))
{
- sb.AppendLine("No objects to output.");
+ sb.AppendLine("No objects to output, enable at least 1 feature.");
+ }
+
+ if (_chamferLayers * _layerHeight > _baseHeight)
+ {
+ sb.AppendLine("The chamfer can't be higher than the base height, lower the chamfer layer count.");
}
if (_multipleExposures)
@@ -150,7 +161,7 @@ namespace UVtools.Core.Operations
}
}
if(!found)
- sb.AppendLine($"[ME]: {Layer.ShowHeight(layerHeight)}mm layer height have no exposure(s).");
+ sb.AppendLine($"[ME]: The {Layer.ShowHeight(layerHeight)}mm layer height have no set exposure(s).");
}
}
@@ -158,7 +169,20 @@ namespace UVtools.Core.Operations
{
if (MultipleBrightnessValuesArray.Length == 0)
{
- sb.AppendLine($"[MB]: Multiple brightness tests are enabled but no valid values are set, use from 1 to 255.");
+ sb.AppendLine($"Multiple brightness tests are enabled but no valid values are set, use from 1 to 255.");
+ }
+ }
+
+ if (_patternModel)
+ {
+ if (!CanPatternModel)
+ {
+ sb.AppendLine($"Unable to pattern the loaded model within the available space.");
+ }
+
+ if (!_multipleBrightness && !_multipleExposures)
+ {
+ sb.AppendLine($"Pattern the loaded model requires either multiple brightness or multiple exposures to use with.");
}
}
@@ -361,7 +385,7 @@ namespace UVtools.Core.Operations
if (string.IsNullOrWhiteSpace(mmStr)) continue;
if (!decimal.TryParse(mmStr, out var mm)) continue;
var mmPx = (int)Math.Floor(mm * Ppmm);
- if (mmPx <= 0 || mmPx > 500) continue;
+ if (mmPx is <= 0 or > 500) continue;
if(holes.Contains(mmPx)) continue;
holes.Add((int)Math.Floor(mm * Ppmm));
}
@@ -373,7 +397,7 @@ namespace UVtools.Core.Operations
{
if (string.IsNullOrWhiteSpace(pxStr)) continue;
if (!int.TryParse(pxStr, out var px)) continue;
- if (px <= 0 || px > 500) continue;
+ if (px is <= 0 or > 500) continue;
if (holes.Contains(px)) continue;
holes.Add(px);
}
@@ -385,6 +409,7 @@ namespace UVtools.Core.Operations
public int GetHolesLength(int[] holes)
{
+ if (holes.Length == 0) return 0;
return (int) (holes.Sum() + (holes.Length-1) * _featuresMargin * Yppmm);
}
@@ -406,6 +431,18 @@ namespace UVtools.Core.Operations
set => RaiseAndSetIfChanged(ref _barVerticalSplitter, value);
}
+ public byte BarFenceThickness
+ {
+ get => _barFenceThickness;
+ set => RaiseAndSetIfChanged(ref _barFenceThickness, value);
+ }
+
+ public byte BarFenceOffset
+ {
+ get => _barFenceOffset;
+ set => RaiseAndSetIfChanged(ref _barFenceOffset, value);
+ }
+
public string BarThicknessesPx
{
get => _barThicknessesPx;
@@ -447,7 +484,7 @@ namespace UVtools.Core.Operations
{
if (string.IsNullOrWhiteSpace(pxStr)) continue;
if (!int.TryParse(pxStr, out var px)) continue;
- if (px <= 0 || px > 500) continue;
+ if (px is <= 0 or > 500) continue;
if (bars.Contains(px)) continue;
bars.Add(px);
}
@@ -457,9 +494,15 @@ namespace UVtools.Core.Operations
}
}
- public int GetBarsWidth(int[] bars)
+ public int GetBarsLength(int[] bars)
{
- return (int)(bars.Sum() + (bars.Length + 1) * _barSpacing * Yppmm);
+ if (bars.Length == 0) return 0;
+ int len = (int) (bars.Sum() + (bars.Length + 1) * _barSpacing * Yppmm);
+ if (_barFenceThickness > 0)
+ {
+ len += _barFenceThickness * 2 + _barFenceOffset * 2;
+ }
+ return len;
}
public static Array TextFonts => Enum.GetValues(typeof(FontFace));
@@ -685,6 +728,30 @@ namespace UVtools.Core.Operations
set => RaiseAndSetIfChanged(ref _exposureTable, value);
}
+ public bool PatternModel
+ {
+ get => _patternModel;
+ set
+ {
+ if(!RaiseAndSetIfChanged(ref _patternModel, value)) return;
+ if (_patternModel)
+ {
+ LayerHeight = (decimal) SlicerFile.LayerHeight;
+ MultipleLayerHeight = false;
+ }
+ }
+ }
+
+ public bool PatternModelGlueBottomLayers
+ {
+ get => _patternModelGlueBottomLayers;
+ set => RaiseAndSetIfChanged(ref _patternModelGlueBottomLayers, value);
+ }
+
+
+ public bool CanPatternModel => SlicerFile.BoundingRectangle.Width * 2 + _leftRightMargin * 2 + _partMargin * Xppmm < SlicerFile.ResolutionX ||
+ SlicerFile.BoundingRectangle.Height * 2 + _topBottomMargin * 2 + _partMargin * Yppmm < SlicerFile.ResolutionY;
+
#endregion
#region Constructor
@@ -764,7 +831,7 @@ namespace UVtools.Core.Operations
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 && _featuresHeight == other._featuresHeight && _featuresMargin == other._featuresMargin && _unitOfMeasure == other._unitOfMeasure && _holeDiametersPx == other._holeDiametersPx && _holeDiametersMm == other._holeDiametersMm && _barSpacing == other._barSpacing && _barLength == other._barLength && _barVerticalSplitter == other._barVerticalSplitter && _barThicknessesPx == other._barThicknessesPx && _barThicknessesMm == other._barThicknessesMm && _textFont == other._textFont && _textScale.Equals(other._textScale) && _textThickness == other._textThickness && _text == other._text && _multipleBrightness == other._multipleBrightness && _multipleBrightnessExcludeFrom == other._multipleBrightnessExcludeFrom && _multipleBrightnessValues == other._multipleBrightnessValues && _dontLiftSamePositionedLayers == other._dontLiftSamePositionedLayers && _zeroLightOffSamePositionedLayers == other._zeroLightOffSamePositionedLayers && _multipleLayerHeight == other._multipleLayerHeight && _multipleLayerHeightMaximum == other._multipleLayerHeightMaximum && _multipleLayerHeightStep == other._multipleLayerHeightStep && _multipleExposures == other._multipleExposures && _exposureGenType == other._exposureGenType && _exposureGenIgnoreBaseExposure == other._exposureGenIgnoreBaseExposure && _exposureGenBottomStep == other._exposureGenBottomStep && _exposureGenNormalStep == other._exposureGenNormalStep && _exposureGenTests == other._exposureGenTests && Equals(_exposureTable, other._exposureTable) && _holeShape == other._holeShape;
+ return _displayWidth == other._displayWidth && _displayHeight == other._displayHeight && _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 && _featuresHeight == other._featuresHeight && _featuresMargin == other._featuresMargin && _unitOfMeasure == other._unitOfMeasure && _holeDiametersPx == other._holeDiametersPx && _holeDiametersMm == other._holeDiametersMm && _barSpacing == other._barSpacing && _barLength == other._barLength && _barVerticalSplitter == other._barVerticalSplitter && _barFenceThickness == other._barFenceThickness && _barFenceOffset == other._barFenceOffset && _barThicknessesPx == other._barThicknessesPx && _barThicknessesMm == other._barThicknessesMm && _textFont == other._textFont && _textScale.Equals(other._textScale) && _textThickness == other._textThickness && _text == other._text && _multipleBrightness == other._multipleBrightness && _multipleBrightnessExcludeFrom == other._multipleBrightnessExcludeFrom && _multipleBrightnessValues == other._multipleBrightnessValues && _multipleBrightnessGenExposureTime == other._multipleBrightnessGenExposureTime && _multipleLayerHeight == other._multipleLayerHeight && _multipleLayerHeightMaximum == other._multipleLayerHeightMaximum && _multipleLayerHeightStep == other._multipleLayerHeightStep && _dontLiftSamePositionedLayers == other._dontLiftSamePositionedLayers && _zeroLightOffSamePositionedLayers == other._zeroLightOffSamePositionedLayers && _multipleExposures == other._multipleExposures && _exposureGenType == other._exposureGenType && _exposureGenIgnoreBaseExposure == other._exposureGenIgnoreBaseExposure && _exposureGenBottomStep == other._exposureGenBottomStep && _exposureGenNormalStep == other._exposureGenNormalStep && _exposureGenTests == other._exposureGenTests && _exposureGenManualLayerHeight == other._exposureGenManualLayerHeight && _exposureGenManualBottom == other._exposureGenManualBottom && _exposureGenManualNormal == other._exposureGenManualNormal && Equals(_exposureTable, other._exposureTable) && _holeShape == other._holeShape && _patternModel == other._patternModel && _patternModelGlueBottomLayers == other._patternModelGlueBottomLayers;
}
public override bool Equals(object obj)
@@ -775,6 +842,8 @@ namespace UVtools.Core.Operations
public override int GetHashCode()
{
var hashCode = new HashCode();
+ hashCode.Add(_displayWidth);
+ hashCode.Add(_displayHeight);
hashCode.Add(_layerHeight);
hashCode.Add(_bottomLayers);
hashCode.Add(_bottomExposure);
@@ -795,6 +864,8 @@ namespace UVtools.Core.Operations
hashCode.Add(_barSpacing);
hashCode.Add(_barLength);
hashCode.Add(_barVerticalSplitter);
+ hashCode.Add(_barFenceThickness);
+ hashCode.Add(_barFenceOffset);
hashCode.Add(_barThicknessesPx);
hashCode.Add(_barThicknessesMm);
hashCode.Add((int) _textFont);
@@ -804,6 +875,7 @@ namespace UVtools.Core.Operations
hashCode.Add(_multipleBrightness);
hashCode.Add((int) _multipleBrightnessExcludeFrom);
hashCode.Add(_multipleBrightnessValues);
+ hashCode.Add(_multipleBrightnessGenExposureTime);
hashCode.Add(_multipleLayerHeight);
hashCode.Add(_multipleLayerHeightMaximum);
hashCode.Add(_multipleLayerHeightStep);
@@ -815,8 +887,13 @@ namespace UVtools.Core.Operations
hashCode.Add(_exposureGenBottomStep);
hashCode.Add(_exposureGenNormalStep);
hashCode.Add(_exposureGenTests);
+ hashCode.Add(_exposureGenManualLayerHeight);
+ hashCode.Add(_exposureGenManualBottom);
+ hashCode.Add(_exposureGenManualNormal);
hashCode.Add(_exposureTable);
hashCode.Add((int) _holeShape);
+ hashCode.Add(_patternModel);
+ hashCode.Add(_patternModelGlueBottomLayers);
return hashCode.ToHashCode();
}
@@ -831,9 +908,14 @@ namespace UVtools.Core.Operations
public void SanitizeExposureTable()
{
- List list = _exposureTable.ToList().Distinct().ToList();
+ _exposureTable.ReplaceCollection(GetSanitizedExposureTable());
+ }
+
+ public List GetSanitizedExposureTable()
+ {
+ var list = _exposureTable.ToList().Distinct().ToList();
list.Sort();
- _exposureTable.ReplaceCollection(list);
+ return list;
}
public void GenerateExposure()
@@ -875,17 +957,16 @@ namespace UVtools.Core.Operations
public Mat[] GetLayers()
{
var holes = Holes;
- if (holes.Length == 0) return null;
+ //if (holes.Length == 0) return null;
var bars = Bars;
var textSize = TextSize;
int featuresMarginX = (int)(Xppmm * _featuresMargin);
int featuresMarginY = (int)(Yppmm * _featuresMargin);
- int holePanelWidth = featuresMarginX * 2 + holes[^1];
-
-
- int yMaxSize = Math.Max(Math.Max(GetHolesLength(holes), GetBarsWidth(bars)), textSize.Width);
+ int holePanelWidth = holes.Length > 0 ? featuresMarginX * 2 + holes[^1] : featuresMarginX / 2;
+ int barsPanelHeight = GetBarsLength(bars);
+ int yMaxSize = Math.Max(Math.Max(GetHolesLength(holes), barsPanelHeight), textSize.Width);
int xSize = holePanelWidth * 2;
int ySize = featuresMarginX * 3 + yMaxSize + TextMarkingSpacing;
@@ -896,12 +977,13 @@ namespace UVtools.Core.Operations
if (bars.Length > 0)
{
barsPanelWidth = barLengthPx * 2 + _barVerticalSplitter;
+ if (_barFenceThickness > 0)
+ {
+ barsPanelWidth += _barFenceThickness * 2 + _barFenceOffset * 2;
+ }
xSize += barsPanelWidth + featuresMarginX;
-
}
-
-
if (!textSize.IsEmpty)
{
xSize += textSize.Height + featuresMarginX;
@@ -909,13 +991,18 @@ namespace UVtools.Core.Operations
int positiveSideWidth = xSize - holePanelWidth;
- Rectangle rect = new Rectangle(new Point(1, 1), new Size(xSize, ySize));
+ Rectangle rect = new(new Point(1, 1), new Size(xSize, ySize));
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].CloneBlank();
- CvInvoke.Rectangle(layers[1], new Rectangle(rect.Size.Width - holePanelWidth, 0, rect.Size.Width, layers[0].Height), EmguExtensions.WhiteByte, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ if (holes.Length > 0)
+ {
+ CvInvoke.Rectangle(layers[1],
+ new Rectangle(rect.Size.Width - holePanelWidth, 0, rect.Size.Width, layers[0].Height),
+ EmguExtensions.WhiteByte, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ }
// Print holes
int xPos = 0;
@@ -1028,7 +1115,10 @@ namespace UVtools.Core.Operations
// Print Zebra bars
if (bars.Length > 0)
{
- yPos = featuresMarginY;
+ int yStartPos = featuresMarginY;
+ int xStartPos = xPos;
+ yPos = yStartPos + _barFenceThickness / 2 + _barFenceOffset;
+ xPos += _barFenceThickness / 2 + _barFenceOffset;
for (int i = 0; i < bars.Length; i++)
{
// Print positive bottom
@@ -1045,13 +1135,27 @@ namespace UVtools.Core.Operations
CvInvoke.Rectangle(layers[1], new Rectangle(xPos, yPos, barLengthPx - 1, barSpacingPx - 1),
EmguExtensions.WhiteByte, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ yPos += barSpacingPx;
+
+ if (_barFenceThickness > 0)
+ {
+ CvInvoke.Rectangle(layers[1], new Rectangle(
+ xStartPos - 1,
+ yStartPos - 1,
+ barsPanelWidth - _barFenceThickness + 1,
+ yPos - yStartPos + _barFenceThickness / 2 + _barFenceOffset + 1),
+ EmguExtensions.WhiteByte, _barFenceThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+
+ yPos += _barFenceThickness * 2 + _barFenceOffset * 2;
+ }
+
xPos += featuresMarginX;
}
if (!textSize.IsEmpty)
{
CvInvoke.Rotate(layers[1], layers[1], RotateFlags.Rotate90CounterClockwise);
- CvInvoke.PutText(layers[1], _text, new Point(featuresMarginX, layers[1].Height - barsPanelWidth - xPos), _textFont, _textScale, EmguExtensions.WhiteByte, _textThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.PutText(layers[1], _text, new Point(featuresMarginX, layers[1].Height - barsPanelWidth - featuresMarginX * (barsPanelWidth > 0 ? 2 : 1)), _textFont, _textScale, EmguExtensions.WhiteByte, _textThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
CvInvoke.Rotate(layers[1], layers[1], RotateFlags.Rotate90Clockwise);
}
@@ -1116,205 +1220,396 @@ namespace UVtools.Core.Operations
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, $"Features: {Holes.Length+Bars.Length}", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness);
+ if (_patternModel)
+ {
+ CvInvoke.PutText(thumbnail, $"Patterned Model", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness);
+ }
+ else
+ {
+ CvInvoke.PutText(thumbnail, $"Features: {Holes.Length + Bars.Length}", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness);
+ }
+
return thumbnail;
}
protected override bool ExecuteInternally(OperationProgress progress)
{
- var layers = GetLayers();
- if (layers is null) return false;
- progress.ItemCount = 0;
- SanitizeExposureTable();
- if (layers[0].Width > SlicerFile.ResolutionX || layers[0].Height > SlicerFile.ResolutionY)
- {
- return false;
- }
-
- List newLayers = new();
-
- Dictionary 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 featuresMarginX = (int)(Xppmm * _featuresMargin);
- int featuresMarginY = (int)(Yppmm * _featuresMargin);
-
- var holes = Holes;
+ int partMarginXPx = (int) Math.Floor(_partMargin * Xppmm);
+ int partMarginYPx = (int) Math.Floor(_partMargin * Yppmm);
var anchor = new Point(-1, -1);
using var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor);
- var brightnesses = MultipleBrightnessValuesArray;
-
- if (brightnesses.Length == 0 || !_multipleBrightness) brightnesses = new[] {byte.MaxValue};
-
- void AddLayer(decimal currentHeight, decimal layerHeight, decimal bottomExposure, decimal normalExposure)
+ if (_patternModel)
{
- var layerDifference = currentHeight / layerHeight;
+ ConcurrentBag parallelLayers = new();
+ Dictionary table = new();
+ var boundingRectangle = SlicerFile.BoundingRectangle;
+ int xHalf = boundingRectangle.Width / 2;
+ int yHalf = boundingRectangle.Height / 2;
- if (!layerDifference.IsInteger()) return; // Not at right height to process with layer height
- //Debug.WriteLine($"{currentHeight} / {layerHeight} = {layerDifference}, Floor={Math.Floor(layerDifference)}");
+ var brightnesses = MultipleBrightnessValuesArray;
+ var multipleExposures = _exposureTable.Where(item => item.IsValid && item.LayerHeight == (decimal) SlicerFile.LayerHeight).ToArray();
+ if (brightnesses.Length == 0 || !_multipleBrightness) brightnesses = new[] { byte.MaxValue };
+ if (multipleExposures.Length == 0 || !_multipleExposures) multipleExposures = new[] { new ExposureItem((decimal)SlicerFile.LayerHeight, _bottomExposure, _normalExposure) };
- using var mat = EmguExtensions.InitMat(SlicerFile.Resolution);
- int layerCountOnHeight = (int)Math.Floor(currentHeight / layerHeight);
- bool isBottomLayer = layerCountOnHeight <= _bottomLayers;
- bool isBaseLayer = currentHeight <= _baseHeight;
- ushort microns = (ushort)Math.Floor(layerHeight * 1000);
- Point position;
- bool addSomething = false;
-
- foreach(var brightness in brightnesses)
+ int currentX = sideMarginPx;
+ int currentY = topBottomMarginPx;
+ Rectangle glueBottomLayerRectangle = new(new Point(currentX, currentY), Size.Empty);
+ foreach (var multipleExposure in multipleExposures)
{
- var bottomExposureTemp = bottomExposure;
- var normalExposureTemp = normalExposure;
- ExposureItem key = new(layerHeight, bottomExposure, normalExposure, brightness);
-
- if (table.TryGetValue(key, out var pos))
+ foreach (var brightness in brightnesses)
{
- position = pos;
- }
- else
- {
- if (currentX + layers[0].Width + sideMarginPx > SlicerFile.ResolutionX)
+ if (currentX + boundingRectangle.Width + sideMarginPx >= SlicerFile.ResolutionX)
{
currentX = sideMarginPx;
- currentY += layers[0].Height + (int)Math.Floor(_partMargin * Yppmm);
+ currentY += boundingRectangle.Height + partMarginYPx;
}
- if (currentY + layers[0].Height + topBottomMarginPx > SlicerFile.ResolutionY)
- {
- break; // Reach the end
- }
+ if (currentY + topBottomMarginPx >= SlicerFile.ResolutionY) break;
- position = new Point(currentX, currentY);
- table.Add(key, new Point(currentX, currentY));
+ var item = multipleExposure.Clone();
+ item.Brightness = brightness;
+ table.Add(item, new Point(currentX, currentY));
- currentX += layers[0].Width + (int)Math.Floor(_partMargin * Xppmm);
+ glueBottomLayerRectangle.Size = new Size(currentX + boundingRectangle.Width, currentY + boundingRectangle.Height);
+
+ currentX += boundingRectangle.Width + partMarginXPx;
}
-
-
- Mat matRoi = new(mat, new Rectangle(position, layers[0].Size));
-
- 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);
- }
-
- if (_multipleBrightness && brightness < 255)
- {
- // normalExposure - 255
- // x - brightness
- normalExposureTemp = Math.Round(normalExposure * brightness / byte.MaxValue, 2);
- if (_multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.None)
- {
- bottomExposureTemp = Math.Round(bottomExposure * brightness / byte.MaxValue, 2);
- }
- }
-
- var textHeightStart = matRoi.Height - featuresMarginY - TextMarkingSpacing;
- CvInvoke.PutText(matRoi, $"{microns}u", new Point(TextMarkingStartX, textHeightStart), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- CvInvoke.PutText(matRoi, $"{bottomExposureTemp}s", new Point(TextMarkingStartX, textHeightStart + TextMarkingLineBreak), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- CvInvoke.PutText(matRoi, $"{normalExposureTemp}s", new Point(TextMarkingStartX, textHeightStart + TextMarkingLineBreak * 2), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- CvInvoke.PutText(matRoi, $"{microns}u", new Point(matRoi.Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- CvInvoke.PutText(matRoi, $"{bottomExposureTemp}s", new Point(matRoi.Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart + TextMarkingLineBreak), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- CvInvoke.PutText(matRoi, $"{normalExposureTemp}s", new Point(matRoi.Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart + TextMarkingLineBreak * 2), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
-
- if(_multipleBrightness)
- {
- CvInvoke.PutText(matRoi, brightness.ToString(), new Point(matRoi.Width / 3, 35), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- if (brightness < 255 &&
- (_multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.None ||
- _multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.Bottom && !isBottomLayer ||
- _multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.BottomAndBase && !isBottomLayer && !isBaseLayer)
- )
- {
- using var pattern = matRoi.CloneBlank();
- pattern.SetTo(new MCvScalar(brightness));
- CvInvoke.BitwiseAnd(matRoi, pattern, matRoi, matRoi);
- }
- }
-
- addSomething = true;
}
- if (!addSomething) return;
+ if (table.Count <= 1) return false;
+ ushort microns = SlicerFile.LayerHeightUm;
- Layer layer = new(layerIndex++, mat, SlicerFile)
+ var tableGrouped = table.GroupBy(pair => new {pair.Key.LayerHeight, pair.Key.BottomExposure, pair.Key.Exposure}).Distinct();
+ SlicerFile.BottomLayerCount = _bottomLayers;
+ progress.ItemCount = (uint) (SlicerFile.LayerCount * table.Count);
+ Parallel.For(0, SlicerFile.LayerCount, layerIndex =>
{
- 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);
-
- progress++;
- }
-
- for (decimal currentHeight = _layerHeight; currentHeight <= totalHeight; currentHeight += Layer.HeightPrecisionIncrement)
- {
- currentHeight = Layer.RoundHeight(currentHeight);
- for (decimal layerHeight = _layerHeight; layerHeight <= endLayerHeight; layerHeight += _multipleLayerHeightStep)
- {
- progress.Token.ThrowIfCancellationRequested();
- layerHeight = Layer.RoundHeight(layerHeight);
-
- if (_multipleExposures)
+ if (progress.Token.IsCancellationRequested) return;
+ var layer = SlicerFile[layerIndex];
+ using var mat = layer.LayerMat;
+ var matRoi = new Mat(mat, boundingRectangle);
+ int layerCountOnHeight = (int)Math.Floor(layer.PositionZ / SlicerFile.LayerHeight);
+ foreach (var group in tableGrouped)
{
- foreach (var exposureItem in _exposureTable)
+ var newLayer = layer.Clone();
+ newLayer.ExposureTime = (float)(newLayer.IsBottomLayer ? group.Key.BottomExposure : group.Key.Exposure);
+ using var newMat = mat.CloneBlank();
+ foreach (var brightness in brightnesses)
{
- if (exposureItem.IsValid && exposureItem.LayerHeight == layerHeight)
+ ExposureItem item = new(group.Key.LayerHeight, group.Key.BottomExposure, group.Key.Exposure, brightness);
+ if(!table.TryGetValue(item, out var point)) continue;
+
+ var newMatRoi = new Mat(newMat, new Rectangle(point, matRoi.Size));
+ matRoi.CopyTo(newMatRoi);
+
+ if (layer.IsBottomLayer)
{
- AddLayer(currentHeight, layerHeight, exposureItem.BottomExposure, exposureItem.Exposure);
+ if (_patternModelGlueBottomLayers)
+ {
+ newMatRoi.SetTo(EmguExtensions.WhiteByte);
+ }
+ }
+
+ if (layerCountOnHeight < _chamferLayers)
+ {
+ CvInvoke.Erode(newMatRoi, newMatRoi, kernel, anchor, _chamferLayers - layerCountOnHeight, BorderType.Reflect101, default);
+ }
+
+ if (layer.IsBottomLayer)
+ {
+ if (_erodeBottomIterations > 0)
+ {
+ CvInvoke.Erode(newMatRoi, newMatRoi, kernel, anchor, _erodeBottomIterations, BorderType.Reflect101, default);
+ }
+
+ if (_multipleBrightness)
+ {
+ CvInvoke.PutText(newMatRoi, brightness.ToString(), new(xHalf - 60, yHalf + 20 - TextMarkingLineBreak * 4), TextMarkingFontFace, 2, EmguExtensions.BlackByte, 3, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ }
+ CvInvoke.PutText(newMatRoi, $"{microns}u", new(xHalf - 60, yHalf + 20 - TextMarkingLineBreak * 2), TextMarkingFontFace, 2, EmguExtensions.BlackByte, 3, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.PutText(newMatRoi, $"{group.Key.BottomExposure}s", new(xHalf - 60, yHalf + 20), TextMarkingFontFace, 2, EmguExtensions.BlackByte, 3, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.PutText(newMatRoi, $"{group.Key.Exposure}s", new(xHalf - 60, yHalf + 20 + TextMarkingLineBreak * 2), TextMarkingFontFace, 2, EmguExtensions.BlackByte, 3, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ }
+
+ if (brightness < 255)
+ {
+ if (_multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.None ||
+ _multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.Bottom && !layer.IsBottomLayer ||
+ _multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.BottomAndBase && !layer.IsBottomLayer)
+ {
+ using var pattern = matRoi.New();
+ pattern.SetTo(new MCvScalar(byte.MaxValue - brightness));
+ CvInvoke.Subtract(newMatRoi, pattern, newMatRoi);
+ }
}
}
+
+
+ newLayer.LayerMat = newMat;
+ parallelLayers.Add(newLayer);
+ progress.LockAndIncrement();
+ }
+ });
+
+ progress.Token.ThrowIfCancellationRequested();
+ if (parallelLayers.IsEmpty) return false;
+ var layers = parallelLayers.OrderBy(layer => layer.PositionZ).ThenBy(layer => layer.ExposureTime).ToList();
+
+ progress.ResetNameAndProcessed("Optimized layers");
+
+ Layer currentLayer = layers[0];
+ for (var layerIndex = 1; layerIndex < layers.Count; layerIndex++)
+ {
+ progress.Token.ThrowIfCancellationRequested();
+ progress++;
+ var layer = layers[layerIndex];
+ if (currentLayer.PositionZ != layer.PositionZ ||
+ currentLayer.ExposureTime != layer.ExposureTime) // Different layers, cache and continue
+ {
+ currentLayer = layer;
+ continue;
+ }
+
+ using var matCurrent = currentLayer.LayerMat;
+ using var mat = layer.LayerMat;
+
+ CvInvoke.Add(matCurrent, mat, matCurrent); // Sum layers
+ currentLayer.LayerMat = matCurrent;
+
+ layers[layerIndex] = null; // Discard
+ }
+
+ layers.RemoveAll(layer => layer is null); // Discard equal layers
+
+ SlicerFile.SuppressRebuildPropertiesWork(() =>
+ {
+ SlicerFile.BottomExposureTime = (float)BottomExposure;
+ SlicerFile.ExposureTime = (float)NormalExposure;
+ SlicerFile.LayerManager.Layers = layers.ToArray();
+ });
+ }
+ else
+ {
+ var layers = GetLayers();
+ if (layers is null) return false;
+ progress.ItemCount = 0;
+ //SanitizeExposureTable();
+ if (layers[0].Width > SlicerFile.ResolutionX || layers[0].Height > SlicerFile.ResolutionY)
+ {
+ return false;
+ }
+
+ List newLayers = new();
+
+ Dictionary table = new();
+ var endLayerHeight = _multipleLayerHeight ? _multipleLayerHeightMaximum : _layerHeight;
+ var totalHeight = TotalHeight;
+ uint layerIndex = 0;
+ int currentX = sideMarginPx;
+ int currentY = topBottomMarginPx;
+ int featuresMarginX = (int)(Xppmm * _featuresMargin);
+ int featuresMarginY = (int)(Yppmm * _featuresMargin);
+
+ var holes = Holes;
+
+ var brightnesses = MultipleBrightnessValuesArray;
+ if (brightnesses.Length == 0 || !_multipleBrightness) brightnesses = new[] { byte.MaxValue };
+
+ ExposureItem lastExposureItem = null;
+ decimal lastcurrentHeight = 0;
+
+ 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)}");
+
+
+ int layerCountOnHeight = (int)Math.Floor(currentHeight / layerHeight);
+ bool isBottomLayer = layerCountOnHeight <= _bottomLayers;
+ bool isBaseLayer = currentHeight <= _baseHeight;
+ ushort microns = (ushort)Math.Floor(layerHeight * 1000);
+ Point position;
+ bool addSomething = false;
+
+ bool reUseLastLayer =
+ lastExposureItem is not null &&
+ lastcurrentHeight == currentHeight &&
+ lastExposureItem.LayerHeight == layerHeight &&
+ (isBottomLayer && lastExposureItem.BottomExposure == bottomExposure || !isBottomLayer && lastExposureItem.Exposure == normalExposure);
+
+ using var mat = reUseLastLayer ? newLayers[^1].LayerMat : EmguExtensions.InitMat(SlicerFile.Resolution);
+
+ lastcurrentHeight = currentHeight;
+
+ foreach (var brightness in brightnesses)
+ {
+ var bottomExposureTemp = bottomExposure;
+ var normalExposureTemp = normalExposure;
+ ExposureItem key = new(layerHeight, bottomExposure, normalExposure, brightness);
+ lastExposureItem = key;
+
+ if (table.TryGetValue(key, out var pos))
+ {
+ position = pos;
+ }
+ else
+ {
+ if (currentX + layers[0].Width + sideMarginPx > SlicerFile.ResolutionX)
+ {
+ currentX = sideMarginPx;
+ currentY += layers[0].Height + partMarginYPx;
+ }
+
+ if (currentY + layers[0].Height + topBottomMarginPx > SlicerFile.ResolutionY)
+ {
+ break; // Reach the end
+ }
+
+ position = new Point(currentX, currentY);
+ table.Add(key, new Point(currentX, currentY));
+
+ currentX += layers[0].Width + partMarginXPx;
+ }
+
+
+ Mat matRoi = new(mat, new Rectangle(position, layers[0].Size));
+
+ 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);
+ }
+
+ if (_multipleBrightness && brightness < 255)
+ {
+ // normalExposure - 255
+ // x - brightness
+ normalExposureTemp = Math.Round(normalExposure * brightness / byte.MaxValue, 2);
+ if (_multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.None)
+ {
+ bottomExposureTemp = Math.Round(bottomExposure * brightness / byte.MaxValue, 2);
+ }
+ }
+
+ var textHeightStart = matRoi.Height - featuresMarginY - TextMarkingSpacing;
+ CvInvoke.PutText(matRoi, $"{microns}u", new Point(TextMarkingStartX, textHeightStart), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.PutText(matRoi, $"{bottomExposureTemp}s", new Point(TextMarkingStartX, textHeightStart + TextMarkingLineBreak), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.PutText(matRoi, $"{normalExposureTemp}s", new Point(TextMarkingStartX, textHeightStart + TextMarkingLineBreak * 2), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ if (holes.Length > 0)
+ {
+ CvInvoke.PutText(matRoi, $"{microns}u", new Point(matRoi.Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.PutText(matRoi, $"{bottomExposureTemp}s", new Point(matRoi.Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart + TextMarkingLineBreak), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.PutText(matRoi, $"{normalExposureTemp}s", new Point(matRoi.Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart + TextMarkingLineBreak * 2), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ }
+
+ if (_multipleBrightness)
+ {
+ CvInvoke.PutText(matRoi, brightness.ToString(), new Point(matRoi.Width / 3, 35), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ if (brightness < 255 &&
+ (_multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.None ||
+ _multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.Bottom && !isBottomLayer ||
+ _multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.BottomAndBase && !isBottomLayer && !isBaseLayer)
+ )
+ {
+ using var pattern = matRoi.New();
+ //pattern.SetTo(new MCvScalar(brightness)); OLD
+ //CvInvoke.BitwiseAnd(matRoi, pattern, matRoi, matRoi); OLD
+
+ pattern.SetTo(new MCvScalar(byte.MaxValue - brightness));
+ CvInvoke.Subtract(matRoi, pattern, matRoi);
+ }
+ }
+
+ addSomething = true;
+ }
+
+ if (!addSomething) return;
+
+ if (reUseLastLayer)
+ {
+ newLayers[^1].LayerMat = mat;
}
else
{
- AddLayer(currentHeight, layerHeight, _bottomExposure, _normalExposure);
+ 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);
}
+
+
+ progress++;
+ }
+
+ for (decimal currentHeight = _layerHeight; currentHeight <= totalHeight; currentHeight += Layer.HeightPrecisionIncrement)
+ {
+ currentHeight = Layer.RoundHeight(currentHeight);
+ for (decimal layerHeight = _layerHeight; layerHeight <= endLayerHeight; layerHeight += _multipleLayerHeightStep)
+ {
+ progress.Token.ThrowIfCancellationRequested();
+ layerHeight = Layer.RoundHeight(layerHeight);
+
+ 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);
+ }
+ }
+ }
+
+ SlicerFile.SuppressRebuildPropertiesWork(() =>
+ {
+ SlicerFile.LayerHeight = (float)LayerHeight;
+ SlicerFile.BottomExposureTime = (float)BottomExposure;
+ SlicerFile.ExposureTime = (float)NormalExposure;
+ SlicerFile.BottomLayerCount = BottomLayers;
+ SlicerFile.LayerManager.Layers = newLayers.ToArray();
+ });
+
+ if (_mirrorOutput)
+ {
+ new OperationFlip(SlicerFile) { FlipDirection = Enumerations.FlipDirection.Horizontally }.Execute(progress);
}
}
if (SlicerFile.ThumbnailsCount > 0)
SlicerFile.SetThumbnails(GetThumbnail());
- SlicerFile.SuppressRebuildPropertiesWork(() =>
- {
- SlicerFile.LayerHeight = (float)LayerHeight;
- SlicerFile.BottomExposureTime = (float)BottomExposure;
- SlicerFile.ExposureTime = (float)NormalExposure;
- SlicerFile.BottomLayerCount = BottomLayers;
- SlicerFile.LayerManager.Layers = newLayers.ToArray();
- });
-
if (_dontLiftSamePositionedLayers)
{
SlicerFile.LayerManager.SetNoLiftForSamePositionedLayers(_zeroLightOffSamePositionedLayers);
}
- if (_mirrorOutput)
- {
- new OperationFlip(SlicerFile){FlipDirection = Enumerations.FlipDirection.Horizontally}.Execute(progress);
- }
-
new OperationMove(SlicerFile).Execute(progress);
return !progress.Token.IsCancellationRequested;
diff --git a/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs b/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs
index 454bbbc..13f30e1 100644
--- a/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs
+++ b/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs
@@ -10,13 +10,11 @@ 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;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-using UVtools.Core.Objects;
namespace UVtools.Core.Operations
{
@@ -38,7 +36,7 @@ namespace UVtools.Core.Operations
private bool _centerHoleRelief = true;
private bool _hollowModel = true;
private bool _mirrorOutput;
- private decimal _wallThickness = 2.5M;
+ private decimal _wallThickness = 3.0M;
private decimal _observedXSize;
private decimal _observedYSize;
private decimal _observedZSize;
diff --git a/UVtools.Core/Operations/OperationProgress.cs b/UVtools.Core/Operations/OperationProgress.cs
index 1771536..732434a 100644
--- a/UVtools.Core/Operations/OperationProgress.cs
+++ b/UVtools.Core/Operations/OperationProgress.cs
@@ -184,6 +184,12 @@ namespace UVtools.Core.Operations
ProcessedItems = items;
}
+ public void ResetNameAndProcessed(string name = "", uint items = 0)
+ {
+ ItemName = name;
+ ProcessedItems = items;
+ }
+
public override string ToString()
{
diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj
index 8dce5fe..0ab2bb8 100644
--- a/UVtools.Core/UVtools.Core.csproj
+++ b/UVtools.Core/UVtools.Core.csproj
@@ -10,7 +10,7 @@
https://github.com/sn4k3/UVtools
https://github.com/sn4k3/UVtools
MSLA/DLP, file analysis, calibration, repair, conversion and manipulation
- 2.9.2
+ 2.9.3
Copyright © 2020 PTRTECH
UVtools.png
AnyCPU;x64
diff --git a/UVtools.ScriptSample/README.md b/UVtools.ScriptSample/README.md
index d8c60d8..4bcdc23 100644
--- a/UVtools.ScriptSample/README.md
+++ b/UVtools.ScriptSample/README.md
@@ -26,7 +26,7 @@ If you unsure about a script, run UVtools under a sandbox.
## How to write scripts (For developers)
-The Roslyn Scripts runs C# code directly but require some commun data structures to be removed from the commun csharp file,
+The Roslyn Scripts runs C# code directly but require some common data structures to be removed from the common csharp file,
such as "namespace" which is obligatory on a normal csharp file, and also a csharp file requires an class to encapsulate your code, you can't have code outside classes.
Scripts in otherway can run code directly without any namespace or class, so you can have a script just with "1 + 1",
diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml b/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml
index 5691f10..3a1e20a 100644
--- a/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml
+++ b/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml
@@ -10,7 +10,7 @@
-
@@ -40,7 +40,6 @@
ToolTip.Tip="The printer display height. Required to calculate the pixels per mm."
Text="Display height:"/>
@@ -168,11 +170,13 @@
@@ -181,7 +185,8 @@
-
+
-
-
-
-
-
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
@@ -417,7 +447,7 @@
-
+
@@ -530,9 +560,14 @@
-
+ Padding="5">
+
+
+
+
+
+
+
+
@@ -770,8 +806,6 @@
-
-
@@ -841,6 +875,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateToleranceControl.axaml b/UVtools.WPF/Controls/Calibrators/CalibrateToleranceControl.axaml
index 35a190e..7e0c6ef 100644
--- a/UVtools.WPF/Controls/Calibrators/CalibrateToleranceControl.axaml
+++ b/UVtools.WPF/Controls/Calibrators/CalibrateToleranceControl.axaml
@@ -10,7 +10,7 @@
-
diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj
index 1ddef0a..546c6cf 100644
--- a/UVtools.WPF/UVtools.WPF.csproj
+++ b/UVtools.WPF/UVtools.WPF.csproj
@@ -12,7 +12,7 @@
LICENSE
https://github.com/sn4k3/UVtools
Git
- 2.9.2
+ 2.9.3
@@ -24,14 +24,14 @@
1701;1702;
-
+
-
-
-
+
+
+
-
+