diff --git a/CHANGELOG.md b/CHANGELOG.md index 9deea85..2c621e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 27/02/2023 - v3.12.0 + +- (Add) Allow to pause and resume operations (#654) +- (Add) `Layer.FirstTransitionLayer` +- (Add) `Layer.LastTransitionLayer` +- (Add) File format: Elegoo GOO +- (Add) PrusaSlicer Printer: Elegoo Mars 4 +- (Improvement) Allocate maximum GPU memory for Skia up to 256 MB +- (Improvement) Set and sanitize transition layers exposure time from last bottom layer and first normal layer instead of global times (#659) +- (Change) CXDLP: Default version from 2 to 3 +- (Fix) UI was not rendering with GPU (ANGLE) +- (Fix) `Layer.IsTransitionLayer` was returning the wrong value +- (Upgrade) .NET from 6.0.13 to 6.0.14 + ## 01/01/2023 - v3.11.2 - (Fix) Converting to lgs sets Bottom Layer Count to 0 (#655) diff --git a/PrusaSlicer/printer/Elegoo Mars 4.ini b/PrusaSlicer/printer/Elegoo Mars 4.ini new file mode 100644 index 0000000..e59d377 --- /dev/null +++ b/PrusaSlicer/printer/Elegoo Mars 4.ini @@ -0,0 +1,42 @@ +# generated by PrusaSlicer 2.5.0+win64 on 2023-02-27 at 01:25:19 UTC +absolute_correction = 0 +area_fill = 50 +bed_custom_model = +bed_custom_texture = +bed_shape = 0x0,132.8x0,132.8x74.7,0x74.7 +default_sla_material_profile = Prusa Orange Tough 0.05 +default_sla_print_profile = 0.05 Normal +display_height = 74.7 +display_mirror_x = 0 +display_mirror_y = 1 +display_orientation = landscape +display_pixels_x = 2560 +display_pixels_y = 1440 +display_width = 132.8 +elefant_foot_compensation = 0.2 +elefant_foot_min_width = 0.2 +fast_tilt_time = 5 +gamma_correction = 1 +high_viscosity_tilt_time = 10 +host_type = octoprint +inherits = Original Prusa SL1 +max_exposure_time = 120 +max_initial_exposure_time = 300 +max_print_height = 150 +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_ELEGOO\nPRINTER_MODEL_MARS4\nFILEFORMAT_GOO\n\nSTART_CUSTOM_VALUES\nBottomWaitTimeBeforeCure_3\nWaitTimeBeforeCure_2.5\nBottomWaitTimeAfterCure_3\nWaitTimeAfterCure_1\nBottomLiftHeight_7\nLiftHeight_5\nBottomLiftSpeed_80\nLiftSpeed_100\nBottomWaitTimeAfterLift_0\nWaitTimeAfterLift_0\nRetractSpeed_150\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES +printer_settings_id = +printer_technology = SLA +printer_variant = default +printer_vendor = +printhost_apikey = +printhost_cafile = +relative_correction = 1,1 +relative_correction_x = 1 +relative_correction_y = 1 +relative_correction_z = 1 +slow_tilt_time = 8 +thumbnails = 290x290,116x116 diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 2b9e545..a4b8684 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,2 +1,12 @@ -- (Fix) Converting to lgs sets Bottom Layer Count to 0 (#655) +- (Add) Allow to pause and resume operations (#654) +- (Add) `Layer.FirstTransitionLayer` +- (Add) `Layer.LastTransitionLayer` +- (Add) File format: Elegoo GOO +- (Add) PrusaSlicer Printer: Elegoo Mars 4 +- (Improvement) Allocate maximum GPU memory for Skia up to 256 MB +- (Improvement) Set and sanitize transition layers exposure time from last bottom layer and first normal layer instead of global times (#659) +- (Change) CXDLP: Default version from 2 to 3 +- (Fix) UI was not rendering with GPU (ANGLE) +- (Fix) `Layer.IsTransitionLayer` was returning the wrong value +- (Upgrade) .NET from 6.0.13 to 6.0.14 diff --git a/Scripts/010 Editor/FileFormats.1pj b/Scripts/010 Editor/FileFormats.1pj index 4dbf2bf..a6c26ee 100644 --- a/Scripts/010 Editor/FileFormats.1pj +++ b/Scripts/010 Editor/FileFormats.1pj @@ -15,6 +15,7 @@ mdlp.bt osf.bt osla.bt + goo.bt photons.bt PhotonWorkshop.bt phz.bt diff --git a/Scripts/010 Editor/PhotonWorkshop.bt b/Scripts/010 Editor/PhotonWorkshop.bt index b1640e5..b92b5dc 100644 --- a/Scripts/010 Editor/PhotonWorkshop.bt +++ b/Scripts/010 Editor/PhotonWorkshop.bt @@ -59,7 +59,7 @@ struct HEADER { uint ResolutionY ; float WeightG ; float Price ; - wstring PriceCurrencyDec ; + wstring PriceCurrency ; uint PerLayerOverride ; uint PrintTime ; uint TransitionLayerCount ; diff --git a/Scripts/010 Editor/goo.bt b/Scripts/010 Editor/goo.bt new file mode 100644 index 0000000..dcb7952 --- /dev/null +++ b/Scripts/010 Editor/goo.bt @@ -0,0 +1,123 @@ +//------------------------------------------------ +//--- 010 Editor v8.0.1 Binary Template +// +// File: goo +// Authors: Tiago Conceição +//------------------------------------------------ + +BigEndian(); + +enum BOOL { False, True }; + +struct HEADER { + char Version[4] ; // format version: V3.0 + char Magic[8] ; // Fix contant:0x07 0x00 0x00 0x00 0x44 0x4C 0x50 0x00 + char SoftwareName[32] ; + char SoftwareVersion[24] ; + char FileCreateTime[24] ; // yyyy-mm-dd hh:mm:ss + char MachineName[32] ; + char MachineType[32] ; + char ProfileName[32] ; + ushort AntiAliasingLevel ; + ushort GreyLevel ; + ushort BlurLevel ; + ubyte SmallPreview[116*116*2] ; // RGB_565 16Bit + ubyte SmallPreviewDelimiter[2] ; // \n 0x0D 0x0A + ubyte BigPreview[290*290*2] ; // RGB_565 16Bit + ubyte BigPreviewDelimiter[2] ; // \n 0x0D 0x0A + + uint LayerCount ; + ushort ResolutionX ; + ushort ResolutionY ; + BOOL MirrorX ; + BOOL MirrorY ; + float DisplayWidth ; + float DisplayHeight ; + float MachineZ ; + float LayerHeight ; + float ExposureTime ; + enum { WaitTime, LightOffDelay } DelayMode ; // 1: wait time mode , 0: light off delay mode + float LightOffDelay ; + float BottomWaitTimeAfterCure ; + float BottomWaitTimeAfterLift ; + float BottomWaitTimeBeforeCure ; + float WaitTimeAfterCure ; + float WaitTimeAfterLift ; + float WaitTimeBeforeCure ; + float BottomExposureTime ; + uint BottomLayerCount ; + float BottomLiftHeight ; + float BottomLiftSpeed ; // mm/min + float LiftHeight ; + float LiftSpeed ; // mm/min + float BottomRetractHeight ; + float BottomRetractSpeed ; // mm/min + float RetractHeight ; + float RetractSpeed ; // mm/min + + float BottomLiftHeight2 ; + float BottomLiftSpeed2 ; // mm/min + float LiftHeight2 ; + float LiftSpeed2 ; // mm/min + float BottomRetractHeight2 ; + float BottomRetractSpeed2 ; // mm/min + float RetractHeight2 ; + float RetractSpeed2 ; // mm/min + + ushort BottomLightPWM ; + ushort LightPWM ; + + BOOL PerLayerSettings ; // 0: normal mode; 1:advance mode, printing use the value of "Layer Definition Content" + + uint PrintTime ; // seconds + float Volume ; // The volume of all parts. unit:mm3 + float MaterialGrams ; // The weight of all parts. unit:g + float MaterialCost ; + char PriceCurrency[8] ; // $, € + + uint LayerDefAddress ; + ubyte GrayScaleLevel ; // 0: The range of pixel's gray value is from 0x0 ~ 0xf; 1:The range of pixel's gray value is from 0x0 ~ 0xff; + ushort TransitionLayerCount ; +} header; + +struct LAYER_DEF { + // 0: reserve; 1: current layer pause printing + ushort Pause ; + // The lift distance of Z axis when "Pause flag" equal 1. unit:mm + float PausePositionZ ; + float PositionZ ; + float ExposureTime ; + float LightOffDelay ; + float WaitTimeAfterCure ; + float WaitTimeAfterLift ; + float WaitTimeBeforeCure ; + float LiftHeight ; + float LiftSpeed ; // mm/min + float LiftHeight2 ; + float LiftSpeed2 ; // mm/min + float RetractHeight ; + float RetractSpeed ; // mm/min + float RetractHeight2 ; + float RetractSpeed2 ; // mm/min + ushort LightPWM ; + ubyte DelimiterData[2] ; // \n 0x0D 0x0A + uint DataSize ; + // First RLE byte is Magic (0x55) + ubyte RLE[DataSize] ; + // Last RLE byte is Checksum XOR + ubyte DelimiterRLE[2] ; // \n 0x0D 0x0A +}; + +struct LAYERS{ + local uint i; + for(i = 0; i < header.LayerCount; i++){ + LAYER_DEF layerDef; + } +} layers; + + +struct FOOTER { + ubyte Padding[3] ; // format version + char Magic[8] ; // Fix contant:0x07 0x00 0x00 0x00 0x44 0x4C 0x50 0x00 + +} footer; \ No newline at end of file diff --git a/Scripts/010 Editor/osf.bt b/Scripts/010 Editor/osf.bt index 0c8ebf4..e423987 100644 --- a/Scripts/010 Editor/osf.bt +++ b/Scripts/010 Editor/osf.bt @@ -107,12 +107,12 @@ struct HEADER { ubyte ProtocolType ; // 0 local int leftover = HeaderLength - 350001; - if(HeaderLength == 350013) // VLR + /*if(HeaderLength == 350013) // VLR { uint Unknown ; // 350054 ubyte Unknown1[8] ; leftover -= 12; - } + }*/ if(leftover > 0) diff --git a/Scripts/UVtools.ScriptSample/ScriptAdvancedDialogSample.cs b/Scripts/UVtools.ScriptSample/ScriptAdvancedDialogSample.cs index 36e482a..37c2501 100644 --- a/Scripts/UVtools.ScriptSample/ScriptAdvancedDialogSample.cs +++ b/Scripts/UVtools.ScriptSample/ScriptAdvancedDialogSample.cs @@ -75,7 +75,7 @@ public class ScriptAdvancedDialogSample : ScriptGlobals for (int i = 0; i < Iterations.Value; i++) { - Progress.ThrowIfCancellationRequested(); + Progress.PauseOrCancelIfRequested(); Thread.Sleep(1000); Progress.Log = $"Task {i}: Completed!\n{Progress.Log}"; diff --git a/Scripts/UVtools.ScriptSample/ScriptAutomateWorkflowSample.cs b/Scripts/UVtools.ScriptSample/ScriptAutomateWorkflowSample.cs index 5bfc876..320e33e 100644 --- a/Scripts/UVtools.ScriptSample/ScriptAutomateWorkflowSample.cs +++ b/Scripts/UVtools.ScriptSample/ScriptAutomateWorkflowSample.cs @@ -99,7 +99,7 @@ public class ScriptAutomateWorkflowSample : ScriptGlobals foreach (var operation in operations) // Loop all my created operations to execute them { - Progress.ThrowIfCancellationRequested(); // Abort operation, user requested cancellation + Progress.PauseOrCancelIfRequested(); // Abort operation, user requested cancellation operation.ROI = Operation.ROI; // Copy user selected ROI to my operation operation.MaskPoints = Operation.MaskPoints; // Copy user selected Masks to my operation if (!operation.CanValidate()) continue; // If cant validate don't execute the operation diff --git a/Scripts/UVtools.ScriptSample/ScriptChangeLayerPropertiesSample.cs b/Scripts/UVtools.ScriptSample/ScriptChangeLayerPropertiesSample.cs index 71fc9a8..a425734 100644 --- a/Scripts/UVtools.ScriptSample/ScriptChangeLayerPropertiesSample.cs +++ b/Scripts/UVtools.ScriptSample/ScriptChangeLayerPropertiesSample.cs @@ -48,7 +48,7 @@ public class ScriptChangeLayerPropertiesSample : ScriptGlobals for (uint layerIndex = Operation.LayerIndexStart; layerIndex <= Operation.LayerIndexEnd; layerIndex++) { - Progress.ThrowIfCancellationRequested(); // Abort operation, user requested cancellation + Progress.PauseOrCancelIfRequested(); // Abort operation, user requested cancellation var layer = SlicerFile[layerIndex]; // Unpack and expose layer variable for easier use layer.LiftHeight = random.Next(3, 10); // Random value from 3 to 10 diff --git a/Scripts/UVtools.ScriptSample/ScriptCloneSettings.cs b/Scripts/UVtools.ScriptSample/ScriptCloneSettings.cs index 8b6edd0..39b7d78 100644 --- a/Scripts/UVtools.ScriptSample/ScriptCloneSettings.cs +++ b/Scripts/UVtools.ScriptSample/ScriptCloneSettings.cs @@ -120,7 +120,7 @@ public class ScriptCloneSettings : ScriptGlobals var results = new List?>>(); foreach (var filePath in filePaths) { - Progress.ThrowIfCancellationRequested(); + Progress.PauseOrCancelIfRequested(); Tuple?> result; try diff --git a/Scripts/UVtools.ScriptSample/ScriptCompensateCrossBleeding.cs b/Scripts/UVtools.ScriptSample/ScriptCompensateCrossBleeding.cs index a2178fe..efb8e81 100644 --- a/Scripts/UVtools.ScriptSample/ScriptCompensateCrossBleeding.cs +++ b/Scripts/UVtools.ScriptSample/ScriptCompensateCrossBleeding.cs @@ -1,5 +1,6 @@ using Emgu.CV; using System; +using System.Diagnostics; using System.Drawing; using System.Threading.Tasks; using UVtools.Core; @@ -45,6 +46,7 @@ public class ScriptCompensateCrossBleeding : ScriptGlobals CoreSettings.GetParallelOptions(Progress), layerIndex => { + Progress.PauseIfRequested(); var layersBelowCount = layerIndex > LayerBleed.Value ? LayerBleed.Value : layerIndex; using var sourceMat = originalLayers[layerIndex].LayerMat; diff --git a/Scripts/UVtools.ScriptSample/ScriptInsetSample.cs b/Scripts/UVtools.ScriptSample/ScriptInsetSample.cs index 2a1cc57..3f903b8 100644 --- a/Scripts/UVtools.ScriptSample/ScriptInsetSample.cs +++ b/Scripts/UVtools.ScriptSample/ScriptInsetSample.cs @@ -95,6 +95,7 @@ public class ScriptInsetSample : ScriptGlobals // Loop user selected layers in parallel, this will put each core of CPU working here on parallel Parallel.For(Operation.LayerIndexStart, Operation.LayerIndexEnd+1, CoreSettings.GetParallelOptions(Progress), layerIndex => { + Progress.PauseIfRequested(); var layer = SlicerFile[layerIndex]; // Unpack and expose layer variable for easier use using var mat = layer.LayerMat; // Gets this layer mat/image var original = mat.Clone(); // Keep a original mat copy diff --git a/Scripts/UVtools.ScriptSample/ScriptLightBleedCompensationSample.cs b/Scripts/UVtools.ScriptSample/ScriptLightBleedCompensationSample.cs index 88ca463..3160e6b 100644 --- a/Scripts/UVtools.ScriptSample/ScriptLightBleedCompensationSample.cs +++ b/Scripts/UVtools.ScriptSample/ScriptLightBleedCompensationSample.cs @@ -89,6 +89,7 @@ public class ScriptLightBleedCompensationSample : ScriptGlobals // Loop user selected layers in parallel, this will put each core of CPU working here on parallel Parallel.For(Operation.LayerIndexStart, Operation.LayerIndexEnd+1, CoreSettings.GetParallelOptions(Progress), layerIndex => { + Progress.PauseIfRequested(); var layer = SlicerFile[layerIndex]; // Unpack and expose layer variable for easier use using var mat = layer.LayerMat; // Gets this layer mat/image var original = mat.Clone(); // Keep a original mat copy diff --git a/Scripts/UVtools.ScriptSample/ScriptPreventResinShrinkage.cs b/Scripts/UVtools.ScriptSample/ScriptPreventResinShrinkage.cs index 333d5f3..c3988ba 100644 --- a/Scripts/UVtools.ScriptSample/ScriptPreventResinShrinkage.cs +++ b/Scripts/UVtools.ScriptSample/ScriptPreventResinShrinkage.cs @@ -131,6 +131,7 @@ public class ScriptPreventResinShrinkage : ScriptGlobals CoreSettings.GetParallelOptions(Progress), layerIndex => { + Progress.PauseIfRequested(); var fullLayer = SlicerFile[layerIndex]; if (fullLayer.IsEmpty) { diff --git a/Scripts/UVtools.ScriptSample/ScriptTester.cs b/Scripts/UVtools.ScriptSample/ScriptTester.cs index 0807cd6..712b1a3 100644 --- a/Scripts/UVtools.ScriptSample/ScriptTester.cs +++ b/Scripts/UVtools.ScriptSample/ScriptTester.cs @@ -54,6 +54,7 @@ public class ScriptChangeLayesrPropertiesSample : ScriptGlobals var dict = new Dictionary>(); Parallel.For(Operation.LayerIndexStart, Operation.LayerIndexEnd + 1, CoreSettings.GetParallelOptions(Progress), layerIndex => { + Progress.PauseIfRequested(); using var mat = SlicerFile[layerIndex].LayerMat; using var contours = mat.FindContours(out var hierarchy, RetrType.Tree); diff --git a/UVtools.Core/FileFormats/AnetFile.cs b/UVtools.Core/FileFormats/AnetFile.cs index 80addf3..c741fbb 100644 --- a/UVtools.Core/FileFormats/AnetFile.cs +++ b/UVtools.Core/FileFormats/AnetFile.cs @@ -469,6 +469,7 @@ public sealed class AnetFile : FileFormat { Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); var layer = this[layerIndex]; using var mat = layer.LayerMat; layerData[layerIndex] = new LayerDef(); @@ -479,7 +480,7 @@ public sealed class AnetFile : FileFormat foreach (var layerIndex in batch) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); outputFile.WriteSerialize(layerData[layerIndex]); outputFile.WriteBytes(layerData[layerIndex].EncodedRle); @@ -521,7 +522,7 @@ public sealed class AnetFile : FileFormat { foreach (var layerIndex in batch) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); var layerDef = Helpers.Deserialize(inputFile); layersDefinitions[layerIndex] = layerDef; @@ -543,6 +544,7 @@ public sealed class AnetFile : FileFormat { Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using var mat = layersDefinitions[layerIndex].Decode(out var resolutionX, out var resolutionY); if (layerIndex == 0) // Set file resolution from first layer RLE. Figure out other properties after that { diff --git a/UVtools.Core/FileFormats/CTBEncryptedFile.cs b/UVtools.Core/FileFormats/CTBEncryptedFile.cs index c70dc5d..ba006a4 100644 --- a/UVtools.Core/FileFormats/CTBEncryptedFile.cs +++ b/UVtools.Core/FileFormats/CTBEncryptedFile.cs @@ -1157,7 +1157,7 @@ public sealed class CTBEncryptedFile : FileFormat LayersPointer = new LayerPointer[Settings.LayerCount]; for (uint layerIndex = 0; layerIndex < Settings.LayerCount; layerIndex++) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); LayersPointer[layerIndex] = Helpers.Deserialize(inputFile); Debug.WriteLine($"pointer[{layerIndex}]: {LayersPointer[layerIndex]}"); progress++; @@ -1172,7 +1172,7 @@ public sealed class CTBEncryptedFile : FileFormat { foreach (var layerIndex in batch) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); inputFile.Seek(LayersPointer[layerIndex].PageNumber * ChituboxFile.PageSize + LayersPointer[layerIndex].LayerOffset, SeekOrigin.Begin); LayersDefinition[layerIndex] = Helpers.Deserialize(inputFile); @@ -1185,6 +1185,7 @@ public sealed class CTBEncryptedFile : FileFormat { Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); var layerDef = LayersDefinition[layerIndex]; @@ -1349,6 +1350,7 @@ public sealed class CTBEncryptedFile : FileFormat progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); Parallel.For(0, LayerCount, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); var layerDef = new LayerDef(this, this[layerIndex]); using (var mat = this[layerIndex].LayerMat) { @@ -1362,7 +1364,7 @@ public sealed class CTBEncryptedFile : FileFormat progress.Reset(OperationProgress.StatusWritingFile, LayerCount); for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); var layerDef = LayersDefinition[layerIndex]; LayersPointer[layerIndex] = new LayerPointer(outputFile.Position); diff --git a/UVtools.Core/FileFormats/CXDLPFile.cs b/UVtools.Core/FileFormats/CXDLPFile.cs index 5a14221..eee0d1c 100644 --- a/UVtools.Core/FileFormats/CXDLPFile.cs +++ b/UVtools.Core/FileFormats/CXDLPFile.cs @@ -33,9 +33,12 @@ namespace UVtools.Core.FileFormats; public sealed class CXDLPFile : FileFormat { #region Constants + private const byte HEADER_SIZE = 9; // CXSW3DV2 private const string HEADER_VALUE = "CXSW3DV2"; private const string HEADER_VALUE_GENERIC = "CXSW3D"; + private const byte DEFAULT_VERSION = 3; + #endregion #region Sub Classes @@ -62,7 +65,7 @@ public sealed class CXDLPFile : FileFormat [FieldOrder(2)] [FieldEndianness(Endianness.Big)] - public ushort Version { get; set; } = 2; + public ushort Version { get; set; } = DEFAULT_VERSION; /// /// Gets the size of the printer model @@ -458,7 +461,7 @@ public sealed class CXDLPFile : FileFormat public override uint[] AvailableVersions { get; } = { 2, 3 }; - public override uint DefaultVersion => 2; + public override uint DefaultVersion => DEFAULT_VERSION; public override uint Version { @@ -675,6 +678,7 @@ public sealed class CXDLPFile : FileFormat // Previews Parallel.For(0, previews.Length, CoreSettings.GetParallelOptions(progress), previewIndex => { + progress.PauseIfRequested(); var encodeLength = ThumbnailsOriginalSize[previewIndex].Area() * 2; if (Thumbnails[previewIndex] is null) { @@ -720,6 +724,7 @@ public sealed class CXDLPFile : FileFormat { Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); var layer = this[layerIndex]; using (var mat = layer.LayerMat) { @@ -841,6 +846,7 @@ public sealed class CXDLPFile : FileFormat Parallel.For(0, previews.Length, CoreSettings.GetParallelOptions(progress), previewIndex => { + progress.PauseIfRequested(); Thumbnails[previewIndex] = DecodeImage(DATATYPE_RGB565_BE, previews[previewIndex], ThumbnailsOriginalSize[previewIndex]); previews[previewIndex] = null!; }); @@ -868,7 +874,7 @@ public sealed class CXDLPFile : FileFormat { foreach (var layerIndex in batch) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); inputFile.Seek(4, SeekOrigin.Current); var lineCount = BitExtensions.ToUIntBigEndian(inputFile.ReadBytes(4)); @@ -880,6 +886,7 @@ public sealed class CXDLPFile : FileFormat Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using (var mat = EmguExtensions.InitMat(Resolution)) { diff --git a/UVtools.Core/FileFormats/CXDLPv1File.cs b/UVtools.Core/FileFormats/CXDLPv1File.cs index e7c11e4..bfdb781 100644 --- a/UVtools.Core/FileFormats/CXDLPv1File.cs +++ b/UVtools.Core/FileFormats/CXDLPv1File.cs @@ -492,6 +492,7 @@ public sealed class CXDLPv1File : FileFormat // Previews Parallel.For(0, previews.Length, CoreSettings.GetParallelOptions(progress), previewIndex => { + progress.PauseIfRequested(); var encodeLength = ThumbnailsOriginalSize[previewIndex].Area() * 2; if (Thumbnails[previewIndex] is null) { @@ -529,6 +530,7 @@ public sealed class CXDLPv1File : FileFormat { Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); var layer = this[layerIndex]; using (var mat = layer.LayerMat) { @@ -611,6 +613,7 @@ public sealed class CXDLPv1File : FileFormat Parallel.For(0, previews.Length, CoreSettings.GetParallelOptions(progress), previewIndex => { + progress.PauseIfRequested(); Thumbnails[previewIndex] = DecodeImage(DATATYPE_RGB565_BE, previews[previewIndex], ThumbnailsOriginalSize[previewIndex]); previews[previewIndex] = null!; }); @@ -631,7 +634,7 @@ public sealed class CXDLPv1File : FileFormat { foreach (var layerIndex in batch) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); inputFile.Seek(4, SeekOrigin.Current); var lineCount = BitExtensions.ToUIntBigEndian(inputFile.ReadBytes(4)); @@ -644,6 +647,7 @@ public sealed class CXDLPv1File : FileFormat Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using (var mat = EmguExtensions.InitMat(Resolution)) { for (int i = 0; i < linesBytes[layerIndex].Length; i++) diff --git a/UVtools.Core/FileFormats/ChituboxFile.cs b/UVtools.Core/FileFormats/ChituboxFile.cs index 4b908d0..d6c1a6d 100644 --- a/UVtools.Core/FileFormats/ChituboxFile.cs +++ b/UVtools.Core/FileFormats/ChituboxFile.cs @@ -1906,6 +1906,7 @@ public sealed class ChituboxFile : FileFormat { Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using (var mat = this[layerIndex].LayerMat) { for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++) @@ -1923,7 +1924,7 @@ public sealed class ChituboxFile : FileFormat if (layerIndex == 0) layerDefSize = Helpers.Serializer.SizeOf(LayerDefinitions[0, layerIndex]); for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); var layerDef = LayerDefinitions[aaIndex, layerIndex]; LayerDef? layerDefHash = null; @@ -2087,7 +2088,7 @@ public sealed class ChituboxFile : FileFormat Debug.WriteLine($"-Image GROUP {aaIndex}-"); for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); inputFile.Seek(layerOffset, SeekOrigin.Begin); var layerDef = Helpers.Deserialize(inputFile); layerDef.Parent = this; @@ -2125,7 +2126,7 @@ public sealed class ChituboxFile : FileFormat { for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); inputFile.Seek(LayerDefinitions[aaIndex, layerIndex].PageNumber * PageSize + LayerDefinitions[aaIndex, layerIndex].DataAddress, SeekOrigin.Begin); LayerDefinitions[aaIndex, layerIndex].EncodedRle = inputFile.ReadBytes(LayerDefinitions[aaIndex, layerIndex].DataSize); @@ -2134,6 +2135,7 @@ public sealed class ChituboxFile : FileFormat Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using var mat = LayerDefinitions[0, layerIndex].Decode((uint)layerIndex); _layers[layerIndex] = new Layer((uint)layerIndex, mat, this); diff --git a/UVtools.Core/FileFormats/FDGFile.cs b/UVtools.Core/FileFormats/FDGFile.cs index 776d6f6..8ac32f3 100644 --- a/UVtools.Core/FileFormats/FDGFile.cs +++ b/UVtools.Core/FileFormats/FDGFile.cs @@ -959,6 +959,7 @@ public sealed class FDGFile : FileFormat { Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using (var mat = this[layerIndex].LayerMat) { LayersDefinitions[layerIndex] = new LayerDef(this, this[layerIndex]); @@ -969,7 +970,7 @@ public sealed class FDGFile : FileFormat foreach (var layerIndex in batch) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); var layerDef = LayersDefinitions[layerIndex]; LayerDef? layerDefHash = null; @@ -1070,7 +1071,7 @@ public sealed class FDGFile : FileFormat { foreach (var layerIndex in batch) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); var layerDef = Helpers.Deserialize(inputFile); layerDef.Parent = this; @@ -1090,6 +1091,7 @@ public sealed class FDGFile : FileFormat { Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); if (DecodeType == FileDecodeType.Full) { using var mat = LayersDefinitions[layerIndex].Decode((uint)layerIndex); diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index a31f0cf..0ce4f74 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -386,6 +386,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable public Layer? LastBottomLayer => this.LastOrDefault(layer => layer.IsBottomLayer); + /// + /// Gets the first transition layer + /// + public Layer? FirstTransitionLayer => TransitionLayerCount == 0 ? null : this[BottomLayerCount]; + + /// + /// Gets the last transition layer + /// + public Layer? LastTransitionLayer => TransitionLayerCount == 0 ? null : this[BottomLayerCount + TransitionLayerCount - 1]; + /// /// Gets the first normal layer /// @@ -3604,7 +3615,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable Layer.HeightPrecision) @@ -3624,7 +3635,14 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable TransitionLayerCount = ParseTransitionLayerCountFromLayers()); + SuppressRebuildPropertiesWork(() => + { + var transitionLayers = ParseTransitionLayerCountFromLayers(); + if (transitionLayers > 0) + { + TransitionLayerCount = transitionLayers; + } + }); } bool reSaveFile = Sanitize(); @@ -3684,6 +3702,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable { + progress.PauseIfRequested(); if (matGenFunc is null) { switch (layerImageType) @@ -3767,6 +3786,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable { + progress.PauseIfRequested(); byte[] pngBytes; lock (Mutex) { @@ -4007,6 +4027,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable { + progress.PauseIfRequested(); var byteArr = layer.CompressedPngBytes; if (byteArr is null) return; using var stream = new FileStream(Path.Combine(path, layer.Filename), FileMode.Create, FileAccess.Write); @@ -4024,13 +4045,13 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable 0 ? count : TransitionLayerCount; } /// @@ -4042,7 +4063,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable @@ -4054,7 +4075,9 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatableSeconds public static float GetTransitionStepTime(float longExposureTime, float shortExposureTime, ushort transitionLayerCount) { - return transitionLayerCount == 0 ? 0 : (float)Math.Round((longExposureTime - shortExposureTime) / (transitionLayerCount + 1), 2, MidpointRounding.AwayFromZero); + return transitionLayerCount == 0 + ? 0 + : (float)Math.Round((longExposureTime - shortExposureTime) / (transitionLayerCount + 1), 2, MidpointRounding.AwayFromZero); } /// @@ -4067,6 +4090,21 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable + /// Gets the transition step time from and first normal layer after the last transition layer, value is returned as positive from normal perspective and logic (Longer - shorter) + /// + /// Number of transition layers + /// Seconds + public float GetTransitionStepTimeFromLayers(ushort transitionLayerCount) + { + var bottomExposureTime = LastBottomLayer?.ExposureTime ?? BottomExposureTime; + var exposureTime = TransitionLayerCount > 0 + ? this[BottomLayerCount + TransitionLayerCount - 1].ExposureTime + : this[BottomLayerCount].ExposureTime; + + return GetTransitionStepTime(bottomExposureTime, exposureTime, transitionLayerCount); + } + /// /// Gets the transition step time from and , value is returned as positive from normal perspective and logic (Longer - shorter) /// @@ -4100,6 +4138,25 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable + /// Gets the transition layer count based on and first normal layer after the last transition layer + /// + /// Step decrement time in seconds + /// True if transition layer count can't be higher than supported by the file, otherwise set to false to not look at possible file layers + /// Midpoint rounding method + /// Transition layer count + public ushort GetTransitionLayerCountFromLayers(float stepDecrementTime, bool constrainToLayerCount = true, MidpointRounding rounding = MidpointRounding.AwayFromZero) + { + var bottomExposureTime = LastBottomLayer?.ExposureTime ?? BottomExposureTime; + var exposureTime = TransitionLayerCount > 0 + ? this[BottomLayerCount + TransitionLayerCount - 1].ExposureTime + : this[BottomLayerCount].ExposureTime; + + var count = GetTransitionLayerCount(bottomExposureTime, exposureTime, stepDecrementTime, rounding); + if (constrainToLayerCount) count = (ushort)Math.Min(count, MaximumPossibleTransitionLayerCount); + return count; + } + /// /// Re-set exposure time to the transition layers @@ -4118,27 +4175,37 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatableTrue to default all the previous transition layers exposure time, otherwise false public void SetTransitionLayers(ushort transitionLayerCount, bool resetExposureTimes = true) { + var bottomExposureTime = LastBottomLayer?.ExposureTime ?? BottomExposureTime; + var exposureTime = ExposureTime; + var layersToReset = new List(); + for (uint layerIndex = BottomLayerCount; layerIndex < LastLayerIndex; layerIndex++) + { + if (Math.Abs(this[layerIndex].ExposureTime - this[layerIndex + 1].ExposureTime) < 0.009) + { + exposureTime = this[layerIndex].ExposureTime; + break; // First equal layer, transition ended + } + layersToReset.Add(this[layerIndex]); + } + if (resetExposureTimes) { - for (uint layerIndex = BottomLayerCount; layerIndex < LayerCount; layerIndex++) + foreach (var layer in layersToReset) { - var layer = this[layerIndex]; - if (Math.Abs(layer.ExposureTime - ExposureTime) < 0.009) break; // First equal layer, transition ended - - layer.ExposureTime = ExposureTime; + layer.ExposureTime = exposureTime; } } if (transitionLayerCount == 0) return; - float decrement = GetTransitionStepTime(transitionLayerCount); + float decrement = GetTransitionStepTime(bottomExposureTime, exposureTime, transitionLayerCount); if (decrement <= 0) return; uint appliedLayers = 0; for (uint layerIndex = BottomLayerCount; appliedLayers < transitionLayerCount && layerIndex < LayerCount; layerIndex++) { appliedLayers++; - this[layerIndex].ExposureTime = Math.Clamp(BottomExposureTime - (decrement * appliedLayers), ExposureTime, BottomExposureTime); + this[layerIndex].ExposureTime = Math.Clamp(bottomExposureTime - (decrement * appliedLayers), exposureTime, bottomExposureTime); } } @@ -5097,6 +5164,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable { + progress.PauseIfRequested(); layer.CompressionCodec = newCodec; progress.LockAndIncrement(); }); @@ -5355,6 +5423,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable { + progress.PauseIfRequested(); this[layerIndex].GetBoundingRectangle(); progress.LockAndIncrement(); }); @@ -5725,6 +5794,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable { + progress.PauseIfRequested(); layers[i] = new Layer((uint)i, mats[i], this); }); @@ -6100,6 +6170,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable { + progress.PauseIfRequested(); var layer = this[layerOperationGroup.Key]; using var mat = layer.LayerMat; @@ -6176,7 +6247,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable= 0 && toProcess.Count > 0; operationLayer--) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); var layer = this[operationLayer]; var mat = matCache.Get1((uint) operationLayer); var isMatModified = false; @@ -6426,6 +6497,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable { + progress.PauseIfRequested(); using var mat = GetMergedMatForSequentialPositionedLayers(layer.Index); using var mat32Roi = mat.Roi(roi); diff --git a/UVtools.Core/FileFormats/FlashForgeSVGXFile.cs b/UVtools.Core/FileFormats/FlashForgeSVGXFile.cs index a1f5770..c851385 100644 --- a/UVtools.Core/FileFormats/FlashForgeSVGXFile.cs +++ b/UVtools.Core/FileFormats/FlashForgeSVGXFile.cs @@ -490,6 +490,7 @@ public sealed class FlashForgeSVGXFile : FileFormat Parallel.For(0, LayerCount, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); groups[layerIndex] = new FlashForgeSVGXSvgGroup($"layer-{layerIndex}"); using var mat = this[layerIndex].LayerMat; @@ -636,6 +637,7 @@ public sealed class FlashForgeSVGXFile : FileFormat progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); Parallel.For(0, LayerCount, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); var mat = EmguExtensions.InitMat(Resolution); var group = SVGDocument.Groups.FirstOrDefault(g => g.Id == $"layer-{layerIndex}"); @@ -646,7 +648,7 @@ public sealed class FlashForgeSVGXFile : FileFormat var points = new List(); foreach (var path in @group.Paths) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); var spaceSplit = path.Value.Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); diff --git a/UVtools.Core/FileFormats/GR1File.cs b/UVtools.Core/FileFormats/GR1File.cs index 5cf49b4..7982c46 100644 --- a/UVtools.Core/FileFormats/GR1File.cs +++ b/UVtools.Core/FileFormats/GR1File.cs @@ -338,6 +338,7 @@ public sealed class GR1File : FileFormat // Previews Parallel.For(0, previews.Length, CoreSettings.GetParallelOptions(progress), previewIndex => { + progress.PauseIfRequested(); var encodeLength = ThumbnailsOriginalSize[previewIndex].Area() * 2; if (Thumbnails[previewIndex] is null) { @@ -369,6 +370,7 @@ public sealed class GR1File : FileFormat { Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); var layer = this[layerIndex]; using (var mat = layer.LayerMat) { @@ -450,6 +452,7 @@ public sealed class GR1File : FileFormat Parallel.For(0, previews.Length, CoreSettings.GetParallelOptions(progress), previewIndex => { + progress.PauseIfRequested(); Thumbnails[previewIndex] = DecodeImage(DATATYPE_RGB565_BE, previews[previewIndex], ThumbnailsOriginalSize[previewIndex]); previews[previewIndex] = null!; }); @@ -468,7 +471,7 @@ public sealed class GR1File : FileFormat { foreach (var layerIndex in batch) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); var lineCount = BitExtensions.ToUIntBigEndian(inputFile.ReadBytes(4)); linesBytes[layerIndex] = new byte[lineCount * 6]; @@ -478,6 +481,7 @@ public sealed class GR1File : FileFormat Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using var mat = EmguExtensions.InitMat(Resolution); for (int i = 0; i < linesBytes[layerIndex].Length; i++) diff --git a/UVtools.Core/FileFormats/GooFile.cs b/UVtools.Core/FileFormats/GooFile.cs new file mode 100644 index 0000000..6c0ea74 --- /dev/null +++ b/UVtools.Core/FileFormats/GooFile.cs @@ -0,0 +1,1044 @@ +using BinarySerialization; +using Emgu.CV; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using UVtools.Core.Exceptions; +using UVtools.Core.Extensions; +using UVtools.Core.Layers; +using UVtools.Core.Operations; + +namespace UVtools.Core.FileFormats; + +public sealed class GooFile : FileFormat +{ + + #region Constants + + private const string FileVersion = "V3.0"; + + private static readonly byte[] FileMagic = { + 0x07, 0x00, 0x00, 0x00, + 0x44, 0x4C, 0x50, 0x00 + }; + + private static byte[] Delimiter => new byte[]{ 0x0D, 0x0A }; + + private static byte LayerMagic => 0x55; + + #endregion + + #region Sub Classes + + public class FileHeader + { + [FieldEndianness(Endianness.Big)] [FieldOrder(0)] [FieldLength(4)] public string Version { get; set; } = FileVersion; + [FieldOrder(1)] [FieldCount(8)] public byte[] Magic { get; set; } = FileMagic; + [FieldOrder(2)] [FieldLength(32)] [SerializeAs(SerializedType.TerminatedString)] public string SoftwareName { get; set; } = About.Software; + [FieldOrder(3)] [FieldLength(24)] [SerializeAs(SerializedType.TerminatedString)] public string SoftwareVersion { get; set; } = About.VersionStr; + [FieldOrder(4)] [FieldLength(24)] [SerializeAs(SerializedType.TerminatedString)] public string FileCreateTime { get; set; } = DateTime.UtcNow.ToString("yyyy-mm-dd hh:mm:ss"); + [FieldOrder(5)] [FieldLength(32)] [SerializeAs(SerializedType.TerminatedString)] public string MachineName { get; set; } = DefaultMachineName; + [FieldOrder(6)] [FieldLength(32)] [SerializeAs(SerializedType.TerminatedString)] public string MachineType { get; set; } = "DLP"; + [FieldOrder(7)] [FieldLength(32)] [SerializeAs(SerializedType.TerminatedString)] public string ProfileName { get; set; } = About.Software; + [FieldEndianness(Endianness.Big)] [FieldOrder(8)] public ushort AntiAliasingLevel { get; set; } = 8; + [FieldEndianness(Endianness.Big)] [FieldOrder(9)] public ushort GreyLevel { get; set; } = 1; + [FieldEndianness(Endianness.Big)] [FieldOrder(10)] public ushort BlurLevel { get; set; } = 0; + [FieldOrder(11)] [FieldCount(116 * 116 * 2)] public byte[] SmallPreview565 { get; set; } = Array.Empty(); + [FieldOrder(12)] [FieldCount(2)] public byte[] SmallPreviewDelimiter { get; set; } = Delimiter; + [FieldOrder(13)] [FieldCount(290 * 290 * 2)] public byte[] BigPreview565 { get; set; } = Array.Empty(); + [FieldOrder(14)] [FieldCount(2)] public byte[] BigPreviewDelimiter { get; set; } = Delimiter; + [FieldEndianness(Endianness.Big)] [FieldOrder(15)] public uint LayerCount { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(16)] public ushort ResolutionX { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(17)] public ushort ResolutionY { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(18)] public bool MirrorX { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(19)] public bool MirrorY { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(20)] public float DisplayWidth { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(21)] public float DisplayHeight { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(22)] public float MachineZ { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(23)] public float LayerHeight { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(24)] public float ExposureTime { get; set; } + /// + /// 0: Light off delay mode | 1:Wait time mode + /// + [FieldEndianness(Endianness.Big)] [FieldOrder(25)] public byte DelayMode { get; set; } = 1; + [FieldEndianness(Endianness.Big)] [FieldOrder(26)] public float LightOffDelay { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(27)] public float BottomWaitTimeAfterCure { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(28)] public float BottomWaitTimeAfterLift { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(29)] public float BottomWaitTimeBeforeCure { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(30)] public float WaitTimeAfterCure { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(31)] public float WaitTimeAfterLift { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(32)] public float WaitTimeBeforeCure { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(33)] public float BottomExposureTime { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(34)] public uint BottomLayerCount { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(35)] public float BottomLiftHeight { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(36)] public float BottomLiftSpeed { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(37)] public float LiftHeight { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(38)] public float LiftSpeed { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(39)] public float BottomRetractHeight { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(40)] public float BottomRetractSpeed { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(41)] public float RetractHeight { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(42)] public float RetractSpeed { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(43)] public float BottomLiftHeight2 { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(44)] public float BottomLiftSpeed2 { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(45)] public float LiftHeight2 { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(46)] public float LiftSpeed2 { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(47)] public float BottomRetractHeight2 { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(48)] public float BottomRetractSpeed2 { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(49)] public float RetractHeight2 { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(50)] public float RetractSpeed2 { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(51)] public ushort BottomLightPWM { get; set; } = DefaultBottomLightPWM; + [FieldEndianness(Endianness.Big)] [FieldOrder(52)] public ushort LightPWM { get; set; } = DefaultLightPWM; + /// + /// 0: Normal mode + /// 1: Advance mode, printing use the value of "Layer Definition Content" + /// + [FieldEndianness(Endianness.Big)] [FieldOrder(53)] public bool PerLayerSettings { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(54)] public uint PrintTime { get; set; } + /// + /// // The volume of all parts. unit: mm3 + /// + [FieldEndianness(Endianness.Big)] [FieldOrder(55)] public float Volume { get; set; } + /// + /// The weight of all parts. unit: g + /// + [FieldEndianness(Endianness.Big)] [FieldOrder(56)] public float MaterialGrams { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(57)] public float MaterialCost { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(58)] [FieldLength(8)] [SerializeAs(SerializedType.TerminatedString)] public string PriceCurrencySymbol { get; set; } = "$"; + [FieldEndianness(Endianness.Big)] [FieldOrder(59)] public uint LayerDefAddress { get; set; } // 195477 + /// + /// 0:The range of pixel's gray value is from 0x0 ~ 0xf + /// 1:The range of pixel's gray value is from 0x0 ~ 0xff + /// + [FieldEndianness(Endianness.Big)] [FieldOrder(60)] public byte GrayScaleLevel { get; set; } = 1; + [FieldEndianness(Endianness.Big)] [FieldOrder(61)] public ushort TransitionLayerCount { get; set; } + + public override string ToString() + { + return $"{nameof(Version)}: {Version}, {nameof(Magic)}: {Magic}, {nameof(SoftwareName)}: {SoftwareName}, {nameof(SoftwareVersion)}: {SoftwareVersion}, {nameof(FileCreateTime)}: {FileCreateTime}, {nameof(MachineName)}: {MachineName}, {nameof(MachineType)}: {MachineType}, {nameof(ProfileName)}: {ProfileName}, {nameof(AntiAliasingLevel)}: {AntiAliasingLevel}, {nameof(GreyLevel)}: {GreyLevel}, {nameof(BlurLevel)}: {BlurLevel}, {nameof(SmallPreview565)}: {SmallPreview565}, {nameof(SmallPreviewDelimiter)}: {SmallPreviewDelimiter}, {nameof(BigPreview565)}: {BigPreview565}, {nameof(BigPreviewDelimiter)}: {BigPreviewDelimiter}, {nameof(LayerCount)}: {LayerCount}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(MirrorX)}: {MirrorX}, {nameof(MirrorY)}: {MirrorY}, {nameof(DisplayWidth)}: {DisplayWidth}, {nameof(DisplayHeight)}: {DisplayHeight}, {nameof(MachineZ)}: {MachineZ}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(DelayMode)}: {DelayMode}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(BottomWaitTimeAfterCure)}: {BottomWaitTimeAfterCure}, {nameof(BottomWaitTimeAfterLift)}: {BottomWaitTimeAfterLift}, {nameof(BottomWaitTimeBeforeCure)}: {BottomWaitTimeBeforeCure}, {nameof(WaitTimeAfterCure)}: {WaitTimeAfterCure}, {nameof(WaitTimeAfterLift)}: {WaitTimeAfterLift}, {nameof(WaitTimeBeforeCure)}: {WaitTimeBeforeCure}, {nameof(BottomExposureTime)}: {BottomExposureTime}, {nameof(BottomLayerCount)}: {BottomLayerCount}, {nameof(BottomLiftHeight)}: {BottomLiftHeight}, {nameof(BottomLiftSpeed)}: {BottomLiftSpeed}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(BottomRetractHeight)}: {BottomRetractHeight}, {nameof(BottomRetractSpeed)}: {BottomRetractSpeed}, {nameof(RetractHeight)}: {RetractHeight}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(BottomLiftHeight2)}: {BottomLiftHeight2}, {nameof(BottomLiftSpeed2)}: {BottomLiftSpeed2}, {nameof(LiftHeight2)}: {LiftHeight2}, {nameof(LiftSpeed2)}: {LiftSpeed2}, {nameof(BottomRetractHeight2)}: {BottomRetractHeight2}, {nameof(BottomRetractSpeed2)}: {BottomRetractSpeed2}, {nameof(RetractHeight2)}: {RetractHeight2}, {nameof(RetractSpeed2)}: {RetractSpeed2}, {nameof(BottomLightPWM)}: {BottomLightPWM}, {nameof(LightPWM)}: {LightPWM}, {nameof(PerLayerSettings)}: {PerLayerSettings}, {nameof(PrintTime)}: {PrintTime}, {nameof(Volume)}: {Volume}, {nameof(MaterialGrams)}: {MaterialGrams}, {nameof(MaterialCost)}: {MaterialCost}, {nameof(PriceCurrencySymbol)}: {PriceCurrencySymbol}, {nameof(LayerDefAddress)}: {LayerDefAddress}, {nameof(GrayScaleLevel)}: {GrayScaleLevel}, {nameof(TransitionLayerCount)}: {TransitionLayerCount}"; + } + } + + public class LayerDef + { + /// + /// 0: reserve + /// 1: current layer pause printing + /// + [FieldEndianness(Endianness.Big)] [FieldOrder(0)] public ushort Pause { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(1)] public float PausePositionZ { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(2)] public float PositionZ { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(3)] public float ExposureTime { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(4)] public float LightOffDelay { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(5)] public float WaitTimeAfterCure { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(6)] public float WaitTimeAfterLift { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(7)] public float WaitTimeBeforeCure { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(8)] public float LiftHeight { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(9)] public float LiftSpeed { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(10)] public float LiftHeight2 { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(11)] public float LiftSpeed2 { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(12)] public float RetractHeight { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(13)] public float RetractSpeed { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(14)] public float RetractHeight2 { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(15)] public float RetractSpeed2 { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(16)] public ushort LightPWM { get; set; } + [FieldEndianness(Endianness.Big)] [FieldOrder(17)] [FieldCount(2)] public byte[] DelimiterData { get; set; } = Delimiter; + [FieldEndianness(Endianness.Big)] [FieldOrder(18)] public uint DataLength { get; set; } + + + [Ignore] public GooFile? Parent { get; set; } + + [Ignore] public byte[] EncodedRle { get; set; } = Array.Empty(); + + // DelimiterRLE + + public LayerDef() { } + + public LayerDef(GooFile parent, Layer layer) + { + Parent = parent; + PausePositionZ = parent.MachineZ; + SetFrom(layer); + } + + public void SetFrom(Layer layer) + { + PositionZ = layer.PositionZ; + ExposureTime = layer.ExposureTime; + LightOffDelay = layer.LightOffDelay; + LiftHeight = layer.LiftHeight; + LiftSpeed = layer.LiftSpeed; + LiftHeight2 = layer.LiftHeight2; + LiftSpeed2 = layer.LiftSpeed2; + RetractSpeed = layer.RetractSpeed; + RetractHeight2 = layer.RetractHeight2; + RetractSpeed2 = layer.RetractSpeed2; + WaitTimeAfterCure = layer.WaitTimeAfterCure; + WaitTimeAfterLift = layer.WaitTimeAfterLift; + WaitTimeBeforeCure = layer.WaitTimeBeforeCure; + LightPWM = layer.LightPWM; + } + + public void CopyTo(Layer layer) + { + layer.PositionZ = PositionZ; + layer.ExposureTime = ExposureTime; + layer.LightOffDelay = LightOffDelay; + layer.LiftHeight = LiftHeight; + layer.LiftSpeed = LiftSpeed; + layer.LiftHeight2 = LiftHeight2; + layer.LiftSpeed2 = LiftSpeed2; + layer.RetractSpeed = RetractSpeed; + layer.RetractHeight2 = RetractHeight2; + layer.RetractSpeed2 = RetractSpeed2; + layer.WaitTimeAfterCure = WaitTimeAfterCure; + layer.WaitTimeAfterLift = WaitTimeAfterLift; + layer.WaitTimeBeforeCure = WaitTimeBeforeCure; + layer.LightPWM = (byte)LightPWM; + } + + + public Mat DecodeImage(uint layerIndex, bool consumeRle = true) + { + var mat = EmguExtensions.InitMat(Parent!.Resolution); + + if (DataLength <= 3) return mat; + + if (EncodedRle![0] != LayerMagic) throw new MessageException($"RLE for layer {layerIndex} is corrupted, should start with {LayerMagic} but got {EncodedRle[0]}"); + + int pixel = 0; + var lastByteIndex = DataLength - 1; + byte color = 0; + byte checkSum = 0; + + for (var i = 1; i < lastByteIndex; i++) + { + // Calculate checksum + unchecked + { + checkSum += EncodedRle[i]; + } + } + checkSum = (byte)~checkSum; + if (EncodedRle[^1] != checkSum) throw new MessageException($"Decoded RLE for layer {layerIndex} is corrupted, expected checksum <{EncodedRle[^1]}>, got <{checkSum}>"); + + for (var i = 1; i < lastByteIndex; i++) + { + /* Byte0[7:6]: The type of chunk + * (0x0) 0 0 This chunk contain all 0x0 pixels + * (0x1) 0 1 This chunk contain the value of gray between 0x1 to 0xfe. The gray value is after byte0. + * (0x2) 1 0 This chunk contain the diff value from the previous pixel + * (0x3) 1 1 This chunk contain all 0xff pixels + */ + byte chunkType = (byte)(EncodedRle[i] >> 6); + int stride = 0; + + int strideIndex0 = i; + int strideIndex1 = i + 1; + int strideIndex2 = i + 2; + int strideIndex3 = i + 3; + + if (chunkType == 0x0) // 0 0 + { + color = byte.MinValue; + } + else if (chunkType == 0x1) // 0 1 + { + color = EncodedRle[++i]; + strideIndex1++; + strideIndex2++; + strideIndex3++; + } + else if (chunkType == 0x2) // 1 0 + { + /* When byte0[7:6] is [1:0], the meaning of byte0[5:4] follow below definition: + * 0 0 byte0[3:0] is the positive diff value. that's mean + current value subtract previous value is bigger + than 0. The range is from 0 to 15. 0x0 map to 0. + 0xf map to 15. + * 0 1 byte0[3:0] is the positive diff value. And this + value's run-length represent by byte1[7:0] + * 1 0 byte0[3:0] is the negative diff value.that's mean + current value subtract previous value is smaller + than 0. The range is from 0 to 15. 0x0 map to 0. + 0xf map to 15. + * 1 1 byte0[3:0] is the negative diff value. And this + value's run-length represent by byte1[7:0] + */ + byte diffType = (byte)(EncodedRle[i] >> 4 & 0x3); + byte diffValue = (byte)(EncodedRle[i] & 0xf); + if (diffType == 0x0) + { + color += diffValue; + stride = 1; + } + else if (diffType == 0x1) + { + color += diffValue; + stride = EncodedRle[++i]; + } + else if (diffType == 0x2) + { + color -= diffValue; + stride = 1; + } + else if (diffType == 0x3) + { + color -= diffValue; + stride = EncodedRle[++i]; + } + else + { + throw new ArgumentOutOfRangeException(nameof(diffType), $"Diff type {diffType:X} is out of range, can only go up to 0x3."); + } + } + else if (chunkType == 0x3) // 1 1 + { + color = byte.MaxValue; + } + else + { + throw new ArgumentOutOfRangeException(nameof(chunkType), $"Chunk type {chunkType:X} is out of range, can only go up to 0x3."); + } + + if (chunkType != 0x2) + { + /* Byte0[5:4]: The length of chunk except when byte0[7:6] is [1 0] + * (0x0) 0 0 4-bit run-length use byte0[3:0] + * (0x1) 0 1 The run-length consist by byte1[7:0] and byte0[3:0] + * (0x2) 1 0 The run-length consist by byte1[7:0], byte2[7:0] and byte0[3:0] + * (0x3) 1 1 The run-length consist by byte1[7:0], byte2[7:0], byte3[7:0] and byte0[3:0] + */ + byte chunkLength = (byte) (EncodedRle[strideIndex0] >> 4 & 0x3); + switch (chunkLength) + { + case 0x0: + stride = EncodedRle[strideIndex0] & 0xF; + break; + case 0x1: + stride = (EncodedRle[strideIndex1] << 4) + (EncodedRle[strideIndex0] & 0xF); + i += 1; + break; + case 0x2: + stride = (EncodedRle[strideIndex1] << 12) + (EncodedRle[strideIndex2] << 4) + (EncodedRle[strideIndex0] & 0xF); + i += 2; + break; + case 0x3: + stride = (EncodedRle[strideIndex1] << 20) + (EncodedRle[strideIndex2] << 12) + (EncodedRle[strideIndex3] << 4) + (EncodedRle[strideIndex0] & 0xF); + i += 3; + break; + default: + throw new ArgumentOutOfRangeException(nameof(chunkLength), $"Chunk length {chunkLength:X} is out of range, can only go up to 0x3."); + } + } + + mat.FillSpan(ref pixel, stride, color); + } + + if (consumeRle) EncodedRle = null; + + return mat; + } + + public byte[] EncodeImage(Mat image, uint layerIndex) + { + List rle = new(){ LayerMagic }; + byte previousColor = 0; + byte currentColor = 0; + uint stride = 0; + byte checkSum = 0; + var span = image.GetDataByteSpan(); + + void AddRep() + { + if (stride == 0) + { + return; + } + + int firstByteIndex = rle.Count; + rle.Add(0); + + // Difference mode + var differenceColor = (byte) Math.Abs(currentColor - previousColor); + if (stride <= byte.MaxValue && differenceColor <= 0xF) + { + rle[firstByteIndex] = (byte) (0x2 << 6 | (differenceColor & 0xF)); + if (stride > 1) + { + rle[firstByteIndex] |= 0x1 << 4; + } + + if (previousColor > currentColor) + { + rle[firstByteIndex] |= 0x1 << 5; + } + + if (stride > 1) + { + rle.Add((byte) stride); + } + } + else + { + /*if (currentColor == byte.MinValue) + { + 0 0 This chunk contain all 0x0 pixels + firstByte |= 0x00 << 6; + }*/ + if (currentColor == byte.MaxValue) + { + // 1 1 This chunk contain all 0xff pixels + rle[firstByteIndex] |= 0x3 << 6; + } + else if (currentColor > byte.MinValue) + { + // 0 1 This chunk contain the value of gray between 0x1 to 0xfe. The gray value is after byte0. + rle[firstByteIndex] |= 0x1 << 6; + rle.Add(currentColor); + } + + rle[firstByteIndex] |= (byte) (stride & 0xF); + if (stride <= 0xF) + { + return; + } + + if (stride <= 0xFFF) + { + rle[firstByteIndex] |= 0x1 << 4; + rle.Add((byte) (stride >> 4)); + return; + } + + if (stride <= 0xFFFFF) + { + rle[firstByteIndex] |= 0x2 << 4; + rle.Add((byte) (stride >> 12)); + rle.Add((byte) (stride >> 4)); + return; + } + + if (stride <= 0xFFFFFFF) + { + rle[firstByteIndex] |= 0x3 << 4; + rle.Add((byte) (stride >> 20)); + rle.Add((byte) (stride >> 12)); + rle.Add((byte) (stride >> 4)); + return; + } + } + } + + for (int i = 0; i < span.Length; i++) + { + if (currentColor == span[i]) + { + stride++; + } + else + { + AddRep(); + stride = 1; + previousColor = currentColor; + currentColor = span[i]; + } + } + + AddRep(); + + // Calculate checksum + for (int i = 1; i < rle.Count; i++) + { + unchecked + { + checkSum += rle[i]; + } + } + + rle.Add((byte) ~checkSum); + + EncodedRle = rle.ToArray(); + DataLength = (uint)EncodedRle.Length; + + return EncodedRle; + } + + public override string ToString() + { + return $"{nameof(Pause)}: {Pause}, {nameof(PausePositionZ)}: {PausePositionZ}, {nameof(PositionZ)}: {PositionZ}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(WaitTimeAfterCure)}: {WaitTimeAfterCure}, {nameof(WaitTimeAfterLift)}: {WaitTimeAfterLift}, {nameof(WaitTimeBeforeCure)}: {WaitTimeBeforeCure}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(LiftHeight2)}: {LiftHeight2}, {nameof(LiftSpeed2)}: {LiftSpeed2}, {nameof(RetractHeight)}: {RetractHeight}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(RetractHeight2)}: {RetractHeight2}, {nameof(RetractSpeed2)}: {RetractSpeed2}, {nameof(LightPWM)}: {LightPWM}, {nameof(DelimiterData)}: {DelimiterData}, {nameof(DataLength)}: {DataLength}, {nameof(Parent)}: {Parent}, {nameof(EncodedRle)}: {EncodedRle}"; + } + } + + public class FileFooter + { + [FieldOrder(0)] public byte Padding1 { get; set; } + [FieldOrder(1)] public byte Padding2 { get; set; } + [FieldOrder(2)] public byte Padding3 { get; set; } + [FieldOrder(3)] [FieldCount(8)] public byte[] Magic { get; set; } = FileMagic; + + public override string ToString() + { + return $"{nameof(Padding1)}: {Padding1}, {nameof(Padding2)}: {Padding2}, {nameof(Padding3)}: {Padding3}, {nameof(Magic)}: {Magic}"; + } + } + + #endregion + + #region Properties + public override FileFormatType FileType => FileFormatType.Binary; + + public override FileExtension[] FileExtensions { get; } = { + new(typeof(GooFile), "goo", "Elegoo GOO"), + }; + + public override Size[]? ThumbnailsOriginalSize { get; } = + { + new(116, 116), + new(290, 290) + }; + + public FileHeader Header { get; private set; } = new(); + + public LayerDef[]? LayersDefinition { get; private set; } + + public FileFooter Footer { get; private set; } = new(); + + public override PrintParameterModifier[]? PrintParameterModifiers { get; } = { + PrintParameterModifier.BottomLayerCount, + PrintParameterModifier.TransitionLayerCount, + + //PrintParameterModifier.BottomLightOffDelay, + PrintParameterModifier.LightOffDelay, + + PrintParameterModifier.BottomWaitTimeBeforeCure, + PrintParameterModifier.WaitTimeBeforeCure, + + PrintParameterModifier.BottomExposureTime, + PrintParameterModifier.ExposureTime, + + PrintParameterModifier.BottomWaitTimeAfterCure, + PrintParameterModifier.WaitTimeAfterCure, + + PrintParameterModifier.BottomLiftHeight, + PrintParameterModifier.BottomLiftSpeed, + PrintParameterModifier.LiftHeight, + PrintParameterModifier.LiftSpeed, + PrintParameterModifier.BottomLiftHeight2, + PrintParameterModifier.BottomLiftSpeed2, + PrintParameterModifier.LiftHeight2, + PrintParameterModifier.LiftSpeed2, + + PrintParameterModifier.BottomWaitTimeAfterLift, + PrintParameterModifier.WaitTimeAfterLift, + + PrintParameterModifier.BottomRetractSpeed, + PrintParameterModifier.RetractSpeed, + PrintParameterModifier.BottomRetractHeight2, + PrintParameterModifier.BottomRetractSpeed2, + PrintParameterModifier.RetractHeight2, + PrintParameterModifier.RetractSpeed2, + + PrintParameterModifier.BottomLightPWM, + PrintParameterModifier.LightPWM + }; + + public override PrintParameterModifier[]? PrintParameterPerLayerModifiers { get; } = { + PrintParameterModifier.PositionZ, + PrintParameterModifier.LightOffDelay, + PrintParameterModifier.WaitTimeBeforeCure, + PrintParameterModifier.ExposureTime, + PrintParameterModifier.WaitTimeAfterCure, + PrintParameterModifier.LiftHeight, + PrintParameterModifier.LiftSpeed, + PrintParameterModifier.LiftHeight2, + PrintParameterModifier.LiftSpeed2, + PrintParameterModifier.WaitTimeAfterLift, + PrintParameterModifier.RetractSpeed, + PrintParameterModifier.RetractHeight2, + PrintParameterModifier.RetractSpeed2, + PrintParameterModifier.LightPWM + }; + + public override uint ResolutionX + { + get => Header.ResolutionX; + set => base.ResolutionX = Header.ResolutionX = (ushort)value; + } + + public override uint ResolutionY + { + get => Header.ResolutionY; + set => base.ResolutionY = Header.ResolutionY = (ushort)value; + } + + public override float LayerHeight + { + get => Header.LayerHeight; + set + { + Header.LayerHeight = value; + RaisePropertyChanged(); + } + } + + public override float DisplayWidth + { + get => Header.DisplayWidth; + set => base.DisplayWidth = Header.DisplayWidth = RoundDisplaySize(value); + } + + public override float DisplayHeight + { + get => Header.DisplayHeight; + set => base.DisplayHeight = Header.DisplayHeight = RoundDisplaySize(value); + } + + public override float MachineZ + { + get => Header.MachineZ > 0 ? Header.MachineZ : base.MachineZ; + set => base.MachineZ = Header.MachineZ = (float)Math.Round(value, 2); + } + + public override FlipDirection DisplayMirror + { + get + { + if (Header is {MirrorX: true, MirrorY: true}) return FlipDirection.Both; + if (Header.MirrorX) return FlipDirection.Horizontally; + if (Header.MirrorY) return FlipDirection.Vertically; + return FlipDirection.None; + } + set + { + switch (value) + { + case FlipDirection.None: + Header.MirrorX = false; + Header.MirrorY = false; + break; + case FlipDirection.Horizontally: + Header.MirrorX = true; + Header.MirrorY = false; + break; + case FlipDirection.Vertically: + Header.MirrorX = false; + Header.MirrorY = true; + break; + case FlipDirection.Both: + Header.MirrorX = true; + Header.MirrorY = true; + break; + default: + throw new ArgumentOutOfRangeException(nameof(value), value, null); + } + RaisePropertyChanged(); + } + } + + public override byte AntiAliasing + { + get => (byte)(Header.AntiAliasingLevel); + set => base.AntiAliasing = (byte)(Header.AntiAliasingLevel = value); + } + + public override uint LayerCount + { + get => base.LayerCount; + set => base.LayerCount = Header.LayerCount = base.LayerCount; + } + + public override ushort BottomLayerCount + { + get => (ushort) Header.BottomLayerCount; + set => base.BottomLayerCount = (ushort) (Header.BottomLayerCount = value); + } + + public override TransitionLayerTypes TransitionLayerType => TransitionLayerTypes.Software; + + public override ushort TransitionLayerCount + { + get => Header.TransitionLayerCount; + set => base.TransitionLayerCount = Header.TransitionLayerCount = (ushort) Math.Min(value, MaximumPossibleTransitionLayerCount); + } + + public override float BottomLightOffDelay => Header.LightOffDelay; + + public override float LightOffDelay + { + get => Header.LightOffDelay; + set => base.LightOffDelay = Header.LightOffDelay = (float)Math.Round(value, 2); + } + + public override float BottomWaitTimeBeforeCure + { + get => base.BottomWaitTimeBeforeCure > 0 ? base.BottomWaitTimeBeforeCure : this.FirstOrDefault(layer => layer is { IsBottomLayer: true, IsDummy: false })?.WaitTimeBeforeCure ?? 0; + set => base.BottomWaitTimeBeforeCure = value; + } + + + public override float WaitTimeBeforeCure + { + get => Header.WaitTimeBeforeCure; + set + { + base.WaitTimeBeforeCure = Header.WaitTimeBeforeCure = Header.WaitTimeBeforeCure = (float)Math.Round(value, 2); + if (value > 0) + { + BottomLightOffDelay = 0; + LightOffDelay = 0; + } + } + } + + public override float BottomExposureTime + { + get => Header.BottomExposureTime; + set => base.BottomExposureTime = Header.BottomExposureTime = (float)Math.Round(value, 2); + } + + public override float BottomWaitTimeAfterCure + { + get => base.BottomWaitTimeAfterCure > 0 ? base.BottomWaitTimeAfterCure : this.FirstOrDefault(layer => layer is { IsBottomLayer: true, IsDummy: false })?.WaitTimeAfterCure ?? 0; + set => base.BottomWaitTimeAfterCure = value; + } + + public override float WaitTimeAfterCure + { + get => Header.WaitTimeAfterCure; + set + { + base.WaitTimeAfterCure = Header.WaitTimeAfterCure = (float)Math.Round(value, 2); + if (value > 0) + { + BottomLightOffDelay = 0; + LightOffDelay = 0; + } + } + } + + public override float ExposureTime + { + get => Header.ExposureTime; + set => base.ExposureTime = Header.ExposureTime = (float)Math.Round(value, 2); + } + + public override float BottomLiftHeight + { + get => Header.BottomLiftHeight; + set => base.BottomLiftHeight = Header.BottomLiftHeight = (float)Math.Round(value, 2); + } + + public override float BottomLiftSpeed + { + get => Header.BottomLiftSpeed; + set => base.BottomLiftSpeed = Header.BottomLiftSpeed = (float)Math.Round(value, 2); + } + + public override float LiftHeight + { + get => Header.LiftHeight; + set => base.LiftHeight = Header.LiftHeight = (float)Math.Round(value, 2); + } + + public override float LiftSpeed + { + get => Header.LiftSpeed; + set => base.LiftSpeed = Header.LiftSpeed = (float)Math.Round(value, 2); + } + + public override float BottomLiftHeight2 + { + get => Header.BottomLiftHeight2; + set => base.BottomLiftHeight2 = Header.BottomLiftHeight2 = (float)Math.Round(value, 2); + } + + public override float BottomLiftSpeed2 + { + get => Header.BottomLiftSpeed2; + set => base.BottomLiftSpeed2 = Header.BottomLiftSpeed2 = (float)Math.Round(value, 2); + } + + public override float LiftHeight2 + { + get => Header.LiftHeight2; + set => base.LiftHeight2 = Header.LiftHeight2 = (float)Math.Round(value, 2); + } + + public override float LiftSpeed2 + { + get => Header.LiftSpeed2; + set => base.LiftSpeed2 = Header.LiftSpeed2 = (float)Math.Round(value, 2); + } + + public override float BottomWaitTimeAfterLift + { + get => base.BottomWaitTimeAfterLift > 0 ? base.BottomWaitTimeAfterLift : this.FirstOrDefault(layer => layer is { IsBottomLayer: true, IsDummy: false })?.WaitTimeAfterLift ?? 0; + set => base.BottomWaitTimeAfterLift = value; + } + + public override float WaitTimeAfterLift + { + get => Header.WaitTimeAfterLift; + set + { + base.WaitTimeAfterLift = Header.WaitTimeAfterLift = Header.WaitTimeAfterLift = (float)Math.Round(value, 2); + if (value > 0) + { + BottomLightOffDelay = 0; + LightOffDelay = 0; + } + } + } + + public override float BottomRetractSpeed + { + get => Header.BottomRetractSpeed; + set => base.BottomRetractSpeed = Header.BottomRetractSpeed = (float)Math.Round(value, 2); + } + + public override float RetractSpeed + { + get => Header.RetractSpeed; + set => base.RetractSpeed = Header.RetractSpeed = (float)Math.Round(value, 2); + } + + public override float BottomRetractHeight2 + { + get => Header.BottomRetractHeight2; + set + { + value = Math.Clamp((float)Math.Round(value, 2), 0, BottomRetractHeightTotal); + base.BottomRetractHeight2 = Header.BottomRetractHeight2 = value; + } + } + + public override float BottomRetractSpeed2 + { + get => Header.BottomRetractSpeed2; + set => base.BottomRetractSpeed2 = Header.BottomRetractSpeed2 = (float)Math.Round(value, 2); + } + + public override float RetractHeight2 + { + get => Header.RetractHeight2; + set + { + value = Math.Clamp((float)Math.Round(value, 2), 0, RetractHeightTotal); + base.RetractHeight2 = Header.RetractHeight2 = value; + } + } + + public override float RetractSpeed2 + { + get => Header.RetractSpeed2; + set => base.RetractSpeed2 = Header.RetractSpeed2 = (float)Math.Round(value, 2); + } + + public override byte BottomLightPWM + { + get => (byte)Header.BottomLightPWM; + set => base.BottomLightPWM = (byte)(Header.BottomLightPWM = value); + } + + public override byte LightPWM + { + get => (byte)Header.LightPWM; + set => base.LightPWM = (byte)(Header.LightPWM = value); + } + + public override float PrintTime + { + get => base.PrintTime; + set + { + base.PrintTime = value; + Header.PrintTime = (uint)base.PrintTime; + } + } + + public override string MachineName + { + get => Header.MachineName; + set => base.MachineName = Header.MachineName = value; + } + + public override float MaterialGrams + { + get => Header.MaterialGrams; + set => base.MaterialGrams = Header.MaterialGrams = (float)Math.Round(value, 3); + } + + public override float MaterialCost + { + get => (float)Math.Round(Header.MaterialCost, 3); + set => base.MaterialCost = Header.MaterialCost = (float)Math.Round(value, 3); + } + + public override object[] Configs => new object[] { Header, Footer }; + + #endregion + + #region Constructors + public GooFile() + { + } + #endregion + + #region Methods + + protected override void DecodeInternally(OperationProgress progress) + { + using var inputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Read); + Header = Helpers.Deserialize(inputFile); + Debug.WriteLine($"Header: {Header}"); + + var expectedMagic = FileMagic; + + if (!Header.Magic.SequenceEqual(expectedMagic)) + { + throw new FileLoadException("Not a valid GOO file! Magic value mismatch", FileFullPath); + } + + progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount); + + Thumbnails[0] = DecodeImage(DATATYPE_RGB565_BE, Header.SmallPreview565, ThumbnailsOriginalSize![0]); + progress++; + Thumbnails[1] = DecodeImage(DATATYPE_RGB565_BE, Header.BigPreview565, ThumbnailsOriginalSize[1]); + progress++; + + + progress.Reset(OperationProgress.StatusDecodeLayers, Header.LayerCount); + Init(Header.LayerCount, DecodeType == FileDecodeType.Partial); + LayersDefinition = new LayerDef[LayerCount]; + inputFile.Seek(Header.LayerDefAddress, SeekOrigin.Begin); + + foreach (var batch in BatchLayersIndexes()) + { + foreach (var layerIndex in batch) + { + progress.PauseOrCancelIfRequested(); + + LayersDefinition[layerIndex] = Helpers.Deserialize(inputFile); + LayersDefinition[layerIndex].Parent = this; + if (DecodeType == FileDecodeType.Full) + { + LayersDefinition[layerIndex].EncodedRle = inputFile.ReadBytes(LayersDefinition[layerIndex].DataLength); + } + else + { + inputFile.Seek(LayersDefinition[layerIndex].DataLength, SeekOrigin.Current); + } + + inputFile.Seek(2, SeekOrigin.Current); // \n + Debug.WriteLine($"layer[{layerIndex}]: {LayersDefinition[layerIndex]}"); + } + + if (DecodeType == FileDecodeType.Full) + { + Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => + { + progress.PauseIfRequested(); + var layerDef = LayersDefinition[layerIndex]; + + _layers[layerIndex] = new Layer((uint) layerIndex, layerDef.DecodeImage((uint) layerIndex), this); + layerDef.CopyTo(_layers[layerIndex]); + + progress.LockAndIncrement(); + }); + } + } + + + Footer = Helpers.Deserialize(inputFile); + Debug.WriteLine($"Footer: {Footer}"); + if (!Footer.Magic.SequenceEqual(expectedMagic)) + { + throw new FileLoadException("Not a valid GOO file! Footer magic value mismatch", FileFullPath); + } + } + + protected override void OnBeforeEncode(bool isPartialEncode) + { + Header.PerLayerSettings = AllLayersAreUsingGlobalParameters; + Header.Volume = Volume; + Header.MaterialGrams = MaterialMilliliters; + } + + protected override void EncodeInternally(OperationProgress progress) + { + using var outputFile = new FileStream(TemporaryOutputFileFullPath, FileMode.Create, FileAccess.Write); + + progress.Reset(OperationProgress.StatusEncodePreviews, 2); + + Mat?[] thumbnails = { GetThumbnail(true), GetThumbnail(false) }; + Header.BigPreview565 = EncodeImage(DATATYPE_RGB565_BE, thumbnails[0]!); + progress++; + Header.SmallPreview565 = EncodeImage(DATATYPE_RGB565_BE, thumbnails[1]!); + progress++; + + Header.LayerDefAddress = (uint)Helpers.Serializer.SizeOf(Header); + outputFile.Seek(Header.LayerDefAddress, SeekOrigin.Begin); + + progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); + var layerData = new LayerDef[LayerCount]; + + var delimiter = Delimiter; + + foreach (var batch in BatchLayersIndexes()) + { + Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => + { + progress.PauseIfRequested(); + using (var mat = this[layerIndex].LayerMat) + { + layerData[layerIndex] = new LayerDef(this, this[layerIndex]); + layerData[layerIndex].EncodeImage(mat, (uint) layerIndex); + } + progress.LockAndIncrement(); + }); + + foreach (var layerIndex in batch) + { + progress.PauseOrCancelIfRequested(); + + outputFile.WriteSerialize(layerData[layerIndex]); + outputFile.WriteBytes(layerData[layerIndex].EncodedRle); + outputFile.WriteBytes(delimiter); + + layerData[layerIndex].EncodedRle = null!; // Free + } + } + + // Footer + outputFile.WriteSerialize(Footer); + + // Header + outputFile.Seek(0, SeekOrigin.Begin); + outputFile.WriteSerialize(Header); + + Debug.WriteLine("Encode Results:"); + Debug.WriteLine(Header); + Debug.WriteLine(Footer); + Debug.WriteLine("-End-"); + } + + protected override void PartialSaveInternally(OperationProgress progress) + { + using var outputFile = new FileStream(TemporaryOutputFileFullPath, FileMode.Open, FileAccess.Write); + outputFile.Seek(0, SeekOrigin.Begin); + + outputFile.WriteSerialize(Header); + outputFile.Seek(Header.LayerDefAddress, SeekOrigin.Begin); + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + LayersDefinition![layerIndex].SetFrom(this[layerIndex]); + outputFile.WriteSerialize(LayersDefinition[layerIndex]); + outputFile.Seek(LayersDefinition[layerIndex].DataLength + 2, SeekOrigin.Current); + } + } + #endregion +} \ No newline at end of file diff --git a/UVtools.Core/FileFormats/LGSFile.cs b/UVtools.Core/FileFormats/LGSFile.cs index 2c8add2..59f37b1 100644 --- a/UVtools.Core/FileFormats/LGSFile.cs +++ b/UVtools.Core/FileFormats/LGSFile.cs @@ -494,6 +494,7 @@ public sealed class LGSFile : FileFormat { Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using (var mat = this[layerIndex].LayerMat) { layerData[layerIndex] = new LayerDef(this); @@ -504,7 +505,7 @@ public sealed class LGSFile : FileFormat foreach (var layerIndex in batch) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); outputFile.WriteSerialize(layerData[layerIndex]); layerData[layerIndex].EncodedRle = null!; // Free this } @@ -516,7 +517,7 @@ public sealed class LGSFile : FileFormat for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); outputFile.WriteSerialize(layerData[layerIndex]); progress++; } @@ -565,7 +566,7 @@ public sealed class LGSFile : FileFormat { foreach (var layerIndex in batch) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); layerData[layerIndex] = Helpers.Deserialize(inputFile); layerData[layerIndex].Parent = this; @@ -573,6 +574,7 @@ public sealed class LGSFile : FileFormat Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using var mat = layerData[layerIndex].Decode(); _layers[layerIndex] = new Layer((uint)layerIndex, mat, this); diff --git a/UVtools.Core/FileFormats/MDLPFile.cs b/UVtools.Core/FileFormats/MDLPFile.cs index 3dfbfb3..f3891b5 100644 --- a/UVtools.Core/FileFormats/MDLPFile.cs +++ b/UVtools.Core/FileFormats/MDLPFile.cs @@ -299,6 +299,7 @@ public sealed class MDLPFile : FileFormat // Previews Parallel.For(0, previews.Length, CoreSettings.GetParallelOptions(progress), previewIndex => { + progress.PauseIfRequested(); var encodeLength = ThumbnailsOriginalSize[previewIndex].Area() * 2; if (Thumbnails[previewIndex] is null) { @@ -330,6 +331,7 @@ public sealed class MDLPFile : FileFormat { Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); var layer = this[layerIndex]; using (var mat = layer.LayerMat) { @@ -409,6 +411,7 @@ public sealed class MDLPFile : FileFormat Parallel.For(0, previews.Length, CoreSettings.GetParallelOptions(progress), previewIndex => { + progress.PauseIfRequested(); Thumbnails[previewIndex] = DecodeImage(DATATYPE_RGB565_BE, previews[previewIndex], ThumbnailsOriginalSize[previewIndex]); previews[previewIndex] = null!; }); @@ -426,7 +429,7 @@ public sealed class MDLPFile : FileFormat { foreach (var layerIndex in batch) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); var lineCount = BitExtensions.ToUIntBigEndian(inputFile.ReadBytes(4)); @@ -437,6 +440,7 @@ public sealed class MDLPFile : FileFormat Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using (var mat = EmguExtensions.InitMat(Resolution)) { diff --git a/UVtools.Core/FileFormats/OSFFile.cs b/UVtools.Core/FileFormats/OSFFile.cs index cf42e39..2933a5d 100644 --- a/UVtools.Core/FileFormats/OSFFile.cs +++ b/UVtools.Core/FileFormats/OSFFile.cs @@ -714,6 +714,7 @@ public sealed class OSFFile : FileFormat { Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); var layer = this[layerIndex]; using (var mat = layer.LayerMat) @@ -730,7 +731,7 @@ public sealed class OSFFile : FileFormat foreach (var layerIndex in batch) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); outputFile.WriteSerialize(layerDef[layerIndex]); outputFile.WriteBytes(layerDef[layerIndex].EncodedRle); layerDef[layerIndex].EncodedRle = null!; // Free this @@ -781,7 +782,7 @@ public sealed class OSFFile : FileFormat { foreach (var layerIndex in batch) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); //Debug.WriteLine($"{layerIndex}: {inputFile.Position}"); layerDef[layerIndex] = Helpers.Deserialize(inputFile); @@ -835,6 +836,7 @@ public sealed class OSFFile : FileFormat Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using var mat = layerDef[layerIndex].DecodeImage(this); _layers[layerIndex] = new Layer((uint)layerIndex, mat, this); layerDef[layerIndex].EncodedRle = null!; diff --git a/UVtools.Core/FileFormats/OSLAFile.cs b/UVtools.Core/FileFormats/OSLAFile.cs index 17f3369..d7cf08c 100644 --- a/UVtools.Core/FileFormats/OSLAFile.cs +++ b/UVtools.Core/FileFormats/OSLAFile.cs @@ -461,7 +461,7 @@ public sealed class OSLAFile : FileFormat var image = Thumbnails[i]; if(image is null) continue; - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); var bytes = EncodeImage(HeaderSettings.PreviewDataType, image); if (bytes.Length == 0) continue; @@ -504,6 +504,7 @@ public sealed class OSLAFile : FileFormat Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using (var mat = this[layerIndex].LayerMat) { layerBytes[layerIndex] = EncodeImage(HeaderSettings.LayerDataType, mat); @@ -514,7 +515,7 @@ public sealed class OSLAFile : FileFormat foreach (var layerIndex in batch) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); // Try to reuse layers var hash = CryptExtensions.ComputeSHA1Hash(layerBytes[layerIndex]); @@ -540,7 +541,7 @@ public sealed class OSLAFile : FileFormat outputFile.Seek(HeaderSettings.LayerDefinitionsAddress, SeekOrigin.Begin); for (int layerIndex = 0; layerIndex < layerDataAddresses.Length; layerIndex++) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); var layer = this[layerIndex]; var layerdef = new LayerDef(layer); @@ -599,7 +600,7 @@ public sealed class OSLAFile : FileFormat for (byte i = 0; i < HeaderSettings.PreviewCount; i++) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); var preview = Helpers.Deserialize(inputFile); Debug.Write($"Preview {i} -> "); @@ -628,7 +629,7 @@ public sealed class OSLAFile : FileFormat uint layerTableSize = 0; for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); layerDataAddresses[layerIndex] = inputFile.ReadUIntLittleEndian(); layerDef[layerIndex] = Helpers.Deserialize(inputFile); @@ -654,7 +655,7 @@ public sealed class OSLAFile : FileFormat foreach (var layerIndex in batch) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); inputFile.Seek(layerDataAddresses[layerIndex], SeekOrigin.Begin); layerBytes[layerIndex] = inputFile.ReadBytes(inputFile.ReadUIntLittleEndian()); @@ -662,6 +663,7 @@ public sealed class OSLAFile : FileFormat Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using var mat = DecodeImage(HeaderSettings.LayerDataType, layerBytes[layerIndex], Resolution); layerBytes[layerIndex] = null!; // Clean diff --git a/UVtools.Core/FileFormats/PHZFile.cs b/UVtools.Core/FileFormats/PHZFile.cs index bfc1d7b..d9b5558 100644 --- a/UVtools.Core/FileFormats/PHZFile.cs +++ b/UVtools.Core/FileFormats/PHZFile.cs @@ -981,6 +981,7 @@ public sealed class PHZFile : FileFormat { Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using (var mat = this[layerIndex].LayerMat) { LayersDefinitions[layerIndex] = new LayerDef(this, this[layerIndex]); @@ -991,7 +992,7 @@ public sealed class PHZFile : FileFormat foreach (var layerIndex in batch) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); var layerDef = LayersDefinitions[layerIndex]; LayerDef? layerDefHash = null; @@ -1093,7 +1094,7 @@ public sealed class PHZFile : FileFormat { foreach (var layerIndex in batch) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); var layerDef = Helpers.Deserialize(inputFile); layerDef.Parent = this; @@ -1113,6 +1114,7 @@ public sealed class PHZFile : FileFormat { Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using var mat = LayersDefinitions[layerIndex].Decode((uint)layerIndex); _layers[layerIndex] = new Layer((uint)layerIndex, mat, this); progress.LockAndIncrement(); diff --git a/UVtools.Core/FileFormats/PhotonSFile.cs b/UVtools.Core/FileFormats/PhotonSFile.cs index 8fe3035..19fbc37 100644 --- a/UVtools.Core/FileFormats/PhotonSFile.cs +++ b/UVtools.Core/FileFormats/PhotonSFile.cs @@ -415,6 +415,7 @@ public sealed class PhotonSFile : FileFormat { Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using (var mat = this[layerIndex].LayerMat) { layerData[layerIndex] = new LayerDef(mat); @@ -425,7 +426,7 @@ public sealed class PhotonSFile : FileFormat foreach (var layerIndex in batch) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); outputFile.WriteSerialize(layerData[layerIndex]); outputFile.WriteBytes(layerData[layerIndex].EncodedRle); @@ -468,7 +469,7 @@ public sealed class PhotonSFile : FileFormat { foreach (var layerIndex in batch) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); var layerDef = Helpers.Deserialize(inputFile); layersDefinitions[layerIndex] = layerDef; @@ -491,6 +492,7 @@ public sealed class PhotonSFile : FileFormat { Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using var mat = layersDefinitions[layerIndex].Decode(); _layers[layerIndex] = new Layer((uint)layerIndex, mat, this); progress.LockAndIncrement(); diff --git a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs index d14ee70..f46f02a 100644 --- a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs +++ b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs @@ -1760,6 +1760,7 @@ public sealed class PhotonWorkshopFile : FileFormat { Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using (var mat = this[layerIndex].LayerMat) { LayersDefinition.Layers[layerIndex] = new LayerDef(this, this[layerIndex]); @@ -1770,7 +1771,7 @@ public sealed class PhotonWorkshopFile : FileFormat foreach (var layerIndex in batch) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); var layerDef = LayersDefinition.Layers[layerIndex]; @@ -1924,7 +1925,7 @@ public sealed class PhotonWorkshopFile : FileFormat { foreach (var layerIndex in batch) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); LayersDefinition[layerIndex] = Helpers.Deserialize(inputFile); LayersDefinition[layerIndex].Parent = this; @@ -1944,6 +1945,7 @@ public sealed class PhotonWorkshopFile : FileFormat { Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using var mat = LayersDefinition[layerIndex].Decode(); _layers[layerIndex] = new Layer((uint)layerIndex, mat, this) { diff --git a/UVtools.Core/FileFormats/ZCodexFile.cs b/UVtools.Core/FileFormats/ZCodexFile.cs index 24996bf..509673e 100644 --- a/UVtools.Core/FileFormats/ZCodexFile.cs +++ b/UVtools.Core/FileFormats/ZCodexFile.cs @@ -390,7 +390,7 @@ public sealed class ZCodexFile : FileFormat float lastZPosition = 0; for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); var layer = this[layerIndex]; GCode.AppendLine($"{GCodeKeywordSlice} {layerIndex}"); diff --git a/UVtools.Core/Layers/Layer.cs b/UVtools.Core/Layers/Layer.cs index 3c8cd1b..08fc726 100644 --- a/UVtools.Core/Layers/Layer.cs +++ b/UVtools.Core/Layers/Layer.cs @@ -292,7 +292,8 @@ public class Layer : BindableBase, IEquatable, IEquatable /// /// Gets if this layer is also an transition layer /// - public bool IsTransitionLayer => SlicerFile.TransitionLayerCount <= Number; + public bool IsTransitionLayer => SlicerFile.TransitionLayerCount > 0 && + Index >= SlicerFile.BottomLayerCount && Index < SlicerFile.BottomLayerCount + SlicerFile.TransitionLayerCount; /// /// Gets the previous layer, returns null if no previous layer diff --git a/UVtools.Core/Managers/IssueManager.cs b/UVtools.Core/Managers/IssueManager.cs index 6b8e7f7..183f9bc 100644 --- a/UVtools.Core/Managers/IssueManager.cs +++ b/UVtools.Core/Managers/IssueManager.cs @@ -236,6 +236,7 @@ public sealed class IssueManager : RangeObservableCollection // Detect contours Parallel.For(0, SlicerFile.LayerCount, CoreSettings.ParallelOptions, layerIndexInt => { + progress.PauseIfRequested(); if (progress.Token.IsCancellationRequested) return; uint layerIndex = (uint)layerIndexInt; var layer = SlicerFile[layerIndex]; @@ -706,6 +707,7 @@ public sealed class IssueManager : RangeObservableCollection airContours[layerIndex] = new(); Parallel.For(0, hollows[layerIndex].Count, CoreSettings.ParallelOptions, i => { + progress.PauseIfRequested(); //for (var i = 0; i < hollows[layerIndex].Count; i++) //{ if (progress.Token.IsCancellationRequested) return; @@ -793,7 +795,10 @@ public sealed class IssueManager : RangeObservableCollection if (airContours[layerIndex] is not null) { Parallel.ForEach(airContours[layerIndex], CoreSettings.ParallelOptions, vec => - CvInvoke.DrawContours(layerAirMap, vec, -1, EmguExtensions.WhiteColor, -1) + { + progress.PauseIfRequested(); + CvInvoke.DrawContours(layerAirMap, vec, -1, EmguExtensions.WhiteColor, -1); + } ); } @@ -810,6 +815,7 @@ public sealed class IssueManager : RangeObservableCollection /* all we care about is contours the first pass thought were resin traps, since there was no access to air from the bottom */ Parallel.For(0, resinTraps[layerIndex].Count, CoreSettings.ParallelOptions, x => { + progress.PauseIfRequested(); if (progress.Token.IsCancellationRequested) return; /* check if each contour overlaps known air */ @@ -949,6 +955,7 @@ public sealed class IssueManager : RangeObservableCollection { Parallel.ForEach(listOfLayers.Where(list => list is not null), contoursGroups => { + progress.PauseIfRequested(); for (var groupIndex = 0; groupIndex < contoursGroups.Count; groupIndex++) { var contours = contoursGroups[groupIndex]; @@ -985,6 +992,7 @@ public sealed class IssueManager : RangeObservableCollection /* select new LayerIssue(this[layerIndex], LayerIssue.IssueType.ResinTrap, area.Contour, area.BoundingRectangle)) */ foreach (var trap in resinTraps[layerIndex]) { + progress.PauseIfRequested(); if (progress.Token.IsCancellationRequested) return; var area = EmguContours.GetContourArea(trap); @@ -1053,6 +1061,7 @@ public sealed class IssueManager : RangeObservableCollection foreach (var trap in suctionCups[layerIndex]) { + progress.PauseIfRequested(); if (progress.Token.IsCancellationRequested) return; var area = EmguContours.GetContourArea(trap); diff --git a/UVtools.Core/Operations/Operation.cs b/UVtools.Core/Operations/Operation.cs index 06230f6..76029d9 100644 --- a/UVtools.Core/Operations/Operation.cs +++ b/UVtools.Core/Operations/Operation.cs @@ -741,7 +741,7 @@ public abstract class Operation : BindableBase, IDisposable var result = ExecuteInternally(progress); - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); return result; } diff --git a/UVtools.Core/Operations/OperationBlur.cs b/UVtools.Core/Operations/OperationBlur.cs index 39d8c7d..806b4f1 100644 --- a/UVtools.Core/Operations/OperationBlur.cs +++ b/UVtools.Core/Operations/OperationBlur.cs @@ -133,6 +133,7 @@ public sealed class OperationBlur : Operation { Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using (var mat = SlicerFile[layerIndex].LayerMat) { Execute(mat); diff --git a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs index c5d4f94..e805061 100644 --- a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs +++ b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs @@ -1893,6 +1893,7 @@ public sealed class OperationCalibrateExposureFinder : Operation progress.ItemCount = (uint) (SlicerFile.LayerCount * table.Count); Parallel.For(0, SlicerFile.LayerCount, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); var layer = SlicerFile[layerIndex]; using var mat = layer.LayerMat; var matRoi = new Mat(mat, boundingRectangle); @@ -1970,7 +1971,7 @@ public sealed class OperationCalibrateExposureFinder : Operation Layer currentLayer = layers[0]; for (var layerIndex = 1; layerIndex < layers.Count; layerIndex++) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); progress++; var layer = layers[layerIndex]; if (currentLayer.PositionZ != layer.PositionZ || @@ -2203,7 +2204,7 @@ public sealed class OperationCalibrateExposureFinder : Operation currentHeight = Layer.RoundHeight(currentHeight); for (decimal layerHeight = _layerHeight; layerHeight <= endLayerHeight; layerHeight += _multipleLayerHeightStep) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); layerHeight = Layer.RoundHeight(layerHeight); if (_multipleExposures) diff --git a/UVtools.Core/Operations/OperationCalibrateStressTower.cs b/UVtools.Core/Operations/OperationCalibrateStressTower.cs index c6f17c6..02822ee 100644 --- a/UVtools.Core/Operations/OperationCalibrateStressTower.cs +++ b/UVtools.Core/Operations/OperationCalibrateStressTower.cs @@ -419,6 +419,7 @@ public sealed class OperationCalibrateStressTower : Operation Parallel.For(0, LayerCount, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); newLayers[layerIndex] = new Layer((uint)layerIndex, layers[layerIndex], SlicerFile) {IsModified = true}; layers[layerIndex].Dispose(); progress.LockAndIncrement(); diff --git a/UVtools.Core/Operations/OperationCalibrateTolerance.cs b/UVtools.Core/Operations/OperationCalibrateTolerance.cs index bf76c8e..ad3dbb7 100644 --- a/UVtools.Core/Operations/OperationCalibrateTolerance.cs +++ b/UVtools.Core/Operations/OperationCalibrateTolerance.cs @@ -730,6 +730,7 @@ public sealed class OperationCalibrateTolerance : Operation Parallel.For(0, LayerCount, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); newLayers[layerIndex] = new Layer((uint)layerIndex, layers[layerIndex], SlicerFile) {IsModified = true}; layers[layerIndex].Dispose(); progress.LockAndIncrement(); diff --git a/UVtools.Core/Operations/OperationChangeResolution.cs b/UVtools.Core/Operations/OperationChangeResolution.cs index 409032e..cecd653 100644 --- a/UVtools.Core/Operations/OperationChangeResolution.cs +++ b/UVtools.Core/Operations/OperationChangeResolution.cs @@ -254,6 +254,7 @@ public sealed class OperationChangeResolution : Operation Parallel.For(0, SlicerFile.LayerCount, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using var mat = SlicerFile[layerIndex].LayerMat; if (mat.Size != newSize) diff --git a/UVtools.Core/Operations/OperationDoubleExposure.cs b/UVtools.Core/Operations/OperationDoubleExposure.cs index 957e452..21f9377 100644 --- a/UVtools.Core/Operations/OperationDoubleExposure.cs +++ b/UVtools.Core/Operations/OperationDoubleExposure.cs @@ -283,6 +283,7 @@ public class OperationDoubleExposure : Operation Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); var firstLayer = SlicerFile[layerIndex]; var secondLayer = firstLayer.Clone(); var isBottomLayer = firstLayer.IsBottomLayer; diff --git a/UVtools.Core/Operations/OperationDynamicLayerHeight.cs b/UVtools.Core/Operations/OperationDynamicLayerHeight.cs index 1f60dc5..df0abc7 100644 --- a/UVtools.Core/Operations/OperationDynamicLayerHeight.cs +++ b/UVtools.Core/Operations/OperationDynamicLayerHeight.cs @@ -637,7 +637,7 @@ public sealed class OperationDynamicLayerHeight : Operation while (true) // In a stack { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); progress.ProcessedItems = layerIndex - LayerIndexStart; if (currentLayerHeight >= (float)_maximumLayerHeight || layerIndex == LayerIndexEnd) diff --git a/UVtools.Core/Operations/OperationDynamicLifts.cs b/UVtools.Core/Operations/OperationDynamicLifts.cs index c444a73..286c7cb 100644 --- a/UVtools.Core/Operations/OperationDynamicLifts.cs +++ b/UVtools.Core/Operations/OperationDynamicLifts.cs @@ -289,7 +289,7 @@ public sealed class OperationDynamicLifts : Operation for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); var calculateLayer = SlicerFile[layerIndex == 0 ? 0 : layerIndex - 1]; var setLayer = SlicerFile[layerIndex]; diff --git a/UVtools.Core/Operations/OperationFadeExposureTime.cs b/UVtools.Core/Operations/OperationFadeExposureTime.cs index cc92577..9b74f37 100644 --- a/UVtools.Core/Operations/OperationFadeExposureTime.cs +++ b/UVtools.Core/Operations/OperationFadeExposureTime.cs @@ -198,7 +198,7 @@ public class OperationFadeExposureTime : Operation var exposure = _fromExposureTime; for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); exposure += increment; SlicerFile[layerIndex].ExposureTime = (float)exposure; } diff --git a/UVtools.Core/Operations/OperationFlip.cs b/UVtools.Core/Operations/OperationFlip.cs index d4a8a06..ef565d9 100644 --- a/UVtools.Core/Operations/OperationFlip.cs +++ b/UVtools.Core/Operations/OperationFlip.cs @@ -102,6 +102,7 @@ public class OperationFlip : Operation { Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using var mat = SlicerFile[layerIndex].LayerMat; Execute(mat); SlicerFile[layerIndex].LayerMat = mat; diff --git a/UVtools.Core/Operations/OperationInfill.cs b/UVtools.Core/Operations/OperationInfill.cs index 1d578c0..e28a9d7 100644 --- a/UVtools.Core/Operations/OperationInfill.cs +++ b/UVtools.Core/Operations/OperationInfill.cs @@ -171,6 +171,7 @@ public sealed class OperationInfill : Operation Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using var mat = SlicerFile[layerIndex].LayerMat; Execute(mat, layerIndex, mask!); SlicerFile[layerIndex].LayerMat = mat; diff --git a/UVtools.Core/Operations/OperationLayerArithmetic.cs b/UVtools.Core/Operations/OperationLayerArithmetic.cs index ffdbfe9..d4442a3 100644 --- a/UVtools.Core/Operations/OperationLayerArithmetic.cs +++ b/UVtools.Core/Operations/OperationLayerArithmetic.cs @@ -247,7 +247,7 @@ public class OperationLayerArithmetic : Operation { if(!operation.IsValid) continue; - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); using var result = SlicerFile[operation.Operations[0].LayerIndex].LayerMat; using var resultRoi = GetRoiOrDefault(result); using var imageMask = GetMask(resultRoi); @@ -255,7 +255,7 @@ public class OperationLayerArithmetic : Operation progress.ItemCount = (uint)operation.Operations.Count; for (int i = 1; i < operation.Operations.Count; i++) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); using var image = SlicerFile[operation.Operations[i].LayerIndex].LayerMat; var imageRoi = GetRoiOrDefault(image); @@ -293,6 +293,7 @@ public class OperationLayerArithmetic : Operation progress.Reset("Applied layers", (uint)operation.SetLayers.Count); Parallel.ForEach(operation.SetLayers, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); progress.LockAndIncrement(); if (operation.Operations.Count == 1 || HaveROIorMask) { diff --git a/UVtools.Core/Operations/OperationLayerExportGif.cs b/UVtools.Core/Operations/OperationLayerExportGif.cs index e2fb4c5..e27ddbb 100644 --- a/UVtools.Core/Operations/OperationLayerExportGif.cs +++ b/UVtools.Core/Operations/OperationLayerExportGif.cs @@ -226,6 +226,7 @@ public sealed class OperationLayerExportGif : Operation Parallel.For(0, TotalLayers, CoreSettings.GetParallelOptions(progress), i => { + progress.PauseIfRequested(); uint layerIndex = (uint) (LayerIndexStart + i * (_skip + 1)); var layer = SlicerFile[layerIndex]; using var mat = layer.LayerMat; @@ -269,7 +270,7 @@ public sealed class OperationLayerExportGif : Operation progress.ResetNameAndProcessed("Packed layers"); foreach (var buffer in layerBuffer) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); using var stream = new MemoryStream(buffer); using var img = Image.FromStream(stream); gif.AddFrame(img, -1, GifQuality.Bit8); diff --git a/UVtools.Core/Operations/OperationLayerExportHeatMap.cs b/UVtools.Core/Operations/OperationLayerExportHeatMap.cs index 3f176de..72322b5 100644 --- a/UVtools.Core/Operations/OperationLayerExportHeatMap.cs +++ b/UVtools.Core/Operations/OperationLayerExportHeatMap.cs @@ -138,6 +138,7 @@ public sealed class OperationLayerExportHeatMap : Operation Parallel.ForEach(layerRange, CoreSettings.GetParallelOptions(progress), layer => { + progress.PauseIfRequested(); using var mat = _mergeSamePositionedLayers ? SlicerFile.GetMergedMatForSequentialPositionedLayers(layer.Index) : layer.LayerMat; diff --git a/UVtools.Core/Operations/OperationLayerExportHtml.cs b/UVtools.Core/Operations/OperationLayerExportHtml.cs index 9686ece..9ed8806 100644 --- a/UVtools.Core/Operations/OperationLayerExportHtml.cs +++ b/UVtools.Core/Operations/OperationLayerExportHtml.cs @@ -582,6 +582,7 @@ public sealed class OperationLayerExportHtml : Operation var layerSvgPath = new string[SlicerFile.LayerCount]; Parallel.For(0, SlicerFile.LayerCount, CoreSettings.ParallelOptions, layerIndex => { + progress.PauseIfRequested(); using var mat = SlicerFile[layerIndex].LayerMat; CvInvoke.Threshold(mat, mat, 127, byte.MaxValue, ThresholdType.Binary); // Remove AA diff --git a/UVtools.Core/Operations/OperationLayerExportImage.cs b/UVtools.Core/Operations/OperationLayerExportImage.cs index 3685873..83441f9 100644 --- a/UVtools.Core/Operations/OperationLayerExportImage.cs +++ b/UVtools.Core/Operations/OperationLayerExportImage.cs @@ -181,6 +181,7 @@ public sealed class OperationLayerExportImage : Operation Parallel.For(LayerIndexStart, LayerIndexEnd+1, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using var mat = SlicerFile[layerIndex].LayerMat; var matRoi = mat; if (_cropByRoi && HaveROI) diff --git a/UVtools.Core/Operations/OperationLayerExportMesh.cs b/UVtools.Core/Operations/OperationLayerExportMesh.cs index b3382f4..bc01965 100644 --- a/UVtools.Core/Operations/OperationLayerExportMesh.cs +++ b/UVtools.Core/Operations/OperationLayerExportMesh.cs @@ -342,6 +342,7 @@ public sealed class OperationLayerExportMesh : Operation /* Seems to be faster to parallel on the Y and not the X */ Parallel.For(0, curLayer!.Height, CoreSettings.GetParallelOptions(progress), y => { + progress.PauseIfRequested(); /* Collects all the faces found for this thread, will be combined into the main dictionary later */ var threadDict = new Dictionary>(); for (var x = 0; x < curLayer.Width; x++) @@ -545,6 +546,7 @@ public sealed class OperationLayerExportMesh : Operation /* We build out a 3 dimensional KD tree for each layer, having 1 big KD tree is prohibitive when you get to millions and millions of faces. */ Parallel.For(0, distinctLayers.Length, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); /* Create the KD tree for the layer, in practice there should never be dups, but just in case, set to skip */ layerTrees[layerIndex] = new KdTree(3, new FloatMath(), AddDuplicateBehavior.Skip); @@ -577,6 +579,7 @@ public sealed class OperationLayerExportMesh : Operation */ Parallel.For(0, distinctLayers.Length, CoreSettings.GetParallelOptions(progress), i => { + progress.PauseIfRequested(); /* if no faces on this layer... skip.... needed for empty layers */ if (layerTrees[i] is null) return; diff --git a/UVtools.Core/Operations/OperationLayerExportSkeleton.cs b/UVtools.Core/Operations/OperationLayerExportSkeleton.cs index efc677f..40c290e 100644 --- a/UVtools.Core/Operations/OperationLayerExportSkeleton.cs +++ b/UVtools.Core/Operations/OperationLayerExportSkeleton.cs @@ -93,6 +93,7 @@ public sealed class OperationLayerExportSkeleton : Operation Parallel.For(LayerIndexStart, LayerIndexEnd+1, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using var mat = SlicerFile[layerIndex].LayerMat; var matRoi = GetRoiOrDefault(mat); using var skeletonRoi = matRoi.Skeletonize(); diff --git a/UVtools.Core/Operations/OperationLayerImport.cs b/UVtools.Core/Operations/OperationLayerImport.cs index dc5a65c..46690fa 100644 --- a/UVtools.Core/Operations/OperationLayerImport.cs +++ b/UVtools.Core/Operations/OperationLayerImport.cs @@ -268,6 +268,7 @@ public sealed class OperationLayerImport : Operation Parallel.ForEach(keyImage, CoreSettings.GetParallelOptions(progress), pair => { + progress.PauseIfRequested(); using var mat = CvInvoke.Imread(pair.Value, ImreadModes.Grayscale); if (pair.Key == 0) format.Resolution = mat.Size; format[pair.Key] = new Layer(pair.Key, mat, format); @@ -289,7 +290,7 @@ public sealed class OperationLayerImport : Operation fileFormats.Add(fileFormat); } - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); if (fileFormats.Count == 0) return false; @@ -385,6 +386,7 @@ public sealed class OperationLayerImport : Operation progress.Reset(ProgressAction, fileFormat.LayerCount); Parallel.For(0, fileFormat.LayerCount, CoreSettings.GetParallelOptions(progress), i => { + progress.PauseIfRequested(); uint layerIndex = (uint)(_startLayerIndex + i); switch (_importType) diff --git a/UVtools.Core/Operations/OperationLayerReHeight.cs b/UVtools.Core/Operations/OperationLayerReHeight.cs index cb65550..7bacb19 100644 --- a/UVtools.Core/Operations/OperationLayerReHeight.cs +++ b/UVtools.Core/Operations/OperationLayerReHeight.cs @@ -296,7 +296,7 @@ public sealed class OperationLayerReHeight : Operation uint newLayerIndex = 0; for (uint layerIndex = 0; layerIndex < SlicerFile.LayerCount; layerIndex++) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); var oldLayer = SlicerFile[layerIndex]; for (byte i = 0; i < _selectedItem.Modifier; i++) @@ -320,6 +320,7 @@ public sealed class OperationLayerReHeight : Operation Parallel.ForEach(layerIndexes, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); var oldLayer = SlicerFile[layerIndex]; using var matSum = oldLayer.LayerMat; Mat? matXorSum = null; diff --git a/UVtools.Core/Operations/OperationLightBleedCompensation.cs b/UVtools.Core/Operations/OperationLightBleedCompensation.cs index c4c153d..915c998 100644 --- a/UVtools.Core/Operations/OperationLightBleedCompensation.cs +++ b/UVtools.Core/Operations/OperationLightBleedCompensation.cs @@ -205,6 +205,7 @@ public class OperationLightBleedCompensation : Operation if (dimMats.Length == 0) return false; Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); var layer = SlicerFile[layerIndex]; using var mat = layer.LayerMat; using var original = mat.Clone(); diff --git a/UVtools.Core/Operations/OperationLithophane.cs b/UVtools.Core/Operations/OperationLithophane.cs index dd11dda..c0836b3 100644 --- a/UVtools.Core/Operations/OperationLithophane.cs +++ b/UVtools.Core/Operations/OperationLithophane.cs @@ -394,6 +394,7 @@ public class OperationLithophane : Operation progress.Reset("Threshold levels", byte.MaxValue); Parallel.For(_startThresholdRange, _endThresholdRange, CoreSettings.GetParallelOptions(progress), threshold => { + progress.PauseIfRequested(); using var thresholdMat = new Mat(); CvInvoke.Threshold(mat, thresholdMat, threshold, byte.MaxValue, ThresholdType.Binary); if (CvInvoke.CountNonZero(thresholdMat) == 0) return; @@ -436,6 +437,7 @@ public class OperationLithophane : Operation progress.ResetNameAndProcessed("Packed layers"); Parallel.ForEach(indexes, CoreSettings.GetParallelOptions(progress), i => { + progress.PauseIfRequested(); progress.LockAndIncrement(); using var mat = thresholdLayers[i].LayerMat; for (int index = i+1; index < i + layerIncrement && index < thresholdLayers.Length; index++) diff --git a/UVtools.Core/Operations/OperationMask.cs b/UVtools.Core/Operations/OperationMask.cs index 6991af5..3d51481 100644 --- a/UVtools.Core/Operations/OperationMask.cs +++ b/UVtools.Core/Operations/OperationMask.cs @@ -97,6 +97,7 @@ public class OperationMask : Operation { Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using var mat = SlicerFile[layerIndex].LayerMat; Execute(mat); SlicerFile[layerIndex].LayerMat = mat; diff --git a/UVtools.Core/Operations/OperationMorph.cs b/UVtools.Core/Operations/OperationMorph.cs index 5462b2e..f270384 100644 --- a/UVtools.Core/Operations/OperationMorph.cs +++ b/UVtools.Core/Operations/OperationMorph.cs @@ -178,6 +178,7 @@ public sealed class OperationMorph : Operation Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); int iterations = FileFormat.MutateGetIterationVar(isFade, (int)IterationsStart, (int)IterationsEnd, iterationSteps, maxIteration, LayerIndexStart, (uint)layerIndex); using var mat = SlicerFile[layerIndex].LayerMat; diff --git a/UVtools.Core/Operations/OperationMove.cs b/UVtools.Core/Operations/OperationMove.cs index 4948705..5c3c7da 100644 --- a/UVtools.Core/Operations/OperationMove.cs +++ b/UVtools.Core/Operations/OperationMove.cs @@ -266,6 +266,7 @@ public class OperationMove : Operation Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using (var mat = SlicerFile[layerIndex].LayerMat) { Execute(mat); diff --git a/UVtools.Core/Operations/OperationPattern.cs b/UVtools.Core/Operations/OperationPattern.cs index 028aa5c..157b21a 100644 --- a/UVtools.Core/Operations/OperationPattern.cs +++ b/UVtools.Core/Operations/OperationPattern.cs @@ -301,6 +301,7 @@ public class OperationPattern : Operation { Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using var mat = SlicerFile[layerIndex].LayerMat; using var layerRoi = new Mat(mat, ROI); using var dstLayer = mat.NewBlank(); diff --git a/UVtools.Core/Operations/OperationPixelArithmetic.cs b/UVtools.Core/Operations/OperationPixelArithmetic.cs index f560560..a3a4485 100644 --- a/UVtools.Core/Operations/OperationPixelArithmetic.cs +++ b/UVtools.Core/Operations/OperationPixelArithmetic.cs @@ -583,6 +583,7 @@ public class OperationPixelArithmetic : Operation Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); var layer = SlicerFile[layerIndex]; using (var mat = layer.LayerMat) { diff --git a/UVtools.Core/Operations/OperationPixelDimming.cs b/UVtools.Core/Operations/OperationPixelDimming.cs index 3728b30..7490944 100644 --- a/UVtools.Core/Operations/OperationPixelDimming.cs +++ b/UVtools.Core/Operations/OperationPixelDimming.cs @@ -652,6 +652,7 @@ public class OperationPixelDimming : Operation Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using var mat = SlicerFile[layerIndex].LayerMat; Execute(mat, layerIndex, patternMask, alternatePatternMask); SlicerFile[layerIndex].LayerMat = mat; diff --git a/UVtools.Core/Operations/OperationProgress.cs b/UVtools.Core/Operations/OperationProgress.cs index 94493ff..d2cb5e9 100644 --- a/UVtools.Core/Operations/OperationProgress.cs +++ b/UVtools.Core/Operations/OperationProgress.cs @@ -12,7 +12,7 @@ using UVtools.Core.Objects; namespace UVtools.Core.Operations; -public sealed class OperationProgress : BindableBase +public sealed class OperationProgress : BindableBase, IDisposable { public const string StatusDecodePreviews = "Decoded Previews"; public const string StatusGatherLayers = "Gathered Layers"; @@ -39,7 +39,25 @@ public sealed class OperationProgress : BindableBase public CancellationToken Token => TokenSource.Token; public void ThrowIfCancellationRequested() => TokenSource.Token.ThrowIfCancellationRequested(); + public ManualResetEvent ManualReset { get; } = new (true); + + /// + /// Blocks the current thread until the current WaitHandle receives a signal. + /// + /// true if the current instance receives a signal. If the current instance is never signaled, WaitOne() never returns. + public bool PauseIfRequested() => ManualReset.WaitOne(); + + /// + /// Blocks or cancels the current thread until the current WaitHandle receives a signal. + /// + public void PauseOrCancelIfRequested() + { + ManualReset.WaitOne(); + TokenSource.Token.ThrowIfCancellationRequested(); + } + private bool _canCancel = true; + private bool _isPaused; private string _title = "Operation"; private string _itemName = "Initializing"; private uint _processedItems; @@ -77,6 +95,25 @@ public sealed class OperationProgress : BindableBase set => RaiseAndSetIfChanged(ref _canCancel, value); } + public bool IsPaused + { + get => _isPaused; + set + { + if(!RaiseAndSetIfChanged(ref _isPaused, value)) return; + if (value) + { + ManualReset.Reset(); // pause + StopWatch.Stop(); + } + else + { + ManualReset.Set(); // resume + StopWatch.Start(); + } + } + } + /// /// Gets or sets the item name for the operation /// @@ -183,6 +220,7 @@ public sealed class OperationProgress : BindableBase public void Init(bool canCancel = true) { CanCancel = canCancel; + IsPaused = false; Title = "Operation"; ItemName = "Initializing"; ItemCount = 0; @@ -250,4 +288,10 @@ $"{_processedItems.ToString().PadLeft(_itemCount.ToString().Length, '0')}/{_item RaisePropertyChanged(nameof(ProgressPercent)); RaisePropertyChanged(nameof(Description)); } + + public void Dispose() + { + TokenSource.Dispose(); + ManualReset.Dispose(); + } } \ No newline at end of file diff --git a/UVtools.Core/Operations/OperationRaftRelief.cs b/UVtools.Core/Operations/OperationRaftRelief.cs index 443dae4..8ca1b9f 100644 --- a/UVtools.Core/Operations/OperationRaftRelief.cs +++ b/UVtools.Core/Operations/OperationRaftRelief.cs @@ -283,7 +283,7 @@ public class OperationRaftRelief : Operation progress.Reset("Tracing raft", layerCount, firstSupportLayerIndex); for (; firstSupportLayerIndex < layerCount; firstSupportLayerIndex++) { - progress.ThrowIfCancellationRequested(); + progress.PauseOrCancelIfRequested(); supportsMat = GetRoiOrDefault(SlicerFile[firstSupportLayerIndex].LayerMat); //var circles = CvInvoke.HoughCircles(supportsMat, HoughModes.Gradient, 1, 5, 80, 35, 5, 255); // OLD var circles = CvInvoke.HoughCircles(supportsMat, HoughModes.GradientAlt, 1.5, 25, 300, 0.80, 5, 255); @@ -397,6 +397,7 @@ public class OperationRaftRelief : Operation progress.Reset(ProgressAction, firstSupportLayerIndex - _ignoreFirstLayers); Parallel.For(_ignoreFirstLayers, firstSupportLayerIndex, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using var mat = SlicerFile[layerIndex].LayerMat; using var original = mat.Clone(); var target = GetRoiOrDefault(mat); diff --git a/UVtools.Core/Operations/OperationRedrawModel.cs b/UVtools.Core/Operations/OperationRedrawModel.cs index a769a95..c14eed6 100644 --- a/UVtools.Core/Operations/OperationRedrawModel.cs +++ b/UVtools.Core/Operations/OperationRedrawModel.cs @@ -172,6 +172,7 @@ public class OperationRedrawModel : Operation if (startLayerIndex < 0) return false; Parallel.For(0, otherFile.LayerCount, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); var fullMatLayerIndex = startLayerIndex + layerIndex; using var fullMat = SlicerFile[fullMatLayerIndex].LayerMat; using var original = fullMat.Clone(); diff --git a/UVtools.Core/Operations/OperationRepairLayers.cs b/UVtools.Core/Operations/OperationRepairLayers.cs index e0806b8..15f0a99 100644 --- a/UVtools.Core/Operations/OperationRepairLayers.cs +++ b/UVtools.Core/Operations/OperationRepairLayers.cs @@ -280,6 +280,7 @@ public class OperationRepairLayers : Operation islandsToRecompute.Clear(); Parallel.ForEach(issuesGroup, CoreSettings.GetParallelOptions(progress), group => { + progress.PauseIfRequested(); var layer = SlicerFile[group.Key]; var image = layer.LayerMat; var span = image.GetDataByteSpan(); @@ -327,6 +328,7 @@ public class OperationRepairLayers : Operation var sync = new object(); Parallel.ForEach(issuesGroup, CoreSettings.GetParallelOptions(progress), group => { + progress.PauseIfRequested(); using var mat = SlicerFile[group.Key].LayerMat; var matSpan = mat.GetDataByteSpan(); var matCache = new Dictionary(); @@ -417,6 +419,7 @@ public class OperationRepairLayers : Operation { Parallel.For(LayerIndexStart, LayerIndexEnd, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); var layer = SlicerFile[layerIndex]; Mat? image = null; diff --git a/UVtools.Core/Operations/OperationResize.cs b/UVtools.Core/Operations/OperationResize.cs index 4b57c59..e2b623e 100644 --- a/UVtools.Core/Operations/OperationResize.cs +++ b/UVtools.Core/Operations/OperationResize.cs @@ -159,6 +159,7 @@ public class OperationResize : Operation Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); var newX = _x; var newY = _y; if (IsFade) diff --git a/UVtools.Core/Operations/OperationRotate.cs b/UVtools.Core/Operations/OperationRotate.cs index 970652b..2f8f41c 100644 --- a/UVtools.Core/Operations/OperationRotate.cs +++ b/UVtools.Core/Operations/OperationRotate.cs @@ -97,6 +97,7 @@ public class OperationRotate : Operation { Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using var mat = SlicerFile[layerIndex].LayerMat; Execute(mat); SlicerFile[layerIndex].LayerMat = mat; diff --git a/UVtools.Core/Operations/OperationSolidify.cs b/UVtools.Core/Operations/OperationSolidify.cs index 71498f1..34457b2 100644 --- a/UVtools.Core/Operations/OperationSolidify.cs +++ b/UVtools.Core/Operations/OperationSolidify.cs @@ -89,6 +89,7 @@ public sealed class OperationSolidify : Operation { Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using var mat = SlicerFile[layerIndex].LayerMat; Execute(mat); SlicerFile[layerIndex].LayerMat = mat; diff --git a/UVtools.Core/Operations/OperationThreshold.cs b/UVtools.Core/Operations/OperationThreshold.cs index bb513fe..4190bfc 100644 --- a/UVtools.Core/Operations/OperationThreshold.cs +++ b/UVtools.Core/Operations/OperationThreshold.cs @@ -84,6 +84,7 @@ public class OperationThreshold : Operation { Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.GetParallelOptions(progress), layerIndex => { + progress.PauseIfRequested(); using (var mat = SlicerFile[layerIndex].LayerMat) { Execute(mat); diff --git a/UVtools.Core/Suggestions/SuggestionTransitionLayerCount.cs b/UVtools.Core/Suggestions/SuggestionTransitionLayerCount.cs index 433daf0..08b10bd 100644 --- a/UVtools.Core/Suggestions/SuggestionTransitionLayerCount.cs +++ b/UVtools.Core/Suggestions/SuggestionTransitionLayerCount.cs @@ -83,11 +83,16 @@ public sealed class SuggestionTransitionLayerCount : Suggestion : SlicerFile.ParseTransitionLayerCountFromLayers(); var suggestedTransitionLayerCount = TransitionLayerCount; - var suggestedTransitionDecrementTime = SlicerFile.GetTransitionStepTime(suggestedTransitionLayerCount); + var suggestedTransitionDecrementTime = SlicerFile.GetTransitionStepTimeFromLayers(suggestedTransitionLayerCount); + + var bottomExposureTime = SlicerFile.LastBottomLayer?.ExposureTime ?? SlicerFile.BottomExposureTime; + var exposureTime = SlicerFile.TransitionLayerCount > 0 + ? SlicerFile[SlicerFile.BottomLayerCount + SlicerFile.TransitionLayerCount - 1].ExposureTime + : SlicerFile[SlicerFile.BottomLayerCount].ExposureTime; return IsApplied - ? $"{GlobalAppliedMessage}: {SlicerFile.BottomExposureTime}s » {(actualTransitionDecrementTime <= 0 || actualTransitionLayerCount == 0 ? string.Empty : $"[-{actualTransitionDecrementTime}s/{actualTransitionLayerCount} layers] » ")}{SlicerFile.ExposureTime}s" - : $"{GlobalNotAppliedMessage} {SlicerFile.BottomExposureTime}s » {(actualTransitionDecrementTime <= 0 || actualTransitionLayerCount == 0 ? string.Empty : $"[-{actualTransitionDecrementTime}s/{actualTransitionLayerCount} layers] » ")}{SlicerFile.ExposureTime}s is out of the recommended {SlicerFile.BottomExposureTime}s » {(suggestedTransitionDecrementTime <= 0 || suggestedTransitionLayerCount == 0 ? string.Empty : $"[-{suggestedTransitionDecrementTime}s/{suggestedTransitionLayerCount} layers] » ")}{SlicerFile.ExposureTime}s"; + ? $"{GlobalAppliedMessage}: {bottomExposureTime}s » {(actualTransitionDecrementTime <= 0 || actualTransitionLayerCount == 0 ? string.Empty : $"[-{actualTransitionDecrementTime}s/{actualTransitionLayerCount} layers] » ")}{exposureTime}s" + : $"{GlobalNotAppliedMessage} {bottomExposureTime}s » {(actualTransitionDecrementTime <= 0 || actualTransitionLayerCount == 0 ? string.Empty : $"[-{actualTransitionDecrementTime}s/{actualTransitionLayerCount} layers] » ")}{exposureTime}s is out of the recommended {bottomExposureTime}s » {(suggestedTransitionDecrementTime <= 0 || suggestedTransitionLayerCount == 0 ? string.Empty : $"[-{suggestedTransitionDecrementTime}s/{suggestedTransitionLayerCount} layers] » ")}{exposureTime}s"; } } @@ -108,17 +113,22 @@ public sealed class SuggestionTransitionLayerCount : Suggestion : SlicerFile.ParseTransitionLayerCountFromLayers(); var suggestedTransitionLayerCount = TransitionLayerCount; - var suggestedTransitionDecrementTime = SlicerFile.GetTransitionStepTime(suggestedTransitionLayerCount); + var suggestedTransitionDecrementTime = SlicerFile.GetTransitionStepTimeFromLayers(suggestedTransitionLayerCount); + + var bottomExposureTime = SlicerFile.LastBottomLayer?.ExposureTime ?? SlicerFile.BottomExposureTime; + var exposureTime = SlicerFile.TransitionLayerCount > 0 + ? SlicerFile[SlicerFile.BottomLayerCount + SlicerFile.TransitionLayerCount - 1].ExposureTime + : SlicerFile[SlicerFile.BottomLayerCount].ExposureTime; return - $"{Title}: ({SlicerFile.BottomExposureTime}s » {(actualTransitionDecrementTime <= 0 || actualTransitionLayerCount == 0 ? string.Empty : $"[-{actualTransitionDecrementTime}s/{actualTransitionLayerCount} layers] » ")}{SlicerFile.ExposureTime}s) » ({SlicerFile.BottomExposureTime}s » {(suggestedTransitionDecrementTime <= 0 || suggestedTransitionLayerCount == 0 ? string.Empty : $"[-{suggestedTransitionDecrementTime}s/{suggestedTransitionLayerCount} layers] » ")}{SlicerFile.ExposureTime}s)"; + $"{Title}: ({bottomExposureTime}s » {(actualTransitionDecrementTime <= 0 || actualTransitionLayerCount == 0 ? string.Empty : $"[-{actualTransitionDecrementTime}s/{actualTransitionLayerCount} layers] » ")}{exposureTime}s) » ({bottomExposureTime}s » {(suggestedTransitionDecrementTime <= 0 || suggestedTransitionLayerCount == 0 ? string.Empty : $"[-{suggestedTransitionDecrementTime}s/{suggestedTransitionLayerCount} layers] » ")}{exposureTime}s)"; } } public ushort TransitionLayerCount => (ushort)Math.Min( Math.Clamp( - SlicerFile.GetTransitionLayerCount((float)_transitionStepTime, false), + SlicerFile.GetTransitionLayerCountFromLayers((float)_transitionStepTime, false), _minimumTransitionLayerCount, _maximumTransitionLayerCount) , SlicerFile.MaximumPossibleTransitionLayerCount); diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index 9808d0b..8501c6e 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 - 3.11.2 + 3.12.0 Copyright © 2020 PTRTECH UVtools.png AnyCPU;x64 @@ -78,18 +78,19 @@ + - + - + diff --git a/UVtools.Installer/Code/HeatGeneratedFileList.wxs b/UVtools.Installer/Code/HeatGeneratedFileList.wxs index cdda20b..2a0428b 100644 --- a/UVtools.Installer/Code/HeatGeneratedFileList.wxs +++ b/UVtools.Installer/Code/HeatGeneratedFileList.wxs @@ -227,6 +227,9 @@ + + + @@ -320,8 +323,8 @@ - - + + @@ -1031,6 +1034,9 @@ + + + @@ -1662,6 +1668,7 @@ + @@ -1693,7 +1700,7 @@ - + @@ -1929,6 +1936,7 @@ + diff --git a/UVtools.Installer/Code/Product.wxs b/UVtools.Installer/Code/Product.wxs index 7117eae..0b0f183 100644 --- a/UVtools.Installer/Code/Product.wxs +++ b/UVtools.Installer/Code/Product.wxs @@ -153,7 +153,7 @@ - + diff --git a/UVtools.WPF/MainWindow.Issues.cs b/UVtools.WPF/MainWindow.Issues.cs index 2df8907..55be5b6 100644 --- a/UVtools.WPF/MainWindow.Issues.cs +++ b/UVtools.WPF/MainWindow.Issues.cs @@ -239,6 +239,7 @@ public partial class MainWindow { Parallel.ForEach(processParallelIssues, CoreSettings.GetParallelOptions(Progress), layerIssues => { + Progress.PauseIfRequested(); using (var image = SlicerFile[layerIssues.Key].LayerMat) { var bytes = image.GetDataByteSpan(); diff --git a/UVtools.WPF/MainWindow.Progress.cs b/UVtools.WPF/MainWindow.Progress.cs index 2d81e31..c7fedc5 100644 --- a/UVtools.WPF/MainWindow.Progress.cs +++ b/UVtools.WPF/MainWindow.Progress.cs @@ -52,12 +52,21 @@ public partial class MainWindow }; } + public void ProgressOnClickPauseResume() + { + if (!Progress.CanCancel) return; + DialogResult = DialogResults.Cancel; + Progress.CanCancel = false; + Progress.TokenSource.Cancel(); + } + public void ProgressOnClickCancel() { if (!Progress.CanCancel) return; DialogResult = DialogResults.Cancel; Progress.CanCancel = false; Progress.TokenSource.Cancel(); + Progress.IsPaused = false; } public void ProgressShow(string title, bool canCancel = true) diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml index aec0202..888442f 100644 --- a/UVtools.WPF/MainWindow.axaml +++ b/UVtools.WPF/MainWindow.axaml @@ -2245,18 +2245,26 @@ - - + + + + + + + + + RowDefinitions="30" ColumnDefinitions="*,40,100"> - + + + Gets the first transition layer + + + + + Gets the last transition layer + + Gets the first normal layer @@ -3436,6 +3446,13 @@ Number of transition layers Seconds + + + Gets the transition step time from and first normal layer after the last transition layer, value is returned as positive from normal perspective and logic (Longer - shorter) + + Number of transition layers + Seconds + Gets the transition step time from and , value is returned as positive from normal perspective and logic (Longer - shorter) @@ -3461,6 +3478,15 @@ Midpoint rounding method Transition layer count + + + Gets the transition layer count based on and first normal layer after the last transition layer + + Step decrement time in seconds + True if transition layer count can't be higher than supported by the file, otherwise set to false to not look at possible file layers + Midpoint rounding method + Transition layer count + Re-set exposure time to the transition layers @@ -3941,6 +3967,39 @@ Gets the Y dimension of the preview image, in pixels. + + + 0: Light off delay mode | 1:Wait time mode + + + + + 0: Normal mode + 1: Advance mode, printing use the value of "Layer Definition Content" + + + + + // The volume of all parts. unit: mm3 + + + + + The weight of all parts. unit: g + + + + + 0:The range of pixel's gray value is from 0x0 ~ 0xf + 1:The range of pixel's gray value is from 0x0 ~ 0xff + + + + + 0: reserve + 1: current layer pause printing + + Gets the file tag = MKSDLP @@ -6697,6 +6756,17 @@ Use the alternate pattern every layers + + + Blocks the current thread until the current WaitHandle receives a signal. + + true if the current instance receives a signal. If the current instance is never signaled, WaitOne() never returns. + + + + Blocks or cancels the current thread until the current WaitHandle receives a signal. + + Gets or sets if operation can be cancelled